diff options
298 files changed, 7047 insertions, 3038 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/api/api.go b/api/api.go index 94bccaaa8250..9aac879b4eae 100644 --- a/api/api.go +++ b/api/api.go @@ -27,6 +27,7 @@ import ( const art = "art.module.public.api" const conscrypt = "conscrypt.module.public.api" const i18n = "i18n.module.public.api" + var core_libraries_modules = []string{art, conscrypt, i18n} // The intention behind this soong plugin is to generate a number of "merged" @@ -92,6 +93,8 @@ type fgProps struct { type MergedTxtDefinition struct { // "current.txt" or "removed.txt" TxtFilename string + // Filename in the new dist dir. "android.txt" or "android-removed.txt" + DistFilename string // The module for the non-updatable / non-module part of the api. BaseTxt string // The list of modules that are relevant for this merged txt. @@ -112,7 +115,6 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { if txt.Scope != "public" { filename = txt.Scope + "-" + filename } - props := genruleProps{} props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) props.Tools = []string{"metalava"} @@ -126,9 +128,9 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { Dest: proptools.StringPtr(filename), }, { - Targets: []string{"sdk"}, + Targets: []string{"api_txt", "sdk"}, Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), - Dest: proptools.StringPtr(txt.TxtFilename), + Dest: proptools.StringPtr(txt.DistFilename), }, } props.Visibility = []string{"//visibility:public"} @@ -240,34 +242,39 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ var textFiles []MergedTxtDefinition tagSuffix := []string{".api.txt}", ".removed-api.txt}"} + distFilename := []string{"android.txt", "android-removed.txt"} for i, f := range []string{"current.txt", "removed.txt"} { textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-" + f, - Modules: bootclasspath, - ModuleTag: "{.public" + tagSuffix[i], - Scope: "public", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-" + f, + Modules: bootclasspath, + ModuleTag: "{.public" + tagSuffix[i], + Scope: "public", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-system-" + f, - Modules: bootclasspath, - ModuleTag: "{.system" + tagSuffix[i], - Scope: "system", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-" + f, + Modules: bootclasspath, + ModuleTag: "{.system" + tagSuffix[i], + Scope: "system", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-module-lib-" + f, - Modules: bootclasspath, - ModuleTag: "{.module-lib" + tagSuffix[i], - Scope: "module-lib", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-module-lib-" + f, + Modules: bootclasspath, + ModuleTag: "{.module-lib" + tagSuffix[i], + Scope: "module-lib", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-system-server-" + f, - Modules: system_server_classpath, - ModuleTag: "{.system-server" + tagSuffix[i], - Scope: "system-server", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-server-" + f, + Modules: system_server_classpath, + ModuleTag: "{.system-server" + tagSuffix[i], + Scope: "system-server", }) } for _, txt := range textFiles { 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 b08ffc90a00d..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 { @@ -52021,7 +52021,6 @@ package android.view.accessibility { method public boolean isSelected(); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean); - method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean); } public static final class AccessibilityNodeInfo.CollectionItemInfo.Builder { diff --git a/core/api/removed.txt b/core/api/removed.txt index 608a9a4efeca..1fa1e89fb46e 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -525,6 +525,14 @@ package android.view { } +package android.view.accessibility { + + public static final class AccessibilityNodeInfo.CollectionItemInfo { + method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean); + } + +} + package android.view.translation { public final class TranslationManager { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 650de2e00c3b..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 { @@ -762,7 +762,7 @@ package android.app { method public void clearRequireCompatChange(); method public boolean isPendingIntentBackgroundActivityLaunchAllowed(); method public static android.app.BroadcastOptions makeBasic(); - method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean); 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/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 8b3c9fa73798..56f8760f6059 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -532,7 +532,7 @@ public class BroadcastOptions extends ComponentOptions { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) + @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from = 0) long id) { mIdForResponseEvent = id; } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4fbe232556ed..df9f2a3cbb25 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -57,6 +57,7 @@ interface INotificationManager @UnsupportedAppUsage void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id, int userId); + boolean isInCall(String pkg, int uid); void setShowBadge(String pkg, int uid, boolean showBadge); boolean canShowBadge(String pkg, int uid); boolean hasSentValidMsg(String pkg, int uid); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6e1d1cd2e4c9..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 @@ -7917,6 +7923,8 @@ public class Notification implements Parcelable * @hide */ public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) { + // TODO(b/228941516): This icon should be downscaled to avoid using too much memory, + // see reduceImageSizes. mShortcutIcon = conversationIcon; return this; } @@ -8423,6 +8431,51 @@ public class Notification implements Parcelable return makeMessagingView(StandardTemplateParams.VIEW_TYPE_HEADS_UP); } + /** + * @hide + */ + @Override + public void reduceImageSizes(Context context) { + super.reduceImageSizes(context); + Resources resources = context.getResources(); + boolean isLowRam = ActivityManager.isLowRamDeviceStatic(); + if (mShortcutIcon != null) { + int maxSize = resources.getDimensionPixelSize( + isLowRam ? R.dimen.notification_small_icon_size_low_ram + : R.dimen.notification_small_icon_size); + mShortcutIcon.scaleDownIfNecessary(maxSize, maxSize); + } + + int maxAvatarSize = resources.getDimensionPixelSize( + isLowRam ? R.dimen.notification_person_icon_max_size + : R.dimen.notification_person_icon_max_size_low_ram); + if (mUser != null && mUser.getIcon() != null) { + mUser.getIcon().scaleDownIfNecessary(maxAvatarSize, maxAvatarSize); + } + + reduceMessagesIconSizes(mMessages, maxAvatarSize); + reduceMessagesIconSizes(mHistoricMessages, maxAvatarSize); + } + + /** + * @hide + */ + private static void reduceMessagesIconSizes(@Nullable List<Message> messages, int maxSize) { + if (messages == null) { + return; + } + + for (Message message : messages) { + Person sender = message.mSender; + if (sender != null) { + Icon icon = sender.getIcon(); + if (icon != null) { + icon.scaleDownIfNecessary(maxSize, maxSize); + } + } + } + } + public static final class Message { /** @hide */ public static final String KEY_TEXT = "text"; 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/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index dea083422612..cc62d5337c02 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -180,7 +180,7 @@ "name": "CtsAppSecurityHostTestCases", "options": [ { - "include-filter": "com.android.cts.splitapp.SplitAppTest" + "include-filter": "android.appsecurity.cts.SplitTests" }, { "include-filter": "android.appsecurity.cts.EphemeralTest" @@ -199,14 +199,6 @@ "name": "CtsRollbackManagerHostTestCases" }, { - "name": "CtsOsHostTestCases", - "options": [ - { - "include-filter": "com.android.server.pm.PackageParserTest" - } - ] - }, - { "name": "CtsContentTestCases", "options": [ { 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 d94ad3aa1732..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 @@ -1387,6 +1410,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AE metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AE regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same aeRegions values at different @@ -1609,6 +1640,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AF metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AF regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same afRegions values at different @@ -1823,6 +1862,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AWB metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AWB regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same awbRegions values at different diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 60d5e9eba3e0..1faec5b76524 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -829,6 +829,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AE metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AE regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same aeRegions values at different @@ -1301,6 +1309,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AF metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AF regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same afRegions values at different @@ -1926,6 +1942,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AWB metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AWB regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same awbRegions values at different 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/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java index 69e63133eb03..2d1a3fe8e967 100644 --- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java @@ -57,9 +57,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria"; + private static final int DEFAULT_ROAMING_MATCH_CRITERIA = MATCH_ANY; private final int mRoamingMatchCriteria; private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria"; + private static final int DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA = MATCH_ANY; private final int mOpportunisticMatchCriteria; private VcnCellUnderlyingNetworkTemplate( @@ -253,23 +255,31 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString()); - pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString()); - pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); - pw.println( - "mOpportunisticMatchCriteria: " - + getMatchCriteriaString(mOpportunisticMatchCriteria)); + if (!mAllowedNetworkPlmnIds.isEmpty()) { + pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds); + } + if (!mAllowedNetworkPlmnIds.isEmpty()) { + pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds); + } + if (mRoamingMatchCriteria != DEFAULT_ROAMING_MATCH_CRITERIA) { + pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); + } + if (mOpportunisticMatchCriteria != DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA) { + pw.println( + "mOpportunisticMatchCriteria: " + + getMatchCriteriaString(mOpportunisticMatchCriteria)); + } } /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */ public static final class Builder { - private int mMeteredMatchCriteria = MATCH_ANY; + private int mMeteredMatchCriteria = DEFAULT_METERED_MATCH_CRITERIA; @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); - private int mRoamingMatchCriteria = MATCH_ANY; - private int mOpportunisticMatchCriteria = MATCH_ANY; + private int mRoamingMatchCriteria = DEFAULT_ROAMING_MATCH_CRITERIA; + private int mOpportunisticMatchCriteria = DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA; private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS; private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS; diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java index 3a9ca3edded7..9235d0913295 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java @@ -15,8 +15,6 @@ */ package android.net.vcn; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; - import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntDef; @@ -88,6 +86,9 @@ public abstract class VcnUnderlyingNetworkTemplate { /** @hide */ static final String METERED_MATCH_KEY = "mMeteredMatchCriteria"; + /** @hide */ + static final int DEFAULT_METERED_MATCH_CRITERIA = MATCH_ANY; + private final int mMeteredMatchCriteria; /** @hide */ @@ -237,11 +238,21 @@ public abstract class VcnUnderlyingNetworkTemplate { pw.println(this.getClass().getSimpleName() + ":"); pw.increaseIndent(); - pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); - pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps); - pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps); - pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps); - pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps); + if (mMeteredMatchCriteria != DEFAULT_METERED_MATCH_CRITERIA) { + pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); + } + if (mMinEntryUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps); + } + if (mMinExitUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps); + } + if (mMinEntryDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps); + } + if (mMinExitDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps); + } dumpTransportSpecificFields(pw); pw.decreaseIndent(); diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java index 23a07abdf0cb..2544a6d63561 100644 --- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java @@ -147,7 +147,9 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mSsids: " + mSsids); + if (!mSsids.isEmpty()) { + pw.println("mSsids: " + mSsids); + } } /** 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/WindowManager.java b/core/java/android/view/WindowManager.java index 8e9f9d9fb4f3..cfe44bbbf3c6 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3590,12 +3590,13 @@ public interface WindowManager extends ViewManager { /** * If specified, the insets provided by this window will be our window frame minus the - * insets specified by providedInternalInsets. This should not be used together with - * {@link WindowState#mGivenContentInsets}. If both of them are set, both will be applied. + * insets specified by providedInternalInsets for each type. This should not be used + * together with {@link WindowState#mGivenContentInsets}. If both of them are set, both will + * be applied. * * @hide */ - public Insets providedInternalInsets = Insets.NONE; + public Insets[] providedInternalInsets; /** * If specified, the insets provided by this window for the IME will be our window frame @@ -3603,7 +3604,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public Insets providedInternalImeInsets = Insets.NONE; + public Insets[] providedInternalImeInsets; /** * If specified, the frame that used to calculate relative {@link RoundedCorner} will be @@ -3989,8 +3990,18 @@ public interface WindowManager extends ViewManager { } else { out.writeInt(0); } - providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */); - providedInternalImeInsets.writeToParcel(out, 0 /* parcelableFlags */); + if (providedInternalInsets != null) { + out.writeInt(providedInternalInsets.length); + out.writeTypedArray(providedInternalInsets, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } + if (providedInternalImeInsets != null) { + out.writeInt(providedInternalImeInsets.length); + out.writeTypedArray(providedInternalImeInsets, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } out.writeBoolean(insetsRoundedCornerFrame); if (paramsForRotation != null) { checkNonRecursiveParams(); @@ -4070,8 +4081,16 @@ public interface WindowManager extends ViewManager { providesInsetsTypes = new int[insetsTypesLength]; in.readIntArray(providesInsetsTypes); } - providedInternalInsets = Insets.CREATOR.createFromParcel(in); - providedInternalImeInsets = Insets.CREATOR.createFromParcel(in); + int providedInternalInsetsLength = in.readInt(); + if (providedInternalInsetsLength > 0) { + providedInternalInsets = new Insets[providedInternalInsetsLength]; + in.readTypedArray(providedInternalInsets, Insets.CREATOR); + } + int providedInternalImeInsetsLength = in.readInt(); + if (providedInternalImeInsetsLength > 0) { + providedInternalImeInsets = new Insets[providedInternalImeInsetsLength]; + in.readTypedArray(providedInternalImeInsets, Insets.CREATOR); + } insetsRoundedCornerFrame = in.readBoolean(); int paramsForRotationLength = in.readInt(); if (paramsForRotationLength > 0) { @@ -4374,12 +4393,12 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } - if (!providedInternalInsets.equals(o.providedInternalInsets)) { + if (!Arrays.equals(providedInternalInsets, o.providedInternalInsets)) { providedInternalInsets = o.providedInternalInsets; changes |= LAYOUT_CHANGED; } - if (!providedInternalImeInsets.equals(o.providedInternalImeInsets)) { + if (!Arrays.equals(providedInternalImeInsets, o.providedInternalImeInsets)) { providedInternalImeInsets = o.providedInternalImeInsets; changes |= LAYOUT_CHANGED; } @@ -4590,13 +4609,21 @@ public interface WindowManager extends ViewManager { sb.append(InsetsState.typeToString(providesInsetsTypes[i])); } } - if (!providedInternalInsets.equals(Insets.NONE)) { + if (providedInternalInsets != null) { + sb.append(System.lineSeparator()); sb.append(" providedInternalInsets="); - sb.append(providedInternalInsets); + for (int i = 0; i < providedInternalInsets.length; ++i) { + if (i > 0) sb.append(' '); + sb.append((providedInternalInsets[i])); + } } - if (!providedInternalImeInsets.equals(Insets.NONE)) { + if (providedInternalImeInsets != null) { + sb.append(System.lineSeparator()); sb.append(" providedInternalImeInsets="); - sb.append(providedInternalImeInsets); + for (int i = 0; i < providedInternalImeInsets.length; ++i) { + if (i > 0) sb.append(' '); + sb.append((providedInternalImeInsets[i])); + } } if (insetsRoundedCornerFrame) { sb.append(" insetsRoundedCornerFrame="); diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 0008aa64efa4..90e349864092 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -5682,6 +5682,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @param heading Whether the item is a heading. (Prefer * {@link AccessibilityNodeInfo#setHeading(boolean)}) * @param selected Whether the item is selected. + * @removed */ @Deprecated @NonNull 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/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index fadeea41c833..314b0a0c81db 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -176,8 +176,9 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O // Add current system language into suggestion list for(LocaleStore.LocaleInfo localeInfo: LocaleStore.getSystemCurrentLocaleInfo()) { - if (appCurrentLocale == null || - !localeInfo.getLocale().equals(appCurrentLocale.getLocale())) { + boolean isNotCurrentLocale = appCurrentLocale == null + || !localeInfo.getLocale().equals(appCurrentLocale.getLocale()); + if (!isForCountryMode && isNotCurrentLocale) { mLocaleList.add(localeInfo); } } 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_util_Process.cpp b/core/jni/android_util_Process.cpp index a6fbf094a030..b9d5ee4b3015 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -913,7 +913,7 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz, end = i; i++; } else if ((mode&PROC_QUOTES) != 0) { - while (buffer[i] != '"' && i < endIndex) { + while (i < endIndex && buffer[i] != '"') { i++; } end = i; 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/dimens.xml b/core/res/res/values/dimens.xml index a4761894f8e9..d9d1a082ed4a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -782,6 +782,10 @@ <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen> <!-- The alpha of a disabled notification button --> <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item> + <!-- The maximum size of Person avatar image in MessagingStyle notifications. + This is bigger than displayed because listeners can use it for other displays + e.g. wearables. --> + <dimen name="notification_person_icon_max_size">144dp</dimen> <!-- The maximum size of the small notification icon on low memory devices. --> <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen> @@ -795,6 +799,10 @@ <dimen name="notification_big_picture_max_width_low_ram">294dp</dimen> <!-- The size of the right icon image when on low ram --> <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen> + <!-- The maximum size of Person avatar image in MessagingStyle notifications. + This is bigger than displayed because listeners can use it for other displays + e.g. wearables. --> + <dimen name="notification_person_icon_max_size_low_ram">96dp</dimen> <!-- The maximum size of the grayscale icon --> <dimen name="notification_grayscale_icon_max_size">256dp</dimen> 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/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5f14647c97d9..a9b95da8982a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3604,6 +3604,7 @@ <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/> <java-symbol type="dimen" name="notification_custom_view_max_image_height"/> <java-symbol type="dimen" name="notification_custom_view_max_image_width"/> + <java-symbol type="dimen" name="notification_person_icon_max_size" /> <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/> <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/> @@ -3612,6 +3613,7 @@ <java-symbol type="dimen" name="notification_grayscale_icon_max_size"/> <java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/> <java-symbol type="dimen" name="notification_custom_view_max_image_width_low_ram"/> + <java-symbol type="dimen" name="notification_person_icon_max_size_low_ram" /> <!-- Accessibility fingerprint gestures --> <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" /> diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index e6d23643e8c0..a5da4424f722 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -521,6 +521,70 @@ public class NotificationTest { } @Test + public void testBuild_ensureMessagingUserIsNotTooBig_resizesIcon() { + Icon hugeIcon = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + Icon hugeMessageAvatar = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + Icon hugeHistoricMessageAvatar = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + + Notification.MessagingStyle style = new Notification.MessagingStyle( + new Person.Builder().setIcon(hugeIcon).setName("A User").build()); + style.addMessage(new Notification.MessagingStyle.Message("A message", 123456, + new Person.Builder().setIcon(hugeMessageAvatar).setName("A Sender").build())); + style.addHistoricMessage(new Notification.MessagingStyle.Message("A message", 123456, + new Person.Builder().setIcon(hugeHistoricMessageAvatar).setName( + "A Historic Sender").build())); + Notification notification = new Notification.Builder(mContext, "Channel").setStyle( + style).build(); + + Bitmap personIcon = style.getUser().getIcon().getBitmap(); + assertThat(personIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + assertThat(personIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + + Bitmap avatarIcon = style.getMessages().get(0).getSenderPerson().getIcon().getBitmap(); + assertThat(avatarIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + assertThat(avatarIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + + Bitmap historicAvatarIcon = style.getHistoricMessages().get( + 0).getSenderPerson().getIcon().getBitmap(); + assertThat(historicAvatarIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + assertThat(historicAvatarIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + } + + @Test + public void testBuild_ensureMessagingShortcutIconIsNotTooBig_resizesIcon() { + Icon hugeIcon = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + Notification.MessagingStyle style = new Notification.MessagingStyle( + new Person.Builder().setName("A User").build()).setShortcutIcon(hugeIcon); + + Notification notification = new Notification.Builder(mContext, "Channel").setStyle( + style).build(); + Bitmap shortcutIcon = style.getShortcutIcon().getBitmap(); + + assertThat(shortcutIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_small_icon_size)); + assertThat(shortcutIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_small_icon_size)); + } + + @Test public void testColors_ensureColors_dayMode_producesValidPalette() { Notification.Colors c = new Notification.Colors(); boolean colorized = false; 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/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index c21aa2dd7353..9b09616d4630 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -703,12 +703,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-1427392850": { - "message": "WindowState: Setting back callback %s (priority: %d) (Client IWindow: %s). (WindowState: %s)", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "-1427184084": { "message": "addWindow: New client %s: window=%s Callers=%s", "level": "VERBOSE", @@ -859,6 +853,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1277068810": { + "message": "startBackNavigation currentTask=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-1270731689": { "message": "Attempted to set replacing window on app token with no content %s", "level": "WARN", @@ -1099,12 +1099,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-1010850753": { - "message": "No focused window, defaulting to top task's window", - "level": "WARN", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "-1009117329": { "message": "isFetchingAppTransitionSpecs=true", "level": "VERBOSE", @@ -3043,12 +3037,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "878005951": { - "message": "startBackNavigation task=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "892244061": { "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", "level": "INFO", @@ -3331,12 +3319,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, - "1172542963": { - "message": "onBackNavigationDone backType=%s, task=%s, prevTaskTopActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "1175495463": { "message": "ImeContainer just became organized. Reparenting under parent. imeParentSurfaceControl=%s", "level": "INFO", @@ -3409,6 +3391,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1264179654": { + "message": "No focused window, defaulting to top current task's window", + "level": "WARN", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1270792394": { "message": "Resumed after relaunch %s", "level": "DEBUG", @@ -3427,6 +3415,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowState.java" }, + "1288920916": { + "message": "Error sending initial insets change to WindowContainer overlay", + "level": "ERROR", + "group": "WM_DEBUG_ANIM", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "1305412562": { "message": "Report configuration: %s %s", "level": "VERBOSE", @@ -3853,6 +3847,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1778919449": { + "message": "onBackNavigationDone backType=%s, task=%s, prevActivity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1781673113": { "message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s", "level": "DEBUG", diff --git a/data/keyboards/Vendor_0e6f_Product_f501.kl b/data/keyboards/Vendor_0e6f_Product_f501.kl new file mode 100644 index 000000000000..b46c005353b1 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_f501.kl @@ -0,0 +1,55 @@ +# 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. + +# +# XBox-compatible USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Button labeled as "BACK" (left-pointing triangle) +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Button labeled as "START" (right-pointing triangle) +key 315 BUTTON_START diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 7f70e1cce6f5..4b723d1569c9 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -47,7 +47,7 @@ public final class BLASTBufferQueue { /** Create a new connection with the surface flinger. */ public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height, @PixelFormat.Format int format) { - this(name, false /* updateDestinationFrame */); + this(name, true /* updateDestinationFrame */); update(sc, width, height, format); } diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 54bab4ad3780..ffd041f60e26 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -1637,8 +1637,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * Sets whether the keystore requires the screen to be unlocked before allowing decryption * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this * key while the screen is locked will fail. A locked device requires a PIN, password, - * biometric, or other trusted factor to access. While the screen is locked, the key can - * still be used for encryption or signature verification. + * biometric, or other trusted factor to access. While the screen is locked, any associated + * public key can still be used (e.g for signature verification). */ @NonNull public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { diff --git a/libs/WindowManager/Jetpack/src/TEST_MAPPING b/libs/WindowManager/Jetpack/src/TEST_MAPPING new file mode 100644 index 000000000000..eacfe2520a6a --- /dev/null +++ b/libs/WindowManager/Jetpack/src/TEST_MAPPING @@ -0,0 +1,32 @@ +{ + "presubmit": [ + { + "name": "WMJetpackUnitTests", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + }, + { + "name": "CtsWindowManagerJetpackTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +}
\ No newline at end of file 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/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp index 212fbd0a6752..b6e743a2b7e1 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp +++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp @@ -23,6 +23,8 @@ package { android_test { name: "WMJetpackUnitTests", + // To make the test run via TEST_MAPPING + test_suites: ["device-tests"], srcs: [ "**/*.java", diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java index b6df876e1956..13a2c78d463e 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java @@ -18,6 +18,8 @@ package androidx.window.extensions; import static com.google.common.truth.Truth.assertThat; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -25,6 +27,13 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +/** + * Test class for {@link WindowExtensionsTest}. + * + * Build/Install/Run: + * atest WMJetpackUnitTests:WindowExtensionsTest + */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class WindowExtensionsTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 26463c18b9a0..b06ce4c19d5c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -24,6 +24,8 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -39,6 +41,7 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WMJetpackUnitTests:JetpackTaskFragmentOrganizerTest */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class JetpackTaskFragmentOrganizerTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 120c7ebfe2da..a26a4b657d68 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.extensions.embedding.SplitController.TaskContainer; @@ -37,6 +39,7 @@ import org.junit.runner.RunWith; * Build/Install/Run: * atest WMJetpackUnitTests:SplitController */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class SplitControllerTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java index 7f88f4e7ff2e..af3ad70c04db 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java @@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; +import android.platform.test.annotations.Presubmit; import android.window.TaskFragmentOrganizer; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TaskFragmentAnimationControllerTest { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 1a1cd5b27c53..b6fb82852c2f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1225,12 +1225,6 @@ public class BubbleController { mOverflowListener.applyUpdate(update); } - // Collapsing? Do this first before remaining steps. - if (update.expandedChanged && !update.expanded) { - mStackView.setExpanded(false); - mSysuiProxy.requestNotificationShadeTopUi(false, TAG); - } - // Do removals, if any. ArrayList<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>(update.removedBubbles); @@ -1307,6 +1301,11 @@ public class BubbleController { mStackView.updateBubbleOrder(update.bubbles); } + if (update.expandedChanged && !update.expanded) { + mStackView.setExpanded(false); + mSysuiProxy.requestNotificationShadeTopUi(false, TAG); + } + if (update.selectionChanged && mStackView != null) { mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null) { 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/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index ab00dd5a487c..dc72aead4873 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -61,6 +61,17 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } + // canvas may be an AlphaFilterCanvas, which is intended to draw with a + // modified alpha. We do not have a way to do this without drawing into an + // extra layer, which would have a performance cost. Draw directly into the + // underlying gpu canvas. This matches prior behavior and the behavior in + // Vulkan. + { + auto* gpuCanvas = SkAndroidFrameworkUtils::getBaseWrappedCanvas(canvas); + LOG_ALWAYS_FATAL_IF(!gpuCanvas, "GLFunctorDrawable::onDraw is using an invalid canvas!"); + canvas = gpuCanvas; + } + // flush will create a GrRenderTarget if not already present. canvas->flush(); 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/omapi/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java index f42ca364b6d9..306c09afeaeb 100644 --- a/omapi/java/android/se/omapi/SEService.java +++ b/omapi/java/android/se/omapi/SEService.java @@ -118,6 +118,16 @@ public final class SEService { }); } } + + @Override + public String getInterfaceHash() { + return ISecureElementListener.HASH; + } + + @Override + public int getInterfaceVersion() { + return ISecureElementListener.VERSION; + } } private SEListener mSEListener = new SEListener(); 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/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java index eec73ff37775..72383fe59e7e 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java @@ -122,15 +122,66 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { } if (activity instanceof AppCompatActivity) { - initSupportToolbar((AppCompatActivity) activity); + initSettingsStyleToolBar((SupportActionBarHost) + toolBar -> { + AppCompatActivity appCompatActivity = (AppCompatActivity) activity; + appCompatActivity.setSupportActionBar(toolBar); + return appCompatActivity.getSupportActionBar(); + }); + } else { + initSettingsStyleToolBar((ActionBarHost) + toolBar -> { + activity.setActionBar(toolBar); + return activity.getActionBar(); + }); + } + } + + /** + * Initialize some attributes of {@link ActionBar}. + * + * @param actionBarHost Host Activity that is not AppCompat. + */ + public void initSettingsStyleToolBar(ActionBarHost actionBarHost) { + if (actionBarHost == null) { + Log.w(TAG, "initSettingsStyleToolBar: actionBarHost is null"); return; } final Toolbar toolbar = findViewById(R.id.action_bar); - activity.setActionBar(toolbar); + final ActionBar actionBar = actionBarHost.setupActionBar(toolbar); + + // Enable title and home button by default + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + } + } + + /** + * Initialize some attributes of {@link ActionBar}. + * + * @param supportActionBarHost Host Activity that is AppCompat. + */ + public void initSettingsStyleToolBar(SupportActionBarHost supportActionBarHost) { + if (supportActionBarHost == null) { + Log.w(TAG, "initSettingsStyleToolBar: supportActionBarHost is null"); + return; + } + if (mCollapsingToolbarLayout == null) { + return; + } + + mCollapsingToolbarLayout.removeAllViews(); + inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout); + final androidx.appcompat.widget.Toolbar supportToolbar = + mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); + + final androidx.appcompat.app.ActionBar actionBar = + supportActionBarHost.setupSupportActionBar(supportToolbar); // Enable title and home button by default - final ActionBar actionBar = activity.getActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); @@ -156,20 +207,27 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { } } - /** - * Returns an instance of collapsing toolbar. - */ + /** Returns an instance of collapsing toolbar. */ public CollapsingToolbarLayout getCollapsingToolbarLayout() { return mCollapsingToolbarLayout; } - /** - * Return an instance of app bar. - */ + /** Return an instance of app bar. */ public AppBarLayout getAppBarLayout() { return mAppBarLayout; } + /** Returns the content frame layout. */ + public View getContentFrameLayout() { + return findViewById(R.id.content_frame); + } + + /** Returns the AppCompat Toolbar. */ + public androidx.appcompat.widget.Toolbar getSupportToolbar() { + return (androidx.appcompat.widget.Toolbar) + mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); + } + private void disableCollapsingToolbarLayoutScrollingBehavior() { if (mAppBarLayout == null) { return; @@ -187,25 +245,22 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { params.setBehavior(behavior); } - // This API is for supportActionBar of {@link AppCompatActivity} - private void initSupportToolbar(AppCompatActivity appCompatActivity) { - if (mCollapsingToolbarLayout == null) { - return; - } - - mCollapsingToolbarLayout.removeAllViews(); - inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout); - final androidx.appcompat.widget.Toolbar supportToolbar = - mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); - - appCompatActivity.setSupportActionBar(supportToolbar); + /** Interface to be implemented by a host Activity that is not AppCompat. */ + public interface ActionBarHost { + /** + * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by + * this toolbar if it should be used. + */ + @Nullable ActionBar setupActionBar(Toolbar toolbar); + } - // Enable title and home button by default - final androidx.appcompat.app.ActionBar actionBar = appCompatActivity.getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - } + /** Interface to be implemented by a host Activity that is AppCompat. */ + public interface SupportActionBarHost { + /** + * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by + * this toolbar if it should be used. + */ + @Nullable androidx.appcompat.app.ActionBar setupSupportActionBar( + androidx.appcompat.widget.Toolbar toolbar); } } diff --git a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml index cea1133f7fee..cea1133f7fee 100644 --- a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml +++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml index 75de34e86bc4..1d0c9b941881 100644 --- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml +++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2018 The Android Open Source Project + 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. diff --git a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml index d526df6bedd4..10aa84888d4e 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml @@ -16,5 +16,6 @@ <resources> <dimen name="spinner_height">36dp</dimen> + <dimen name="spinner_dropdown_height">48dp</dimen> <dimen name="spinner_padding_top_or_bottom">8dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml index fd45a16f24ba..6e26ae180685 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml @@ -35,7 +35,7 @@ <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> - <item name="android:minHeight">@dimen/spinner_height</item> + <item name="android:minHeight">@dimen/spinner_dropdown_height</item> <item name="android:paddingStart">16dp</item> <item name="android:paddingEnd">36dp</item> <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item> diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml deleted file mode 100644 index 8ea1f9a794bc..000000000000 --- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources> - <style name="SettingsSpinnerTitleBar"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> - <item name="android:maxLines">1</item> - <item name="android:ellipsize">marquee</item> - <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">36dp</item> - <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item> - <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item> - </style> -</resources> diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java index 26112074a130..7288494beb21 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java @@ -41,7 +41,7 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * access the current theme, resources, etc. */ public SettingsSpinnerAdapter(Context context) { - super(context, DEFAULT_RESOURCE); + super(context, getDefaultResource()); setDropDownViewResource(getDropdownResource()); mDefaultInflater = LayoutInflater.from(context); @@ -51,7 +51,7 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * In overridded {@link #getView(int, View, ViewGroup)}, use this method to get default view. */ public View getDefaultView(int position, View convertView, ViewGroup parent) { - return mDefaultInflater.inflate(DEFAULT_RESOURCE, parent, false /* attachToRoot */); + return mDefaultInflater.inflate(getDefaultResource(), parent, false /* attachToRoot */); } /** @@ -62,8 +62,12 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { return mDefaultInflater.inflate(getDropdownResource(), parent, false /* attachToRoot */); } - private int getDropdownResource() { - return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + private static int getDefaultResource() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + ? DEFAULT_RESOURCE : android.R.layout.simple_spinner_dropdown_item; + } + private static int getDropdownResource() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) ? DFAULT_DROPDOWN_RESOURCE : android.R.layout.simple_spinner_dropdown_item; } } diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml deleted file mode 100644 index e1764afe5d91..000000000000 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?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. - --> - -<ripple -xmlns:android="http://schemas.android.com/apk/res/android" -android:color="@color/settingslib_ripple_color"> - -<item android:id="@android:id/background"> - <layer-list android:paddingMode="stack"> - <item - android:top="8dp" - android:bottom="8dp"> - - <shape> - <corners android:radius="28dp"/> - <solid android:color="@android:color/system_accent1_100"/> - <size android:height="@dimen/settingslib_spinner_height"/> - </shape> - </item> - - <item - android:gravity="center|end" - android:width="18dp" - android:height="18dp" - android:end="12dp" - android:drawable="@drawable/settingslib_arrow_drop_down"/> - </layer-list> -</item> -</ripple> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml index 770a69d3e8e6..770a69d3e8e6 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml index 746671254afd..fbda83272ddd 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml @@ -16,33 +16,30 @@ --> <ripple -xmlns:android="http://schemas.android.com/apk/res/android" -android:color="@color/settingslib_ripple_color"> + xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/settingslib_ripple_color"> <item android:id="@android:id/background"> - <layer-list android:paddingMode="stack"> + <layer-list + android:paddingMode="stack" + android:paddingStart="0dp" + android:paddingEnd="24dp"> <item android:top="8dp" android:bottom="8dp"> <shape> - <corners - android:radius="20dp"/> - <solid - android:color="?android:attr/colorPrimary"/> - <stroke - android:color="#1f000000" - android:width="1dp"/> - <size - android:height="32dp"/> + <corners android:radius="28dp"/> + <solid android:color="@android:color/system_accent1_100"/> + <size android:height="@dimen/settingslib_spinner_height"/> </shape> </item> <item android:gravity="center|end" - android:width="24dp" - android:height="24dp" - android:end="4dp" + android:width="18dp" + android:height="18dp" + android:end="12dp" android:drawable="@drawable/settingslib_arrow_drop_down"/> </layer-list> </item> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml index 056fb828baad..50ef8fb50f19 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml @@ -20,9 +20,17 @@ android:color="@color/settingslib_ripple_color"> <item android:id="@android:id/background"> - <shape> - <corners android:radius="10dp"/> - <solid android:color="@android:color/system_accent2_100"/> - </shape> + <layer-list + android:paddingMode="stack" + android:paddingStart="0dp" + android:paddingEnd="12dp"> + + <item> + <shape> + <corners android:radius="10dp"/> + <solid android:color="@android:color/system_accent2_100"/> + </shape> + </item> + </layer-list> </item> </ripple> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index 29fdab13c717..11546c8ed3d9 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -25,6 +25,4 @@ <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen> <!-- Right padding of the preference --> <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen> - - <dimen name="settingslib_spinner_height">36dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index b12c6d2850c4..3597ee9b08ee 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -45,24 +45,4 @@ <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item> <item name="android:scaleY">0.5</item> </style> - - <style name="Spinner.SettingsLib" - parent="android:style/Widget.Material.Spinner"> - <item name="android:background">@drawable/settingslib_spinner_background</item> - <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background</item> - <item name="android:dropDownVerticalOffset">48dp</item> - <item name="android:layout_marginTop">16dp</item> - <item name="android:layout_marginBottom">8dp</item> - </style> - - <style name="SpinnerItem.SettingsLib" - parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem"> - <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> - </style> - - <style name="SpinnerDropDownItem.SettingsLib" - parent="@android:style/Widget.Material.DropDownItem.Spinner"> - <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> - </style> - </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 4f426a3bf10c..69c122c9992e 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -17,7 +17,7 @@ <resources> <!-- Only using in Settings application --> - <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" > + <style name="Theme.SettingsBase_v31" parent="@android:style/Theme.DeviceDefault.Settings" > <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item> <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item> <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item> @@ -26,11 +26,10 @@ <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item> <item name="android:switchStyle">@style/Switch.SettingsLib</item> <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item> - <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> - <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item> - <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item> </style> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" /> + <!-- Using in SubSettings page including injected settings page --> <style name="Theme.SubSettingsBase" parent="Theme.SettingsBase"> <!-- Suppress the built-in action bar --> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml index 6ed215dd2dbb..bec807b395f7 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml @@ -15,12 +15,6 @@ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:viewportWidth="24" - android:viewportHeight="24" - android:width="24dp" - android:height="24dp"> - <path - android:pathData="M7 10l5 5 5 -5z" - android:fillColor="?android:attr/textColorPrimary"/> -</vector> +<resources> + <dimen name="settingslib_spinner_height">36dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml new file mode 100644 index 000000000000..15fdfe49b0cb --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml @@ -0,0 +1,39 @@ +<?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. + --> +<resources> + <style name="Spinner.SettingsLib" + parent="android:style/Widget.Material.Spinner"> + <item name="android:background">@drawable/settingslib_spinner_background</item> + <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background + </item> + <item name="android:dropDownVerticalOffset">48dp</item> + <item name="android:layout_marginTop">16dp</item> + <item name="android:layout_marginBottom">8dp</item> + </style> + + <style name="SpinnerItem.SettingsLib" + parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem"> + <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> + <item name="android:paddingStart">16dp</item> + </style> + + <style name="SpinnerDropDownItem.SettingsLib" + parent="@android:style/Widget.Material.DropDownItem.Spinner"> + <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> + <item name="android:paddingStart">16dp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml new file mode 100644 index 000000000000..24e3c46b39ce --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml @@ -0,0 +1,24 @@ +<?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. + --> + +<resources> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" > + <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> + <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item> + <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml index fa27bb692fe8..aaab0f041fe3 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml @@ -26,10 +26,4 @@ <style name="TextAppearance.CategoryTitle.SettingsLib" parent="@android:style/TextAppearance.DeviceDefault.Medium"> </style> - - <style name="Spinner.SettingsLib" - parent="android:style/Widget.Material.Spinner"> - <item name="android:background">@drawable/settingslib_spinner_background</item> - <item name="android:dropDownVerticalOffset">48dp</item> - </style> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml index 8dc0f3c1ff2b..2d881d1a8a7b 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml @@ -19,7 +19,6 @@ <!-- Only using in Settings application --> <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="preferenceTheme">@style/PreferenceThemeOverlay</item> - <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> </style> <!-- Using in SubSettings page including injected settings page --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index eb7fea3eb89a..6887d037c6f4 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -224,6 +224,7 @@ <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" /> <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> <!-- It's like, reality, but, you know, virtual --> <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> @@ -522,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 --> @@ -531,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/res/drawable/overlay_button_background.xml b/packages/SystemUI/res/drawable/overlay_button_background.xml index 0e8438c8a11d..c045048802f7 100644 --- a/packages/SystemUI/res/drawable/overlay_button_background.xml +++ b/packages/SystemUI/res/drawable/overlay_button_background.xml @@ -18,7 +18,7 @@ (clipboard text editor, long screenshots) --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:color="?android:textColorPrimary"> + android:color="@color/overlay_button_ripple"> <item android:id="@android:id/background"> <inset android:insetTop="4dp" android:insetBottom="4dp"> <shape android:shape="rectangle"> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 7e8112a8885b..4f7d09963fc6 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -135,6 +135,15 @@ asked for it --> android:layout_height="wrap_content" style="@*android:style/TextAppearance.DeviceDefault.Notification" /> + <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings--> + <TextView + android:id="@+id/non_configurable_call_text" + android:text="@string/notification_unblockable_call_desc" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@*android:style/TextAppearance.DeviceDefault.Notification" /> + <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings--> <TextView android:id="@+id/non_configurable_multichannel_text" diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 2c29f071dcbc..2fb6d6cb9bd5 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -55,7 +55,7 @@ android:clipChildren="false" android:clipToPadding="false" android:focusable="true" - android:paddingBottom="24dp" + android:paddingBottom="@dimen/qqs_layout_padding_bottom" android:importantForAccessibility="yes"> </com.android.systemui.qs.QuickQSPanel> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml deleted file mode 100644 index bb6d4bddf25a..000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2020, 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. ---> -<com.android.systemui.RegionInterceptingFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/rounded_corners_bottom" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/left" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_gravity="left|bottom" - android:tint="#ff000000" - android:visibility="gone" - android:src="@drawable/rounded_corner_bottom"/> - - <ImageView - android:id="@+id/right" - android:layout_width="12dp" - android:layout_height="12dp" - android:tint="#ff000000" - android:visibility="gone" - android:layout_gravity="right|bottom" - android:src="@drawable/rounded_corner_bottom"/> - -</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml deleted file mode 100644 index 46648c88d921..000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_top.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2020, 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. ---> -<com.android.systemui.RegionInterceptingFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/rounded_corners_top" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/left" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_gravity="left|top" - android:tint="#ff000000" - android:visibility="gone" - android:src="@drawable/rounded_corner_top"/> - - <ImageView - android:id="@+id/right" - android:layout_width="12dp" - android:layout_height="12dp" - android:tint="#ff000000" - android:visibility="gone" - android:layout_gravity="right|top" - android:src="@drawable/rounded_corner_top"/> - -</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 3a638b1e5098..dc2bee56373c 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -59,7 +59,6 @@ <color name="global_actions_alert_text">@color/GM2_red_300</color> <!-- Floating overlay actions --> - <color name="overlay_button_ripple">#42FFFFFF</color> <color name="overlay_background_protection_start">#80000000</color> <!-- 50% black --> <!-- Media --> diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml index f5c0509dbf5b..589d12f95f45 100644 --- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml @@ -20,4 +20,6 @@ <dimen name="keyguard_clock_top_margin">40dp</dimen> <dimen name="keyguard_status_view_bottom_margin">40dp</dimen> <dimen name="bouncer_user_switcher_y_trans">20dp</dimen> + + <dimen name="qqs_layout_padding_bottom">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml index 44f8f3aee9ab..fc12d418d218 100644 --- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml @@ -25,6 +25,8 @@ <dimen name="keyguard_status_view_bottom_margin">80dp</dimen> <dimen name="bouncer_user_switcher_y_trans">90dp</dimen> + <dimen name="qqs_layout_padding_bottom">40dp</dimen> + <dimen name="notification_panel_margin_horizontal">96dp</dimen> <dimen name="notification_side_paddings">40dp</dimen> <dimen name="notification_section_divider_height">16dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ff3cb5f2e7c6..d14840317197 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -474,6 +474,7 @@ <dimen name="qs_brightness_margin_top">8dp</dimen> <dimen name="qs_brightness_margin_bottom">24dp</dimen> <dimen name="qqs_layout_margin_top">16dp</dimen> + <dimen name="qqs_layout_padding_bottom">24dp</dimen> <dimen name="qs_customize_internal_side_paddings">8dp</dimen> <dimen name="qs_icon_size">20dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index ff71b4f5e405..5eacc3e6006b 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -170,5 +170,11 @@ <item type="id" name="action_move_bottom_right"/> <item type="id" name="action_move_to_edge_and_hide"/> <item type="id" name="action_move_out_edge_and_show"/> + + <!-- rounded corner view id --> + <item type="id" name="rounded_corner_top_left"/> + <item type="id" name="rounded_corner_top_right"/> + <item type="id" name="rounded_corner_bottom_left"/> + <item type="id" name="rounded_corner_bottom_right"/> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b248efe93e98..0f5115b5c0f5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1373,6 +1373,9 @@ <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> <string name="notification_unblockable_desc">These notifications can\'t be modified.</string> + <!-- Notification: Control panel: Label that displays when a notification cannot be blocked because it's attached to a phone/voip call. --> + <string name="notification_unblockable_call_desc">Call notifications can\'t be modified.</string> + <!-- Notification: Control panel: label that displays when viewing settings for a group of notifications posted to multiple channels. --> <string name="notification_multichannel_desc">This group of notifications cannot be configured here</string> 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/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 32299f5643db..5bd81a42a814 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -86,6 +86,7 @@ public class WindowManagerWrapper { public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT; public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; + public static final int ITYPE_SIZE = InsetsState.SIZE; public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE; public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR; diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt index 1b2ea3b257ab..a08c9000355f 100644 --- a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt +++ b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt @@ -23,7 +23,7 @@ object BouncerPanelExpansionCalculator { * Scale the alpha/position of the host view. */ @JvmStatic - fun getHostViewScaledExpansion(fraction: Float): Float { + fun showBouncerProgress(fraction: Float): Float { return when { fraction >= 0.9f -> 1f fraction < 0.6 -> 0f @@ -35,7 +35,7 @@ object BouncerPanelExpansionCalculator { * Scale the alpha/tint of the back scrim. */ @JvmStatic - fun getBackScrimScaledExpansion(fraction: Float): Float { + fun aboutToShowBouncerProgress(fraction: Float): Float { return MathUtils.constrain((fraction - 0.9f) / 0.1f, 0f, 1f) } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index b3cf92741222..04c9a45af065 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -130,6 +130,24 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } }; + private final KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener + mKeyguardUnlockAnimationListener = + new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { + @Override + public void onSmartspaceSharedElementTransitionStarted() { + // The smartspace needs to be able to translate out of bounds in order to + // end up where the launcher's smartspace is, while its container is being + // swiped off the top of the screen. + setClipChildrenForUnlock(false); + } + + @Override + public void onUnlockAnimationFinished() { + // For performance reasons, reset this once the unlock animation ends. + setClipChildrenForUnlock(true); + } + }; + @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, @@ -162,22 +180,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mUiExecutor = uiExecutor; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mDumpManager = dumpManager; - mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( - new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { - @Override - public void onSmartspaceSharedElementTransitionStarted() { - // The smartspace needs to be able to translate out of bounds in order to - // end up where the launcher's smartspace is, while its container is being - // swiped off the top of the screen. - setClipChildrenForUnlock(false); - } - - @Override - public void onUnlockAnimationFinished() { - // For performance reasons, reset this once the unlock animation ends. - setClipChildrenForUnlock(true); - } - }); } /** @@ -272,6 +274,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS ); updateDoubleLineClock(); + + mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( + mKeyguardUnlockAnimationListener); } int getNotificationIconAreaHeight() { @@ -287,6 +292,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mView.setClockPlugin(null, mStatusBarStateController.getState()); mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver); + + mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener( + mKeyguardUnlockAnimationListener); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 8c3e066849b9..239b478949d2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -327,7 +327,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> * @param fraction amount of the screen that should show. */ public void setExpansion(float fraction) { - float scaledFraction = BouncerPanelExpansionCalculator.getHostViewScaledExpansion(fraction); + float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction); mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f)); mView.setTranslationY(scaledFraction * mTranslationY); } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index ff5715c606b6..9aa5fae1044d 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -63,7 +63,6 @@ public final class Prefs { Key.QS_WORK_ADDED, Key.QS_NIGHTDISPLAY_ADDED, Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, - Key.SEEN_MULTI_USER, Key.SEEN_RINGER_GUIDANCE_COUNT, Key.QS_HAS_TURNED_OFF_MOBILE_DATA, Key.TOUCHED_RINGER_TOGGLE, @@ -106,7 +105,6 @@ public final class Prefs { * Settings panel. */ String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount"; - String SEEN_MULTI_USER = "HasSeenMultiUser"; String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount"; String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed"; String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData"; diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 43d91a24bd3f..9b091018de9f 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -77,6 +77,7 @@ import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; import com.android.systemui.decor.OverlayWindow; import com.android.systemui.decor.PrivacyDotDecorProviderFactory; +import com.android.systemui.decor.RoundedCornerDecorProviderFactory; import com.android.systemui.decor.RoundedCornerResDelegate; import com.android.systemui.qs.SettingObserver; import com.android.systemui.settings.UserTracker; @@ -137,6 +138,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @VisibleForTesting protected RoundedCornerResDelegate mRoundedCornerResDelegate; @VisibleForTesting + protected DecorProviderFactory mRoundedCornerFactory; + private int mProviderRefreshToken = 0; + @VisibleForTesting protected OverlayWindow[] mOverlays = null; @VisibleForTesting @Nullable @@ -282,16 +286,58 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return mDotFactory.getHasProviders(); } + @NonNull + private List<DecorProvider> getProviders(boolean hasHwLayer) { + List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); + if (!hasHwLayer) { + decorProviders.addAll(mRoundedCornerFactory.getProviders()); + } + return decorProviders; + } + + private void updateDisplayIdToProviderFactories() { + mDotFactory.onDisplayUniqueIdChanged(mDisplayUniqueId); + mRoundedCornerFactory.onDisplayUniqueIdChanged(mDisplayUniqueId); + } + + /** + * Check that newProviders is the same list with decorProviders inside mOverlay. + * @param newProviders expected comparing DecorProviders + * @return true if same provider list + */ + @VisibleForTesting + boolean hasSameProviders(@NonNull List<DecorProvider> newProviders) { + final ArrayList<Integer> overlayViewIds = new ArrayList<>(); + if (mOverlays != null) { + for (OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + overlayViewIds.addAll(overlay.getViewIds()); + } + } + if (overlayViewIds.size() != newProviders.size()) { + return false; + } + + for (DecorProvider provider: newProviders) { + if (!overlayViewIds.contains(provider.getViewId())) { + return false; + } + } + return true; + } + private void startOnScreenDecorationsThread() { mRotation = mContext.getDisplay().getRotation(); mDisplayUniqueId = mContext.getDisplay().getUniqueId(); mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), mDisplayUniqueId); + mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); mWindowManager = mContext.getSystemService(WindowManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - updateRoundedCornerDrawable(); - updateRoundedCornerRadii(); + updateHwLayerRoundedCornerDrawable(); setupDecorations(); setupCameraListener(); @@ -343,18 +389,27 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab final String newUniqueId = mContext.getDisplay().getUniqueId(); if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; - mRoundedCornerResDelegate.reloadAll(newUniqueId); final DisplayDecorationSupport newScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole - // screen decoration. - if (!eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) { + + updateDisplayIdToProviderFactories(); + + // When providers or the value of mSupportHwcScreenDecoration is changed, + // re-setup the whole screen decoration. + if (!hasSameProviders(getProviders(newScreenDecorationSupport != null)) + || !eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) { mHwcScreenDecorationSupport = newScreenDecorationSupport; removeAllOverlays(); setupDecorations(); return; } - updateRoundedCornerDrawable(); + + if (mScreenDecorHwcLayer != null) { + updateHwLayerRoundedCornerDrawable(); + updateHwLayerRoundedCornerSize(); + } + + updateOverlayProviderViews(); } if (mCutoutViews != null) { final int size = mCutoutViews.length; @@ -369,7 +424,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.onDisplayChanged(displayId); } - updateOrientation(); } }; @@ -396,6 +450,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return null; } + private void removeRedundantOverlayViews(@NonNull List<DecorProvider> decorProviders) { + if (mOverlays == null) { + return; + } + int[] viewIds = decorProviders.stream().mapToInt(DecorProvider::getViewId).toArray(); + for (final OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + overlay.removeRedundantViews(viewIds); + } + } + private void removeOverlayView(@IdRes int id) { if (mOverlays == null) { return; @@ -411,9 +478,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupDecorations() { - List<DecorProvider> decorProviders = mDotFactory.getProviders(); + if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) { + List<DecorProvider> decorProviders = getProviders(mHwcScreenDecorationSupport != null); + removeRedundantOverlayViews(decorProviders); - if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) { if (mHwcScreenDecorationSupport != null) { createHwcOverlay(); } else { @@ -427,7 +495,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab Pair<List<DecorProvider>, List<DecorProvider>> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); - createOverlay(i, cutout, pair.getFirst(), isOnlyPrivacyDotInSwLayer); + createOverlay(i, pair.getFirst(), isOnlyPrivacyDotInSwLayer); } else { removeOverlay(i); } @@ -560,7 +628,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private void createOverlay( @BoundsPosition int pos, - @Nullable DisplayCutout cutout, @NonNull List<DecorProvider> decorProviders, boolean isOnlyPrivacyDotInSwLayer) { if (mOverlays == null) { @@ -568,14 +635,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } if (mOverlays[pos] != null) { - // When mOverlay[pos] is not null and only privacy dot in sw layer, use privacy dot - // view's visibility - mOverlays[pos].getRootView().setVisibility( - getWindowVisibility(mOverlays[pos], isOnlyPrivacyDotInSwLayer)); + initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer); return; } - mOverlays[pos] = overlayForPosition(pos, decorProviders, isOnlyPrivacyDotInSwLayer); + mOverlays[pos] = new OverlayWindow(mContext); + initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer); final ViewGroup overlayView = mOverlays[pos].getRootView(); overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); overlayView.setAlpha(0); @@ -590,7 +655,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); mCutoutViews[pos].setColor(mTintColor); overlayView.addView(mCutoutViews[pos]); - updateView(pos, cutout); + mCutoutViews[pos].updateRotation(mRotation); } mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); @@ -641,42 +706,24 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } /** - * Allow overrides for top/bottom positions + * Init OverlayWindow with decorProviders */ - private OverlayWindow overlayForPosition( - @BoundsPosition int pos, + private void initOverlay( + @NonNull OverlayWindow overlay, @NonNull List<DecorProvider> decorProviders, boolean isOnlyPrivacyDotInSwLayer) { - final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); - decorProviders.forEach(provider -> { - removeOverlayView(provider.getViewId()); - currentOverlay.addDecorProvider(provider, mRotation); - }); - // When only privacy dot in mOverlay, set the initial visibility of mOverlays to - // INVISIBLE and set it to VISIBLE when the privacy dot is showing. - if (isOnlyPrivacyDotInSwLayer) { - currentOverlay.getRootView().setVisibility(View.INVISIBLE); - } - return currentOverlay; - } - - private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { - if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) { - return; - } - - // update rounded corner view rotation - updateRoundedCornerView(pos, R.id.left, cutout); - updateRoundedCornerView(pos, R.id.right, cutout); - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); - updateRoundedCornerImageView(); - - // update cutout view rotation - if (mCutoutViews != null && mCutoutViews[pos] != null) { - mCutoutViews[pos].updateRotation(mRotation); + if (!overlay.hasSameProviders(decorProviders)) { + decorProviders.forEach(provider -> { + if (overlay.getView(provider.getViewId()) != null) { + return; + } + removeOverlayView(provider.getViewId()); + overlay.addDecorProvider(provider, mRotation); + }); } + // Use visibility of privacy dot views if only privacy dot in sw layer + overlay.getRootView().setVisibility( + getWindowVisibility(overlay, isOnlyPrivacyDotInSwLayer)); } @VisibleForTesting @@ -849,7 +896,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab int oldRotation = mRotation; mPendingRotationChange = false; updateOrientation(); - updateRoundedCornerRadii(); if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); setupDecorations(); if (mOverlays != null) { @@ -910,109 +956,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController.setNewRotation(newRotation); } - if (mPendingRotationChange) { - return; - } - if (newRotation != mRotation) { + if (!mPendingRotationChange && newRotation != mRotation) { mRotation = newRotation; if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingRotationChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); + updateHwLayerRoundedCornerSize(); + updateHwLayerRoundedCornerDrawable(); } - if (mOverlays != null) { - updateLayoutParams(); - final DisplayCutout cutout = getCutout(); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { + updateLayoutParams(); + // update cutout view rotation + if (mCutoutViews != null) { + for (final DisplayCutoutView cutoutView: mCutoutViews) { + if (cutoutView == null) { continue; } - updateView(i, cutout); + cutoutView.updateRotation(mRotation); } } } - } - private void updateRoundedCornerRadii() { - // We should eventually move to just using the intrinsic size of the drawables since - // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not - // upgrading all of the configs to contain (width, height) pairs. Instead assume that a - // device configured using the single integer config value is okay with drawing the corners - // as a square - final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - - if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth() - || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) { - onTuningChanged(SIZE, null); - } - } - - private void updateRoundedCornerView(@BoundsPosition int pos, int id, - @Nullable DisplayCutout cutout) { - final View rounded = mOverlays[pos].getRootView().findViewById(id); - if (rounded == null) { - return; - } - rounded.setVisibility(View.GONE); - if (shouldShowSwLayerRoundedCorner(pos, cutout)) { - final int gravity = getRoundedCornerGravity(pos, id == R.id.left); - ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity; - setRoundedCornerOrientation(rounded, gravity); - rounded.setVisibility(View.VISIBLE); - } - } - - private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) { - final int rotatedPos = getBoundPositionFromRotation(pos, mRotation); - switch (rotatedPos) { - case BOUNDS_POSITION_LEFT: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT; - case BOUNDS_POSITION_TOP: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT; - case BOUNDS_POSITION_RIGHT: - return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT; - case BOUNDS_POSITION_BOTTOM: - return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT; - default: - throw new IllegalArgumentException("Incorrect position: " + rotatedPos); - } + // update all provider views inside overlay + updateOverlayProviderViews(); } - /** - * Configures the rounded corner drawable's view matrix based on the gravity. - * - * The gravity describes which corner to configure for, and the drawable we are rotating is - * assumed to be oriented for the top-left corner of the device regardless of the target corner. - * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or - * y-axis for the top-right and bottom-left corners. - */ - private void setRoundedCornerOrientation(View corner, int gravity) { - corner.setRotation(0); - corner.setScaleX(1); - corner.setScaleY(1); - switch (gravity) { - case Gravity.TOP | Gravity.LEFT: - return; - case Gravity.TOP | Gravity.RIGHT: - corner.setScaleX(-1); // flip X axis - return; - case Gravity.BOTTOM | Gravity.LEFT: - corner.setScaleY(-1); // flip Y axis - return; - case Gravity.BOTTOM | Gravity.RIGHT: - corner.setRotation(180); - return; - default: - throw new IllegalArgumentException("Unsupported gravity: " + gravity); - } - } private boolean hasRoundedCorners() { - return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.isMultipleRadius(); + return mRoundedCornerFactory.getHasProviders(); } private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, @@ -1066,6 +1035,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab context.getResources(), context.getDisplay().getUniqueId()); } + private void updateOverlayProviderViews() { + if (mOverlays == null) { + return; + } + ++mProviderRefreshToken; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; + } + overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); + } + } + private void updateLayoutParams() { if (mOverlays == null) { return; @@ -1085,63 +1067,33 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } mExecutor.execute(() -> { - if (mOverlays == null) return; - if (SIZE.equals(key)) { - boolean hasReloadRoundedCornerRes = false; - if (newValue != null) { - try { - mRoundedCornerResDelegate.updateTuningSizeFactor( - Integer.parseInt(newValue)); - hasReloadRoundedCornerRes = true; - } catch (Exception e) { - } - } - - // When onTuningChanged() is not called through updateRoundedCornerRadii(), - // we need to reload rounded corner res to prevent incorrect dimen - if (!hasReloadRoundedCornerRes) { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); + if (mOverlays == null || !SIZE.equals(key)) { + return; + } + ++mProviderRefreshToken; + try { + final int sizeFactor = Integer.parseInt(newValue); + mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken); + } catch (NumberFormatException e) { + mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken); + } + Integer[] filterIds = { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right + }; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; } - - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); + overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, + mDisplayUniqueId); } + updateHwLayerRoundedCornerSize(); }); } - private void updateRoundedCornerDrawable() { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - updateRoundedCornerImageView(); - } - - private void updateRoundedCornerImageView() { - final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable(); - final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable(); - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom); - return; - } - - if (mOverlays == null) { - return; - } - final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable( - isTopRoundedCorner(i, R.id.left) ? top : bottom); - ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable( - isTopRoundedCorner(i, R.id.right) ? top : bottom); - } - } - private void updateHwLayerRoundedCornerDrawable() { if (mScreenDecorHwcLayer == null) { return; @@ -1156,25 +1108,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable); } - @VisibleForTesting - boolean isTopRoundedCorner(@BoundsPosition int pos, int id) { - switch (pos) { - case BOUNDS_POSITION_LEFT: - case BOUNDS_POSITION_RIGHT: - if (mRotation == ROTATION_270) { - return id == R.id.left ? false : true; - } else { - return id == R.id.left ? true : false; - } - case BOUNDS_POSITION_TOP: - return true; - case BOUNDS_POSITION_BOTTOM: - return false; - default: - throw new IllegalArgumentException("Unknown bounds position"); - } - } - private void updateHwLayerRoundedCornerSize() { if (mScreenDecorHwcLayer == null) { return; @@ -1186,28 +1119,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth); } - private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) { - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth()); - return; - } - - if (mOverlays == null) { - return; - } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - setSize(overlayView.findViewById(R.id.left), - isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom); - setSize(overlayView.findViewById(R.id.right), - isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom); - } - } - @VisibleForTesting protected void setSize(View view, Size pixelSize) { LayoutParams params = view.getLayoutParams(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b2673e923008..233f3648aeb3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -56,7 +56,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -110,6 +112,8 @@ public class AuthContainerView extends LinearLayout @ContainerState private int mContainerState = STATE_UNKNOWN; private final Set<Integer> mFailedModalities = new HashSet<Integer>(); + private final @Background DelayableExecutor mBackgroundExecutor; + // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. @Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason; // HAT received from LockSettingsService when credential is verified. @@ -192,7 +196,7 @@ public class AuthContainerView extends LinearLayout return this; } - public AuthContainerView build(int[] sensorIds, + public AuthContainerView build(@Background DelayableExecutor bgExecutor, int[] sensorIds, @Nullable List<FingerprintSensorPropertiesInternal> fpProps, @Nullable List<FaceSensorPropertiesInternal> faceProps, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @@ -200,7 +204,7 @@ public class AuthContainerView extends LinearLayout @NonNull LockPatternUtils lockPatternUtils) { mConfig.mSensorIds = sensorIds; return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle, - userManager, lockPatternUtils, new Handler(Looper.getMainLooper())); + userManager, lockPatternUtils, new Handler(Looper.getMainLooper()), bgExecutor); } } @@ -253,7 +257,8 @@ public class AuthContainerView extends LinearLayout @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, - @NonNull Handler mainHandler) { + @NonNull Handler mainHandler, + @NonNull @Background DelayableExecutor bgExecutor) { super(config.mContext); mConfig = config; @@ -277,6 +282,7 @@ public class AuthContainerView extends LinearLayout mBackgroundView = mFrameLayout.findViewById(R.id.background); mPanelView = mFrameLayout.findViewById(R.id.panel); mPanelController = new AuthPanelController(mContext, mPanelView); + mBackgroundExecutor = bgExecutor; // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { @@ -384,6 +390,7 @@ public class AuthContainerView extends LinearLayout mCredentialView.setPromptInfo(mConfig.mPromptInfo); mCredentialView.setPanelController(mPanelController, animatePanel); mCredentialView.setShouldAnimateContents(animateContents); + mCredentialView.setBackgroundExecutor(mBackgroundExecutor); mFrameLayout.addView(mCredentialView); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index aaf18b309db2..b05bc245a79f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -64,11 +64,13 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.CoreStartable; import com.android.systemui.assist.ui.DisplayUtils; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import java.util.ArrayList; @@ -139,6 +141,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba private boolean mAllAuthenticatorsRegistered; @NonNull private final UserManager mUserManager; @NonNull private final LockPatternUtils mLockPatternUtils; + private final @Background DelayableExecutor mBackgroundExecutor; @VisibleForTesting final TaskStackListener mTaskStackListener = new TaskStackListener() { @@ -507,13 +510,15 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull StatusBarStateController statusBarStateController, - @Main Handler handler) { + @Main Handler handler, + @Background DelayableExecutor bgExecutor) { super(context); mExecution = execution; mWakefulnessLifecycle = wakefulnessLifecycle; mUserManager = userManager; mLockPatternUtils = lockPatternUtils; mHandler = handler; + mBackgroundExecutor = bgExecutor; mCommandQueue = commandQueue; mActivityTaskManager = activityTaskManager; mFingerprintManager = fingerprintManager; @@ -839,6 +844,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba // Create a new dialog but do not replace the current one yet. final AuthDialog newDialog = buildDialog( + mBackgroundExecutor, promptInfo, requireConfirmation, userId, @@ -934,9 +940,9 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } } - protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, - int userId, int[] sensorIds, String opPackageName, - boolean skipIntro, long operationId, long requestId, + protected AuthDialog buildDialog(@Background DelayableExecutor bgExecutor, + PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, + String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricMultiSensorMode int multiSensorConfig, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @@ -951,8 +957,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba .setOperationId(operationId) .setRequestId(requestId) .setMultiSensorConfig(multiSensorConfig) - .build(sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, userManager, - lockPatternUtils); + .build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, + userManager, lockPatternUtils); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index ed84a37682e4..4fa835e038ec 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -53,6 +53,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -99,6 +101,8 @@ public abstract class AuthCredentialView extends LinearLayout { protected int mEffectiveUserId; protected ErrorTimer mErrorTimer; + protected @Background DelayableExecutor mBackgroundExecutor; + interface Callback { void onCredentialMatched(byte[] attestation); } @@ -217,6 +221,10 @@ public abstract class AuthCredentialView extends LinearLayout { mContainerView = containerView; } + void setBackgroundExecutor(@Background DelayableExecutor bgExecutor) { + mBackgroundExecutor = bgExecutor; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -377,27 +385,33 @@ public abstract class AuthCredentialView extends LinearLayout { } private void showLastAttemptBeforeWipeDialog() { - final AlertDialog alertDialog = new AlertDialog.Builder(mContext) - .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) - .setMessage( - getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType)) - .setPositiveButton(android.R.string.ok, null) - .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); - alertDialog.show(); + mBackgroundExecutor.execute(() -> { + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) + .setMessage( + getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType)) + .setPositiveButton(android.R.string.ok, null) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + mHandler.post(alertDialog::show); + }); } private void showNowWipingDialog() { - final AlertDialog alertDialog = new AlertDialog.Builder(mContext) - .setMessage(getNowWipingMessage(getUserTypeForWipe())) - .setPositiveButton( - com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, - null /* OnClickListener */) - .setOnDismissListener( - dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR)) - .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); - alertDialog.show(); + mBackgroundExecutor.execute(() -> { + String nowWipingMessage = getNowWipingMessage(getUserTypeForWipe()); + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setMessage(nowWipingMessage) + .setPositiveButton( + com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, + null /* OnClickListener */) + .setOnDismissListener( + dialog -> mContainerView.animateAway( + AuthDialogCallback.DISMISSED_ERROR)) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + mHandler.post(alertDialog::show); + }); } private @UserType int getUserTypeForWipe() { @@ -412,6 +426,7 @@ public abstract class AuthCredentialView extends LinearLayout { } } + // This should not be called on the main thread to avoid making an IPC. private String getLastAttemptBeforeWipeMessage( @UserType int userType, @Utils.CredentialType int credentialType) { switch (userType) { @@ -442,6 +457,7 @@ public abstract class AuthCredentialView extends LinearLayout { } } + // This should not be called on the main thread to avoid making an IPC. private String getLastAttemptBeforeWipeProfileMessage( @Utils.CredentialType int credentialType) { return mDevicePolicyManager.getResources().getString( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 33126b3887da..76c1dbcaf20c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -145,6 +145,7 @@ class AuthRippleController @Inject constructor( val lightRevealScrim = centralSurfaces.lightRevealScrim if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) { circleReveal?.let { + lightRevealScrim?.revealAmount = 0f lightRevealScrim?.revealEffect = it startLightRevealScrimOnKeyguardFadingAway = true } @@ -168,7 +169,8 @@ class AuthRippleController @Inject constructor( startDelay = keyguardStateController.keyguardFadingAwayDelay addUpdateListener { animator -> if (lightRevealScrim.revealEffect != circleReveal) { - // if something else took over the reveal, let's do nothing. + // if something else took over the reveal, let's cancel ourselves + cancel() return@addUpdateListener } lightRevealScrim.revealAmount = animator.animatedValue as Float diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 5d131f2a0046..f5f07c8ca989 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -25,6 +25,7 @@ import android.util.Log; import android.util.MathUtils; import android.view.MotionEvent; +import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -71,7 +72,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private float mTransitionToFullShadeProgress; private float mLastDozeAmount; private long mLastUdfpsBouncerShowTime = -1; - private float mStatusBarExpansion; + private float mPanelExpansionFraction; private boolean mLaunchTransitionFadingAway; private boolean mIsLaunchingActivity; private float mActivityLaunchProgress; @@ -188,7 +189,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud pw.println("mQsExpanded=" + mQsExpanded); pw.println("mIsBouncerVisible=" + mIsBouncerVisible); pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount); - pw.println("mStatusBarExpansion=" + mStatusBarExpansion); + pw.println("mPanelExpansionFraction=" + mPanelExpansionFraction); pw.println("unpausedAlpha=" + mView.getUnpausedAlpha()); pw.println("mUdfpsRequested=" + mUdfpsRequested); pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested); @@ -324,14 +325,16 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud */ @Override public void updateAlpha() { - // fade icon on transitions to showing the status bar, but if mUdfpsRequested, then - // the keyguard is occluded by some application - so instead use the input bouncer - // hidden amount to determine the fade - float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mStatusBarExpansion; + // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested, + // then the keyguard is occluded by some application - so instead use the input bouncer + // hidden amount to determine the fade. + float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mPanelExpansionFraction; + int alpha = mShowingUdfpsBouncer ? 255 : (int) MathUtils.constrain( MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f); + if (!mShowingUdfpsBouncer) { alpha *= (1.0f - mTransitionToFullShadeProgress); @@ -471,7 +474,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onPanelExpansionChanged( float fraction, boolean expanded, boolean tracking) { - mStatusBarExpansion = fraction; + mPanelExpansionFraction = + mKeyguardViewManager.bouncerIsInTransit() ? BouncerPanelExpansionCalculator + .aboutToShowBouncerProgress(fraction) : fraction; updateAlpha(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt b/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt index 4315cb0e14d7..ca36375c2cde 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt @@ -39,6 +39,13 @@ import java.util.concurrent.atomic.AtomicInteger * * This class has no sync controls, so make sure to only make modifications from the background * thread. + * + * This class takes the following actions: + * * [registerAction]: action to register this receiver (with the proper filter) with [Context]. + * * [unregisterAction]: action to unregister this receiver with [Context]. + * * [testPendingRemovalAction]: action to check if a particular [BroadcastReceiver] registered + * with [BroadcastDispatcher] has been unregistered and is pending removal. See + * [PendingRemovalStore]. */ class ActionReceiver( private val action: String, @@ -46,7 +53,8 @@ class ActionReceiver( private val registerAction: BroadcastReceiver.(IntentFilter) -> Unit, private val unregisterAction: BroadcastReceiver.() -> Unit, private val bgExecutor: Executor, - private val logger: BroadcastDispatcherLogger + private val logger: BroadcastDispatcherLogger, + private val testPendingRemovalAction: (BroadcastReceiver, Int) -> Boolean ) : BroadcastReceiver(), Dumpable { companion object { @@ -106,7 +114,8 @@ class ActionReceiver( // Immediately return control to ActivityManager bgExecutor.execute { receiverDatas.forEach { - if (it.filter.matchCategories(intent.categories) == null) { + if (it.filter.matchCategories(intent.categories) == null && + !testPendingRemovalAction(it.receiver, userId)) { it.executor.execute { it.receiver.pendingResult = pendingResult it.receiver.onReceive(context, intent) diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index 1c27e320dd83..b7aebc198827 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -63,13 +63,14 @@ private const val DEBUG = true * Broadcast handling may be asynchronous *without* calling goAsync(), as it's running within sysui * and doesn't need to worry about being killed. */ -open class BroadcastDispatcher constructor ( +open class BroadcastDispatcher @JvmOverloads constructor ( private val context: Context, private val bgLooper: Looper, private val bgExecutor: Executor, private val dumpManager: DumpManager, private val logger: BroadcastDispatcherLogger, - private val userTracker: UserTracker + private val userTracker: UserTracker, + private val removalPendingStore: PendingRemovalStore = PendingRemovalStore(logger) ) : Dumpable { // Only modify in BG thread @@ -167,6 +168,7 @@ open class BroadcastDispatcher constructor ( * @param receiver The receiver to unregister. It will be unregistered for all users. */ open fun unregisterReceiver(receiver: BroadcastReceiver) { + removalPendingStore.tagForRemoval(receiver, UserHandle.USER_ALL) handler.obtainMessage(MSG_REMOVE_RECEIVER, receiver).sendToTarget() } @@ -177,13 +179,21 @@ open class BroadcastDispatcher constructor ( * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL]. */ open fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) { + removalPendingStore.tagForRemoval(receiver, user.identifier) handler.obtainMessage(MSG_REMOVE_RECEIVER_FOR_USER, user.identifier, 0, receiver) .sendToTarget() } @VisibleForTesting protected open fun createUBRForUser(userId: Int) = - UserBroadcastDispatcher(context, userId, bgLooper, bgExecutor, logger) + UserBroadcastDispatcher( + context, + userId, + bgLooper, + bgExecutor, + logger, + removalPendingStore + ) override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println("Broadcast dispatcher:") @@ -193,6 +203,8 @@ open class BroadcastDispatcher constructor ( ipw.println("User ${receiversByUser.keyAt(index)}") receiversByUser.valueAt(index).dump(ipw, args) } + ipw.println("Pending removal:") + removalPendingStore.dump(ipw, args) ipw.decreaseIndent() } @@ -223,10 +235,20 @@ open class BroadcastDispatcher constructor ( for (it in 0 until receiversByUser.size()) { receiversByUser.valueAt(it).unregisterReceiver(msg.obj as BroadcastReceiver) } + removalPendingStore.clearPendingRemoval( + msg.obj as BroadcastReceiver, + UserHandle.USER_ALL + ) } MSG_REMOVE_RECEIVER_FOR_USER -> { - receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver) + val userId = if (msg.arg1 == UserHandle.USER_CURRENT) { + userTracker.userId + } else { + msg.arg1 + } + receiversByUser.get(userId)?.unregisterReceiver(msg.obj as BroadcastReceiver) + removalPendingStore.clearPendingRemoval(msg.obj as BroadcastReceiver, userId) } else -> super.handleMessage(msg) } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt b/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt new file mode 100644 index 000000000000..ebf498361c2a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt @@ -0,0 +1,58 @@ +package com.android.systemui.broadcast + +import android.content.BroadcastReceiver +import android.os.UserHandle +import android.util.SparseSetArray +import androidx.annotation.GuardedBy +import com.android.systemui.Dumpable +import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger +import com.android.systemui.util.indentIfPossible +import java.io.PrintWriter + +/** + * Store information about requests for unregistering receivers from [BroadcastDispatcher], before + * they have been completely removed from the system. + * + * This helps make unregistering a receiver a *sync* operation. + */ +class PendingRemovalStore( + private val logger: BroadcastDispatcherLogger +) : Dumpable { + @GuardedBy("pendingRemoval") + private val pendingRemoval: SparseSetArray<BroadcastReceiver> = SparseSetArray() + + fun tagForRemoval(broadcastReceiver: BroadcastReceiver, userId: Int) { + logger.logTagForRemoval(userId, broadcastReceiver) + synchronized(pendingRemoval) { + pendingRemoval.add(userId, broadcastReceiver) + } + } + + fun isPendingRemoval(broadcastReceiver: BroadcastReceiver, userId: Int): Boolean { + return synchronized(pendingRemoval) { + pendingRemoval.contains(userId, broadcastReceiver) || + pendingRemoval.contains(UserHandle.USER_ALL, broadcastReceiver) + } + } + + fun clearPendingRemoval(broadcastReceiver: BroadcastReceiver, userId: Int) { + synchronized(pendingRemoval) { + pendingRemoval.remove(userId, broadcastReceiver) + } + logger.logClearedAfterRemoval(userId, broadcastReceiver) + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + synchronized(pendingRemoval) { + pw.indentIfPossible { + val size = pendingRemoval.size() + for (i in 0 until size) { + val user = pendingRemoval.keyAt(i) + print(user) + print("->") + println(pendingRemoval.get(user)) + } + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt index 24ce2384ab46..6b15188cdb2a 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt @@ -20,12 +20,12 @@ import android.content.BroadcastReceiver import android.content.Context import android.os.Handler import android.os.Looper -import android.os.Message import android.os.UserHandle import android.util.ArrayMap import android.util.ArraySet import android.util.Log import androidx.annotation.VisibleForTesting +import androidx.annotation.WorkerThread import com.android.internal.util.Preconditions import com.android.systemui.Dumpable import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -34,8 +34,6 @@ import java.io.PrintWriter import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger -private const val MSG_REGISTER_RECEIVER = 0 -private const val MSG_UNREGISTER_RECEIVER = 1 private const val TAG = "UserBroadcastDispatcher" private const val DEBUG = false @@ -50,7 +48,8 @@ open class UserBroadcastDispatcher( private val userId: Int, private val bgLooper: Looper, private val bgExecutor: Executor, - private val logger: BroadcastDispatcherLogger + private val logger: BroadcastDispatcherLogger, + private val removalPendingStore: PendingRemovalStore ) : Dumpable { companion object { @@ -60,16 +59,6 @@ open class UserBroadcastDispatcher( val index = AtomicInteger(0) } - private val bgHandler = object : Handler(bgLooper) { - override fun handleMessage(msg: Message) { - when (msg.what) { - MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData, msg.arg1) - MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver) - else -> Unit - } - } - } - // Used for key in actionsToActionsReceivers internal data class ReceiverProperties( val action: String, @@ -77,6 +66,8 @@ open class UserBroadcastDispatcher( val permission: String? ) + private val bgHandler = Handler(bgLooper) + // Only modify in BG thread @VisibleForTesting internal val actionsToActionsReceivers = ArrayMap<ReceiverProperties, ActionReceiver>() @@ -92,19 +83,21 @@ open class UserBroadcastDispatcher( /** * Register a [ReceiverData] for this user. */ + @WorkerThread fun registerReceiver(receiverData: ReceiverData, flags: Int) { - bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, flags, 0, receiverData).sendToTarget() + handleRegisterReceiver(receiverData, flags) } /** * Unregister a given [BroadcastReceiver] for this user. */ + @WorkerThread fun unregisterReceiver(receiver: BroadcastReceiver) { - bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget() + handleUnregisterReceiver(receiver) } private fun handleRegisterReceiver(receiverData: ReceiverData, flags: Int) { - Preconditions.checkState(bgHandler.looper.isCurrentThread, + Preconditions.checkState(bgLooper.isCurrentThread, "This method should only be called from BG thread") if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}") receiverToActions @@ -151,12 +144,13 @@ open class UserBroadcastDispatcher( } }, bgExecutor, - logger + logger, + removalPendingStore::isPendingRemoval ) } private fun handleUnregisterReceiver(receiver: BroadcastReceiver) { - Preconditions.checkState(bgHandler.looper.isCurrentThread, + Preconditions.checkState(bgLooper.isCurrentThread, "This method should only be called from BG thread") if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver") receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach { diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt index 8da651991072..5b3a982ab5e2 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt @@ -87,6 +87,26 @@ class BroadcastDispatcherLogger @Inject constructor( }) } + fun logTagForRemoval(user: Int, receiver: BroadcastReceiver) { + val receiverString = receiver.toString() + log(DEBUG, { + int1 = user + str1 = receiverString + }, { + "Receiver $str1 tagged for removal from user $int1" + }) + } + + fun logClearedAfterRemoval(user: Int, receiver: BroadcastReceiver) { + val receiverString = receiver.toString() + log(DEBUG, { + int1 = user + str1 = receiverString + }, { + "Receiver $str1 has been completely removed for user $int1" + }) + } + fun logReceiverUnregistered(user: Int, receiver: BroadcastReceiver) { val receiverString = receiver.toString() log(INFO, { diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt index 3543bb4ab9e9..03ee8b11ab41 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.decor +import android.content.Context import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View import android.view.ViewGroup @@ -38,9 +38,20 @@ abstract class DecorProvider { /** The aligned bounds for the view which is created through inflateView() */ abstract val alignedBounds: List<Int> + /** + * Called when res info changed. + * Child provider needs to implement it if its view needs to be updated. + */ + abstract fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) + /** Inflate view into parent as current rotation */ abstract fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt index c60cad8419d2..cc4096fb3b19 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt @@ -19,4 +19,5 @@ package com.android.systemui.decor abstract class DecorProviderFactory { abstract val providers: List<DecorProvider> abstract val hasProviders: Boolean + abstract fun onDisplayUniqueIdChanged(displayUniqueId: String?) }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt index 9f8679cdea4a..d775ad39a5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt @@ -16,31 +16,25 @@ package com.android.systemui.decor import android.annotation.IdRes -import android.view.DisplayCutout -import android.view.LayoutInflater +import android.content.Context import android.view.Surface import android.view.View import android.view.ViewGroup -import com.android.systemui.R -import java.util.HashMap - -class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) { - - private val layoutId: Int - get() { - return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT || - pos == DisplayCutout.BOUNDS_POSITION_TOP) { - R.layout.rounded_corners_top - } else { - R.layout.rounded_corners_bottom - } - } +import com.android.systemui.RegionInterceptingFrameLayout + +class OverlayWindow(private val context: Context) { - val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup - private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap() + val rootView = RegionInterceptingFrameLayout(context) as ViewGroup + private val viewProviderMap = mutableMapOf<Int, Pair<View, DecorProvider>>() - fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) { - val view = decorProvider.inflateView(layoutInflater, rootView, rotation) + val viewIds: List<Int> + get() = viewProviderMap.keys.toList() + + fun addDecorProvider( + decorProvider: DecorProvider, + @Surface.Rotation rotation: Int + ) { + val view = decorProvider.inflateView(context, rootView, rotation) viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider) } @@ -56,4 +50,54 @@ class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: viewProviderMap.remove(id) } } + + /** + * Remove views which does not been found in expectExistViewIds + */ + fun removeRedundantViews(expectExistViewIds: IntArray?) { + viewIds.forEach { + if (expectExistViewIds == null || !(expectExistViewIds.contains(it))) { + removeView(it) + } + } + } + + /** + * Check that newProviders is the same list with viewProviderMap. + */ + fun hasSameProviders(newProviders: List<DecorProvider>): Boolean { + return (newProviders.size == viewProviderMap.size) && + newProviders.all { getView(it.viewId) != null } + } + + /** + * Apply new configuration info into views. + * @param filterIds target view ids. Apply to all if null. + * @param rotation current or new rotation direction. + * @param displayUniqueId new displayUniqueId if any. + */ + fun onReloadResAndMeasure( + filterIds: Array<Int>? = null, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) { + filterIds?.forEach { id -> + viewProviderMap[id]?.let { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } ?: run { + viewProviderMap.values.forEach { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt index 7afd7e0eedc5..d16d9604c002 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt @@ -16,6 +16,7 @@ package com.android.systemui.decor +import android.content.Context import android.content.res.Resources import android.view.DisplayCutout import android.view.LayoutInflater @@ -38,6 +39,10 @@ class PrivacyDotDecorProviderFactory @Inject constructor( override val hasProviders: Boolean get() = isPrivacyDotEnabled + override fun onDisplayUniqueIdChanged(displayUniqueId: String?) { + // Do nothing for privacy dot + } + override val providers: List<DecorProvider> get() { return if (hasProviders) { @@ -76,12 +81,21 @@ class PrivacyDotCornerDecorProviderImpl( private val layoutId: Int ) : CornerDecorProvider() { + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + rotation: Int, + displayUniqueId: String? + ) { + // Do nothing here because it is handled inside PrivacyDotViewController + } + override fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View { - inflater.inflate(layoutId, parent, true) + LayoutInflater.from(context).inflate(layoutId, parent, true) return parent.getChildAt(parent.childCount - 1 /* latest new added child */) } } diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt new file mode 100644 index 000000000000..a4f7a586ab97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt @@ -0,0 +1,90 @@ +/* + * 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.decor + +import android.view.DisplayCutout +import com.android.systemui.R + +class RoundedCornerDecorProviderFactory( + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : DecorProviderFactory() { + + override val hasProviders: Boolean + get() = roundedCornerResDelegate.run { + // We don't consider isMultipleRadius here because it makes no sense if size is zero. + topRoundedSize.width > 0 || bottomRoundedSize.width > 0 + } + + override fun onDisplayUniqueIdChanged(displayUniqueId: String?) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, null) + } + + override val providers: List<DecorProvider> + get() { + val hasTop = roundedCornerResDelegate.topRoundedSize.width > 0 + val hasBottom = roundedCornerResDelegate.bottomRoundedSize.width > 0 + return when { + hasTop && hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasTop -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + else -> emptyList() + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt new file mode 100644 index 000000000000..90ff950406b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt @@ -0,0 +1,192 @@ +/* + * 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.decor + +import android.content.Context +import android.view.DisplayCutout +import android.view.Gravity +import android.view.Surface +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import com.android.systemui.R + +class RoundedCornerDecorProviderImpl( + override val viewId: Int, + @DisplayCutout.BoundsPosition override val alignedBound1: Int, + @DisplayCutout.BoundsPosition override val alignedBound2: Int, + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : CornerDecorProvider() { + + private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + + override fun inflateView( + context: Context, + parent: ViewGroup, + @Surface.Rotation rotation: Int + ): View { + return ImageView(context).also { view -> + // View + view.id = viewId + initView(view, rotation) + + // LayoutParams + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + val params = FrameLayout.LayoutParams( + layoutSize.width, + layoutSize.height, + alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation)) + + // AddView + parent.addView(view, params) + } + } + + private fun initView(view: ImageView, @Surface.Rotation rotation: Int) { + view.setRoundedCornerImage(roundedCornerResDelegate, isTop) + view.adjustRotation(alignedBounds, rotation) + view.setColorFilter(IMAGE_TINT_COLOR) + } + + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? + ) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken) + + initView((view as ImageView), rotation) + + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + (view.layoutParams as FrameLayout.LayoutParams).let { + it.width = layoutSize.width + it.height = layoutSize.height + it.gravity = alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation) + view.setLayoutParams(it) + } + } +} + +private const val IMAGE_TINT_COLOR: Int = 0xFF000000.toInt() + +@DisplayCutout.BoundsPosition +private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) { + Surface.ROTATION_0 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM + } + Surface.ROTATION_90 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + Surface.ROTATION_270 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + else /* Surface.ROTATION_180 */ -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP + } +} + +private fun ImageView.setRoundedCornerImage( + resDelegate: RoundedCornerResDelegate, + isTop: Boolean +) { + val drawable = if (isTop) + resDelegate.topRoundedDrawable + else + resDelegate.bottomRoundedDrawable + + if (drawable != null) { + setImageDrawable(drawable) + } else { + setImageResource( + if (isTop) + R.drawable.rounded_corner_top + else + R.drawable.rounded_corner_bottom + ) + } +} + +/** + * Configures the rounded corner drawable's view matrix based on the gravity. + * + * The gravity describes which corner to configure for, and the drawable we are rotating is assumed + * to be oriented for the top-left corner of the device regardless of the target corner. + * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or + * y-axis for the top-right and bottom-left corners. + */ +private fun ImageView.adjustRotation(alignedBounds: List<Int>, @Surface.Rotation rotation: Int) { + var newRotation = 0F + var newScaleX = 1F + var newScaleY = 1F + + val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT) + when (rotation) { + Surface.ROTATION_0 -> when { + isTop && isLeft -> {} + isTop && !isLeft -> { newScaleX = -1F } + !isTop && isLeft -> { newScaleY = -1F } + else /* !isTop && !isLeft */ -> { newRotation = 180F } + } + Surface.ROTATION_90 -> when { + isTop && isLeft -> { newScaleY = -1F } + isTop && !isLeft -> {} + !isTop && isLeft -> { newRotation = 180F } + else /* !isTop && !isLeft */ -> { newScaleX = -1F } + } + Surface.ROTATION_270 -> when { + isTop && isLeft -> { newScaleX = -1F } + isTop && !isLeft -> { newRotation = 180F } + !isTop && isLeft -> {} + else /* !isTop && !isLeft */ -> { newScaleY = -1F } + } + else /* Surface.ROTATION_180 */ -> when { + isTop && isLeft -> { newRotation = 180F } + isTop && !isLeft -> { newScaleY = -1F } + !isTop && isLeft -> { newScaleX = -1F } + else /* !isTop && !isLeft */ -> {} + } + } + + this.rotation = newRotation + this.scaleX = newScaleX + this.scaleY = newScaleY +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt index ed175ef7a095..c2bab269d396 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -35,6 +35,8 @@ class RoundedCornerResDelegate( private val density: Float get() = res.displayMetrics.density + private var reloadToken: Int = 0 + var isMultipleRadius: Boolean = false private set @@ -59,12 +61,26 @@ class RoundedCornerResDelegate( reloadMeasures() } - fun reloadAll(newDisplayUniqueId: String?) { - displayUniqueId = newDisplayUniqueId + private fun reloadAll(newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadRes() reloadMeasures() } + fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + if (displayUniqueId != newDisplayUniqueId) { + displayUniqueId = newDisplayUniqueId + newReloadToken ?.let { reloadToken = it } + reloadRes() + reloadMeasures() + } else { + newReloadToken?.let { reloadAll(it) } + } + } + private fun reloadRes() { val configIdx = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId) isMultipleRadius = getIsMultipleRadius(configIdx) @@ -122,7 +138,11 @@ class RoundedCornerResDelegate( } } - fun updateTuningSizeFactor(factor: Int) { + fun updateTuningSizeFactor(factor: Int?, newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadMeasures(factor) } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index fb1af8b66b91..74949d094e33 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -16,7 +16,7 @@ package com.android.systemui.dreams; -import static com.android.keyguard.BouncerPanelExpansionCalculator.getBackScrimScaledExpansion; +import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; @@ -217,19 +217,19 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve mBlurUtils.applyBlur(mView.getViewRootImpl(), (int) mBlurUtils.blurRadiusOfRatio( - 1 - getBackScrimScaledExpansion(bouncerHideAmount)), false); + 1 - aboutToShowBouncerProgress(bouncerHideAmount)), false); } private static float getAlpha(int position, float expansion) { return Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation( position == POSITION_TOP ? getDreamAlphaScaledExpansion(expansion) - : getBackScrimScaledExpansion(expansion + 0.03f)); + : aboutToShowBouncerProgress(expansion + 0.03f)); } private float getTranslationY(int position, float expansion) { final float fraction = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation( position == POSITION_TOP ? getDreamYPositionScaledExpansion(expansion) - : getBackScrimScaledExpansion(expansion + 0.03f)); + : aboutToShowBouncerProgress(expansion + 0.03f)); return MathUtils.lerp(-mDreamOverlayMaxTranslationY, 0, fraction); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 94b33e1352aa..a8c286241141 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -389,6 +389,10 @@ class KeyguardUnlockAnimationController @Inject constructor( listeners.add(listener) } + fun removeKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) { + listeners.remove(listener) + } + /** * Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind * the keyguard has started successfully. We can use these parameters to directly manipulate the 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 0f45a7562d0c..740ecff6b06e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -83,6 +83,7 @@ import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.InsetsVisibilities; import android.view.KeyEvent; @@ -196,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; @@ -512,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; @@ -536,6 +539,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mRecentsOptional = recentsOptional; mDeadZone = deadZone; mDeviceConfigProxy = deviceConfigProxy; + mNavigationBarTransitions = navigationBarTransitions; mBackAnimation = backAnimation; mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; @@ -560,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(); @@ -631,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()); @@ -704,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) { @@ -731,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); } /** @@ -892,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); } @@ -1429,7 +1433,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mLightBarController = lightBarController; if (mLightBarController != null) { mLightBarController.setNavigationBar( - mView.getLightTransitionsController()); + getBarTransitions().getLightTransitionsController()); } } @@ -1471,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) { @@ -1491,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) { @@ -1558,10 +1562,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); lp.gravity = gravity; + lp.providedInternalInsets = new Insets[InsetsState.SIZE]; if (insetsHeight != -1) { - lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0); + lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = + Insets.of(0, height - insetsHeight, 0, 0); } else { - lp.providedInternalInsets = Insets.NONE; + lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = null; } lp.token = new Binder(); lp.accessibilityTitle = mContext.getString(R.string.nav_bar); 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 f87f81e40e2c..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 @@ -610,7 +613,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca view.setVisibility((View.VISIBLE)); } float alpha = mQSPanelController.bouncerInTransit() - ? BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(progress) + ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(progress) : ShadeInterpolation.getContentAlpha(progress); view.setAlpha(alpha); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9fbdd3c873e2..9de132f64d0b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -364,7 +364,7 @@ public class QSPanel extends LinearLayout implements Tunable { setPaddingRelative(getPaddingStart(), paddingTop, getPaddingEnd(), - getPaddingEnd()); + getPaddingBottom()); } void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 7e0410c0674b..dd99db49c1d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -143,9 +143,10 @@ class QSSecurityFooter extends ViewController<View> @Inject QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, - UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, - SecurityController securityController, DialogLaunchAnimator dialogLaunchAnimator, - @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher) { + UserTracker userTracker, @Main Handler mainHandler, + ActivityStarter activityStarter, SecurityController securityController, + DialogLaunchAnimator dialogLaunchAnimator, @Background Looper bgLooper, + BroadcastDispatcher broadcastDispatcher) { super(rootView); mFooterText = mView.findViewById(R.id.footer_text); mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon); @@ -493,21 +494,23 @@ class QSSecurityFooter extends ViewController<View> private void createDialog() { mShouldUseSettingsButton.set(false); - final View view = createDialogView(); - mMainHandler.post(() -> { - mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme - mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); - mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, - mShouldUseSettingsButton.get() ? getSettingsButton() : getNegativeButton(), - this); - - mDialog.setView(view); - if (mView.isAggregatedVisible()) { - mDialogLaunchAnimator.showFromView(mDialog, mView); - } else { - mDialog.show(); - } + mHandler.post(() -> { + String settingsButtonText = getSettingsButton(); + final View view = createDialogView(); + mMainHandler.post(() -> { + mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); + mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, mShouldUseSettingsButton.get() + ? settingsButtonText : getNegativeButton(), this); + + mDialog.setView(view); + if (mView.isAggregatedVisible()) { + mDialogLaunchAnimator.showFromView(mDialog, mView); + } else { + mDialog.show(); + } + }); }); } @@ -650,6 +653,7 @@ class QSSecurityFooter extends ViewController<View> } } + // This should not be called on the main thread to avoid making an IPC. @VisibleForTesting String getSettingsButton() { return mDpm.getResources().getString( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 3c95da873273..112b1e827845 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -71,7 +71,11 @@ public class QuickQSPanel extends QSPanel { @Override protected void updatePadding() { - // QS Panel is setting a top padding by default, which we don't need. + int bottomPadding = getResources().getDimensionPixelSize(R.dimen.qqs_layout_padding_bottom); + setPaddingRelative(getPaddingStart(), + getPaddingTop(), + getPaddingEnd(), + bottomPadding); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 9a932bae833e..270bdc785178 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -11,6 +11,7 @@ import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffXfermode import android.graphics.RadialGradient import android.graphics.Shader +import android.os.Trace import android.util.AttributeSet import android.util.MathUtils.lerp import android.view.View @@ -222,6 +223,8 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, revealEffect.setRevealAmountOnScrim(value, this) updateScrimOpaque() + Trace.traceCounter(Trace.TRACE_TAG_APP, "light_reveal_amount", + (field * 100).toInt()) invalidate() } } @@ -355,8 +358,8 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, } override fun onDraw(canvas: Canvas?) { - if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 - || revealAmount == 0f) { + if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 || + revealAmount == 0f) { if (revealAmount < 1f) { canvas?.drawColor(revealGradientEndColor) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5bf75c796fc9..efb46b9689d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; +import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -380,12 +381,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) { INotificationManager iNm = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + + boolean isSystem = false; try { - return iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); + isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); } catch (RemoteException e) { Log.e(TAG, "cannot reach NMS"); } - return false; + RoleManager rm = context.getSystemService(RoleManager.class); + List<String> fixedRoleHolders = new ArrayList<>(); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER)); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY)); + if (fixedRoleHolders.contains(sbn.getPackageName())) { + isSystem = true; + } + + return isSystem; } else { PackageManager packageManager = CentralSurfaces.getPackageManagerForUser( context, sbn.getUser().getIdentifier()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 59a78ed6f4ea..8b01a4790f3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -124,6 +124,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; + private boolean mIsSystemRegisteredCall; private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; @@ -229,6 +230,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled(); mUiEventLogger = uiEventLogger; + mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class) + && mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid()); + int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); if (mNumUniqueChannelsInRow == 0) { @@ -252,17 +256,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void bindInlineControls() { - if (mIsNonblockable) { + if (mIsSystemRegisteredCall) { + findViewById(R.id.non_configurable_call_text).setVisibility(VISIBLE); + findViewById(R.id.non_configurable_text).setVisibility(GONE); + findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); + findViewById(R.id.interruptiveness_settings).setVisibility(GONE); + ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); + findViewById(R.id.turn_off_notifications).setVisibility(GONE); + } else if (mIsNonblockable) { findViewById(R.id.non_configurable_text).setVisibility(VISIBLE); + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); findViewById(R.id.turn_off_notifications).setVisibility(GONE); } else if (mNumUniqueChannelsInRow > 1) { + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE); } else { + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE); 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/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index c77b0d6721fb..5e81b5da2455 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -4404,8 +4404,7 @@ public class CentralSurfaces extends CoreStartable implements @Override public void onDozeAmountChanged(float linear, float eased) { if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS) - && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal) - && !mBiometricUnlockController.isWakeAndUnlock()) { + && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { mLightRevealScrim.setRevealAmount(1f - linear); } } 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/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 518a9181ec98..d492c57dfa0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -797,7 +797,7 @@ public abstract class PanelViewController { mExpandedFraction = Math.min(1f, maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); mAmbientState.setExpansionFraction(mStatusBarKeyguardViewManager.bouncerIsInTransit() - ? BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(mExpandedFraction) + ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(mExpandedFraction) : mExpandedFraction); onHeightUpdated(mExpandedHeight); updatePanelExpansionAndVisibility(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 26ffdf25318d..290df3e3e1af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -292,9 +292,7 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); // managed profile - mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - getManagedProfileAccessibilityString()); - mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible); + updateManagedProfile(); // data saver mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, @@ -521,7 +519,7 @@ public class PhoneStatusBarPolicy } private void updateManagedProfile() { - // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in + // getLastResumedActivityUserId needs to acquire the AM lock, which may be contended in // some cases. Since it doesn't really matter here whether it's updated in this frame // or in the next one, we call this method from our UI offload thread. mUiBgExecutor.execute(() -> { @@ -529,6 +527,7 @@ public class PhoneStatusBarPolicy try { userId = ActivityTaskManager.getService().getLastResumedActivityUserId(); boolean isManagedProfile = mUserManager.isManagedProfile(userId); + String accessibilityString = getManagedProfileAccessibilityString(); mHandler.post(() -> { final boolean showIcon; if (isManagedProfile && (!mKeyguardStateController.isShowing() @@ -536,7 +535,7 @@ public class PhoneStatusBarPolicy showIcon = true; mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - getManagedProfileAccessibilityString()); + accessibilityString); } else { showIcon = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index fd6503a53208..cc2ff3fb4388 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -790,7 +790,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) { final float interpolatedFraction = - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion( + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( mBouncerHiddenFraction); mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, mBehindAlpha, interpolatedFraction); @@ -1076,7 +1076,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private float getInterpolatedFraction() { if (mStatusBarKeyguardViewManager.bouncerIsInTransit()) { return BouncerPanelExpansionCalculator - .getBackScrimScaledExpansion(mPanelExpansionFraction); + .aboutToShowBouncerProgress(mPanelExpansionFraction); } return ShadeInterpolation.getNotificationScrimAlpha(mPanelExpansionFraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index ea935be334c1..935f87dc8221 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -17,6 +17,7 @@ import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarStateControllerImpl @@ -85,7 +86,9 @@ class UnlockedScreenOffAnimationController @Inject constructor( duration = LIGHT_REVEAL_ANIMATION_DURATION interpolator = Interpolators.LINEAR addUpdateListener { - lightRevealScrim.revealAmount = it.animatedValue as Float + if (lightRevealScrim.revealEffect !is CircleReveal) { + lightRevealScrim.revealAmount = it.animatedValue as Float + } if (lightRevealScrim.isScrimAlmostOccludes && interactionJankMonitor.isInstrumenting(CUJ_SCREEN_OFF)) { // ends the instrument when the scrim almost occludes the screen. @@ -95,9 +98,9 @@ class UnlockedScreenOffAnimationController @Inject constructor( } addListener(object : AnimatorListenerAdapter() { override fun onAnimationCancel(animation: Animator?) { - lightRevealScrim.revealAmount = 1f - lightRevealAnimationPlaying = false - interactionJankMonitor.cancel(CUJ_SCREEN_OFF) + if (lightRevealScrim.revealEffect !is CircleReveal) { + lightRevealScrim.revealAmount = 1f + } } override fun onAnimationEnd(animation: Animator?) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 846e07fa0bad..a3f01c21d137 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -64,8 +64,6 @@ import com.android.settingslib.users.UserCreatingDialog; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; import com.android.systemui.GuestResumeSessionReceiver; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; import com.android.systemui.animation.DialogLaunchAnimator; @@ -84,6 +82,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.CreateUserActivity; +import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; @@ -144,6 +143,7 @@ public class UserSwitcherController implements Dumpable { // When false, there won't be any visual affordance to add a new user from the keyguard even if // the user is unlocked private boolean mAddUsersFromLockScreen; + private boolean mUserSwitcherEnabled; @VisibleForTesting boolean mPauseRefreshUsers; private int mSecondaryUser = UserHandle.USER_NULL; @@ -160,6 +160,7 @@ public class UserSwitcherController implements Dumpable { private FalsingManager mFalsingManager; private View mView; private String mCreateSupervisedUserPackage; + private GlobalSettings mGlobalSettings; @Inject public UserSwitcherController(Context context, @@ -177,6 +178,7 @@ public class UserSwitcherController implements Dumpable { FalsingManager falsingManager, TelephonyListenerManager telephonyListenerManager, SecureSettings secureSettings, + GlobalSettings globalSettings, @Background Executor bgExecutor, @LongRunning Executor longRunningExecutor, @Main Executor uiExecutor, @@ -194,6 +196,7 @@ public class UserSwitcherController implements Dumpable { mFalsingManager = falsingManager; mInteractionJankMonitor = interactionJankMonitor; mLatencyTracker = latencyTracker; + mGlobalSettings = globalSettings; mGuestResumeSessionReceiver = new GuestResumeSessionReceiver( this, mUserTracker, mUiEventLogger, secureSettings); mBgExecutor = bgExecutor; @@ -237,8 +240,10 @@ public class UserSwitcherController implements Dumpable { @Override public void onChange(boolean selfChange) { mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); - mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; + mAddUsersFromLockScreen = mGlobalSettings.getIntForUser( + Settings.Global.ADD_USERS_WHEN_LOCKED, 0, UserHandle.USER_SYSTEM) != 0; + mUserSwitcherEnabled = mGlobalSettings.getIntForUser( + Settings.Global.USER_SWITCHER_ENABLED, 0, UserHandle.USER_SYSTEM) != 0; refreshUsers(UserHandle.USER_NULL); }; }; @@ -246,6 +251,9 @@ public class UserSwitcherController implements Dumpable { Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true, mSettingsObserver); mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), true, + mSettingsObserver); + mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true, mSettingsObserver); mContext.getContentResolver().registerContentObserver( @@ -314,6 +322,10 @@ public class UserSwitcherController implements Dumpable { for (UserInfo info : infos) { boolean isCurrent = currentId == info.id; boolean switchToEnabled = canSwitchUsers || isCurrent; + if (!mUserSwitcherEnabled && !info.isPrimary()) { + continue; + } + if (info.isEnabled()) { if (info.isGuest()) { // Tapping guest icon triggers remove and a user switch therefore @@ -340,9 +352,6 @@ public class UserSwitcherController implements Dumpable { } } } - if (records.size() > 1 || guestRecord != null) { - Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true); - } if (guestRecord == null) { if (mGuestUserAutoCreated) { @@ -411,12 +420,14 @@ public class UserSwitcherController implements Dumpable { } boolean canCreateGuest(boolean hasExistingGuest) { - return (currentUserCanCreateUsers() || anyoneCanCreateUsers()) + return mUserSwitcherEnabled + && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && !hasExistingGuest; } boolean canCreateUser() { - return (currentUserCanCreateUsers() || anyoneCanCreateUsers()) + return mUserSwitcherEnabled + && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY); } @@ -1034,8 +1045,8 @@ public class UserSwitcherController implements Dumpable { private boolean shouldUseSimpleUserSwitcher() { int defaultSimpleUserSwitcher = mContext.getResources().getBoolean( com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0; - return Settings.Global.getInt(mContext.getContentResolver(), - SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0; + return mGlobalSettings.getIntForUser(SIMPLE_USER_SWITCHER_GLOBAL_SETTING, + defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0; } public void startActivity(Intent intent) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt index 6266bf146f5b..f8fdd8d33a57 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt @@ -29,29 +29,29 @@ import org.junit.runner.RunWith class BouncerPanelExpansionCalculatorTest : SysuiTestCase() { @Test fun testGetHostViewScaledExpansion() { - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(1f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(1f)) .isEqualTo(1f) - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.9f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0.9f)) .isEqualTo(1f) - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.59f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0.59f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0f)) .isEqualTo(0f) assertEquals(BouncerPanelExpansionCalculator - .getHostViewScaledExpansion(0.8f), 2f / 3f, 0.01f) + .showBouncerProgress(0.8f), 2f / 3f, 0.01f) } @Test fun testGetBackScrimScaledExpansion() { - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(1f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(1f)) .isEqualTo(1f) assertEquals(BouncerPanelExpansionCalculator - .getBackScrimScaledExpansion(0.95f), 1f / 2f, 0.01f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.9f)) + .aboutToShowBouncerProgress(0.95f), 1f / 2f, 0.01f) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.9f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.5f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0f)) .isEqualTo(0f) } @@ -63,9 +63,9 @@ class BouncerPanelExpansionCalculatorTest : SysuiTestCase() { .getKeyguardClockScaledExpansion(0.8f), 1f / 3f, 0.01f) assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(0.7f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.5f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0f)) .isEqualTo(0f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 50bd9b094761..6bb994f4368a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -27,14 +27,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -62,9 +63,11 @@ import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowMetrics; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; @@ -103,7 +106,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { private SecureSettings mSecureSettings; private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThreadFactory mThreadFactory; - private ArrayList<DecorProvider> mDecorProviders; + private ArrayList<DecorProvider> mPrivacyDecorProviders; @Mock private Display mDisplay; @Mock @@ -216,16 +219,43 @@ public class ScreenDecorationsTest extends SysuiTestCase { } } - private void verifyRoundedCornerViewsVisibility( + @NonNull + private int[] getRoundCornerIdsFromOverlayId(@DisplayCutout.BoundsPosition int overlayId) { + switch (overlayId) { + case BOUNDS_POSITION_LEFT: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_left }; + case BOUNDS_POSITION_TOP: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right }; + case BOUNDS_POSITION_RIGHT: + return new int[] { + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_right }; + case BOUNDS_POSITION_BOTTOM: + return new int[] { + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right }; + default: + throw new IllegalArgumentException("unknown overlayId: " + overlayId); + } + } + + private void verifyRoundedCornerViewsExist( @DisplayCutout.BoundsPosition final int overlayId, - @View.Visibility final int visibility) { + @View.Visibility final boolean isExist) { final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView(); - final View left = overlay.findViewById(R.id.left); - final View right = overlay.findViewById(R.id.right); - assertNotNull(left); - assertNotNull(right); - assertThat(left.getVisibility()).isEqualTo(visibility); - assertThat(right.getVisibility()).isEqualTo(visibility); + for (int id: getRoundCornerIdsFromOverlayId(overlayId)) { + final View view = overlay.findViewById(id); + if (isExist) { + assertNotNull(view); + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } else { + assertNull(view); + } + } } @Nullable @@ -388,8 +418,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mPrivacyDotShowingListener); // Rounded corner views shall not exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -417,8 +447,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -447,8 +477,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -488,21 +518,26 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_top_left); View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testTopRadius, testTopRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testTopRadius, testTopRadius)); + .findViewById(R.id.rounded_corner_top_right); + ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testTopRadius); + assertEquals(leftParams.height, testTopRadius); + assertEquals(rightParams.width, testTopRadius); + assertEquals(rightParams.height, testTopRadius); + leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_bottom_left); rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testBottomRadius, testBottomRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testBottomRadius, testBottomRadius)); + .findViewById(R.id.rounded_corner_bottom_right); + leftParams = leftRoundedCorner.getLayoutParams(); + rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testBottomRadius); + assertEquals(leftParams.height, testBottomRadius); + assertEquals(rightParams.width, testBottomRadius); + assertEquals(rightParams.height, testBottomRadius); } @Test @@ -518,31 +553,27 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - final Size topRadius = new Size(testTopRadius, testTopRadius); - final Size bottomRadius = new Size(testBottomRadius, testBottomRadius); - View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.left); - boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); - - leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.left); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); + View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_top_left); + View bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_bottom_left); + ViewGroup.LayoutParams topParams = topRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); + + topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_top_right); + bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_bottom_right); + topParams = topRoundedCorner.getLayoutParams(); + bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); } @Test @@ -562,8 +593,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -598,8 +629,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -660,10 +691,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Top rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); // Bottom rounded corner views shall exist because of privacy dot // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -691,7 +722,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Left rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false); // Top privacy dots shall not exist because of no privacy verifyDotViewsNullable(true); @@ -745,8 +776,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall not exist because of no privacy dot verifyDotViewsNullable(true); @@ -775,8 +806,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -914,7 +945,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(2)).setShowingListener(null); // Verify each privacy dot id appears only once - mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { + mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { int findCount = 0; for (OverlayWindow overlay: mScreenDecorations.mOverlays) { if (overlay == null) { @@ -968,8 +999,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added with INVISIBLE because of only privacy dot, // but rounded corners visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); verify(mDotViewController, times(1)).setShowingListener( mScreenDecorations.mPrivacyDotShowingListener); @@ -982,8 +1013,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added with VISIBLE because of privacy dot and // cutout, but rounded corners visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); verify(mDotViewController, times(2)).initialize(any(), any(), any(), any()); verify(mDotViewController, times(1)).setShowingListener(null); } @@ -1297,6 +1328,48 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(cutoutView, times(1)).onDisplayChanged(1); } + @Test + public void testHasSameProvidersWithNullOverlays() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + false /* fillCutout */, false /* privacyDot */); + + mScreenDecorations.start(); + + final ArrayList<DecorProvider> newProviders = new ArrayList<>(); + assertTrue(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopRightDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + } + + @Test + public void testHasSameProvidersWithPrivacyDots() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + true /* fillCutout */, true /* privacyDot */); + + mScreenDecorations.start(); + + final ArrayList<DecorProvider> newProviders = new ArrayList<>(); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopRightDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotBottomLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotBottomRightDecorProvider); + assertTrue(mScreenDecorations.hasSameProviders(newProviders)); + } + private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding, boolean multipleRadius, boolean fillCutout, boolean privacyDot) { mContext.getOrCreateTestableResources().addOverride( @@ -1336,14 +1409,14 @@ public class ScreenDecorationsTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout); - mDecorProviders = new ArrayList<>(); + mPrivacyDecorProviders = new ArrayList<>(); if (privacyDot) { - mDecorProviders.add(mPrivacyDotTopLeftDecorProvider); - mDecorProviders.add(mPrivacyDotTopRightDecorProvider); - mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); - mDecorProviders.add(mPrivacyDotBottomRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider); } - when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders); + when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders); when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 666c9e481adc..2341928b2565 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -40,6 +40,9 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Rule @@ -314,7 +317,8 @@ class AuthContainerViewTest : SysuiTestCase() { wakefulnessLifecycle, userManager, lockPatternUtils, - Handler(TestableLooper.get(this).looper) + Handler(TestableLooper.get(this).looper), + FakeExecutor(FakeSystemClock()) ) if (addToView) { @@ -331,10 +335,11 @@ class AuthContainerViewTest : SysuiTestCase() { wakefulnessLifecycle: WakefulnessLifecycle, userManager: UserManager, lockPatternUtils: LockPatternUtils, - mainHandler: Handler + mainHandler: Handler, + bgExecutor: DelayableExecutor ) : AuthContainerView( config, fpProps, faceProps, - wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler + wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler, bgExecutor ) { override fun postOnAnimation(runnable: Runnable) { runnable.run() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 190228d80cde..4858ab5234f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -80,8 +80,11 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Rule; @@ -156,6 +159,7 @@ public class AuthControllerTest extends SysuiTestCase { private Execution mExecution; private TestableLooper mTestableLooper; private Handler mHandler; + private DelayableExecutor mBackgroundExecutor; private TestableAuthController mAuthController; @Before @@ -164,6 +168,7 @@ public class AuthControllerTest extends SysuiTestCase { mExecution = new FakeExecution(); mTestableLooper = TestableLooper.get(this); mHandler = new Handler(mTestableLooper.getLooper()); + mBackgroundExecutor = new FakeExecutor(new FakeSystemClock()); when(mContextSpy.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) @@ -759,11 +764,12 @@ public class AuthControllerTest extends SysuiTestCase { super(context, execution, commandQueue, activityTaskManager, windowManager, fingerprintManager, faceManager, udfpsControllerFactory, sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, - mUserManager, mLockPatternUtils, statusBarStateController, mHandler); + mUserManager, mLockPatternUtils, statusBarStateController, mHandler, + mBackgroundExecutor); } @Override - protected AuthDialog buildDialog(PromptInfo promptInfo, + protected AuthDialog buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricManager.BiometricMultiSensorMode int multiSensorConfig, diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt index e95eb4ef6509..f5990be8e0c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt @@ -42,6 +42,7 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -81,6 +82,8 @@ class ActionReceiverTest : SysuiTestCase() { @Mock private lateinit var unregisterFunction: BroadcastReceiver.() -> Unit @Mock + private lateinit var isPendingRemovalFunction: (BroadcastReceiver, Int) -> Boolean + @Mock private lateinit var receiver1: BroadcastReceiver @Mock private lateinit var receiver2: BroadcastReceiver @@ -98,13 +101,16 @@ class ActionReceiverTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(FakeSystemClock()) + `when`(isPendingRemovalFunction(any(), anyInt())).thenReturn(false) + actionReceiver = ActionReceiver( ACTION1, USER.identifier, registerFunction, unregisterFunction, executor, - logger + logger, + isPendingRemovalFunction ) } @@ -249,6 +255,20 @@ class ActionReceiverTest : SysuiTestCase() { verify(logger).logBroadcastDispatched(anyInt(), eq(ACTION1), sameNotNull(receiver1)) } + @Test + fun testBroadcastNotDispatchingOnPendingRemoval() { + `when`(isPendingRemovalFunction(receiver1, USER.identifier)).thenReturn(true) + + val receiverData = ReceiverData(receiver1, IntentFilter(ACTION1), directExecutor, USER) + + actionReceiver.addReceiverData(receiverData) + + val intent = Intent(ACTION1) + actionReceiver.onReceive(mContext, intent) + executor.runAllReady() + verify(receiver1, never()).onReceive(any(), eq(intent)) + } + @Test(expected = IllegalStateException::class) fun testBroadcastWithWrongAction_throwsException() { actionReceiver.onReceive(mContext, Intent(ACTION2)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt index a1d19332b537..7795d2caf091 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt @@ -41,6 +41,8 @@ import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -85,6 +87,8 @@ class BroadcastDispatcherTest : SysuiTestCase() { private lateinit var logger: BroadcastDispatcherLogger @Mock private lateinit var userTracker: UserTracker + @Mock + private lateinit var removalPendingStore: PendingRemovalStore private lateinit var executor: Executor @@ -108,6 +112,7 @@ class BroadcastDispatcherTest : SysuiTestCase() { mock(DumpManager::class.java), logger, userTracker, + removalPendingStore, mapOf(0 to mockUBRUser0, 1 to mockUBRUser1)) // These should be valid filters @@ -325,6 +330,57 @@ class BroadcastDispatcherTest : SysuiTestCase() { broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } + @Test + fun testTaggedReceiverForRemovalImmediately_allUsers() { + broadcastDispatcher.unregisterReceiver(broadcastReceiver) + + verify(removalPendingStore).tagForRemoval(broadcastReceiver, UserHandle.USER_ALL) + verify(removalPendingStore, never()).clearPendingRemoval(eq(broadcastReceiver), anyInt()) + } + + @Test + fun testTaggedReceiverForRemovalImmediately_singleUser() { + val user = 0 + broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, UserHandle.of(user)) + + verify(removalPendingStore).tagForRemoval(broadcastReceiver, user) + verify(removalPendingStore, never()).clearPendingRemoval(eq(broadcastReceiver), anyInt()) + } + + @Test + fun testUnregisterReceiverClearsPendingRemovalAfterRemoving_allUsers() { + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user0) + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user1) + + broadcastDispatcher.unregisterReceiver(broadcastReceiver) + + testableLooper.processAllMessages() + + val inOrderUser0 = inOrder(mockUBRUser0, removalPendingStore) + inOrderUser0.verify(mockUBRUser0).unregisterReceiver(broadcastReceiver) + inOrderUser0.verify(removalPendingStore) + .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL) + + val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore) + inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver) + inOrderUser1.verify(removalPendingStore) + .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL) + } + + @Test + fun testUnregisterReceiverclearPendingRemovalAfterRemoving_singleUser() { + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user1) + + broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user1) + + testableLooper.processAllMessages() + + val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore) + inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver) + inOrderUser1.verify(removalPendingStore) + .clearPendingRemoval(broadcastReceiver, user1.identifier) + } + private fun setUserMock(mockContext: Context, user: UserHandle) { `when`(mockContext.user).thenReturn(user) `when`(mockContext.userId).thenReturn(user.identifier) @@ -337,8 +393,17 @@ class BroadcastDispatcherTest : SysuiTestCase() { dumpManager: DumpManager, logger: BroadcastDispatcherLogger, userTracker: UserTracker, + removalPendingStore: PendingRemovalStore, var mockUBRMap: Map<Int, UserBroadcastDispatcher> - ) : BroadcastDispatcher(context, bgLooper, executor, dumpManager, logger, userTracker) { + ) : BroadcastDispatcher( + context, + bgLooper, + executor, + dumpManager, + logger, + userTracker, + removalPendingStore + ) { override fun createUBRForUser(userId: Int): UserBroadcastDispatcher { return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt new file mode 100644 index 000000000000..43d2cb8be2d6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt @@ -0,0 +1,81 @@ +package com.android.systemui.broadcast + +import android.content.BroadcastReceiver +import android.os.UserHandle +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class PendingRemovalStoreTest : SysuiTestCase() { + + @Mock + private lateinit var logger: BroadcastDispatcherLogger + @Mock + private lateinit var receiverOne: BroadcastReceiver + @Mock + private lateinit var receiverTwo: BroadcastReceiver + + private lateinit var store: PendingRemovalStore + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + store = PendingRemovalStore(logger) + } + + @Test + fun testTagForRemoval_logged() { + val user = 10 + store.tagForRemoval(receiverOne, 10) + + verify(logger).logTagForRemoval(user, receiverOne) + } + + @Test + fun testClearedPendingRemoval_logged() { + val user = UserHandle.USER_ALL + store.clearPendingRemoval(receiverOne, user) + + verify(logger).logClearedAfterRemoval(user, receiverOne) + } + + @Test + fun testTaggedReceiverMarkedAsPending_specificUser() { + val user = 10 + store.tagForRemoval(receiverOne, user) + + assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() + assertThat(store.isPendingRemoval(receiverOne, user + 1)).isFalse() + assertThat(store.isPendingRemoval(receiverOne, UserHandle.USER_ALL)).isFalse() + } + + @Test + fun testTaggedReceiverMarkedAsPending_allUsers() { + val user = 10 + store.tagForRemoval(receiverOne, UserHandle.USER_ALL) + + assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() + assertThat(store.isPendingRemoval(receiverOne, user + 1)).isTrue() + assertThat(store.isPendingRemoval(receiverOne, UserHandle.USER_ALL)).isTrue() + } + + @Test + fun testOnlyBlockCorrectReceiver() { + val user = 10 + store.tagForRemoval(receiverOne, user) + + assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() + assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse() + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt index 116b81d4d5ca..39e4467bd84f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt @@ -68,6 +68,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { private lateinit var mockContext: Context @Mock private lateinit var logger: BroadcastDispatcherLogger + @Mock + private lateinit var removalPendingStore: PendingRemovalStore private lateinit var testableLooper: TestableLooper private lateinit var userBroadcastDispatcher: UserBroadcastDispatcher @@ -84,7 +86,13 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { fakeExecutor = FakeExecutor(FakeSystemClock()) userBroadcastDispatcher = object : UserBroadcastDispatcher( - mockContext, USER_ID, testableLooper.looper, mock(Executor::class.java), logger) { + mockContext, + USER_ID, + testableLooper.looper, + mock(Executor::class.java), + logger, + removalPendingStore + ) { override fun createActionReceiver( action: String, permission: String?, @@ -216,7 +224,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { USER_ID, testableLooper.looper, fakeExecutor, - logger + logger, + removalPendingStore ) uBR.registerReceiver( ReceiverData( @@ -243,7 +252,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { USER_ID, testableLooper.looper, fakeExecutor, - logger + logger, + removalPendingStore ) uBR.registerReceiver( ReceiverData( diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt index ca74df0a23c5..69366fa0d4a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -19,25 +19,19 @@ package com.android.systemui.decor import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.util.mockito.eq import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never import org.mockito.Mockito.spy +import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -45,62 +39,144 @@ import org.mockito.Mockito.`when` as whenever class OverlayWindowTest : SysuiTestCase() { companion object { - private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container - private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right + private val TEST_DECOR_VIEW_ID_1 = R.id.privacy_dot_top_left_container + private val TEST_DECOR_VIEW_ID_2 = R.id.privacy_dot_bottom_left_container + private val TEST_DECOR_VIEW_ID_3 = R.id.privacy_dot_bottom_right_container } private lateinit var overlay: OverlayWindow - - @Mock private lateinit var layoutInflater: LayoutInflater - @Mock private lateinit var decorProvider: DecorProvider + private lateinit var decorProvider1: DecorProvider + private lateinit var decorProvider2: DecorProvider + private lateinit var decorProvider3: DecorProvider @Before fun setUp() { - MockitoAnnotations.initMocks(this) - - layoutInflater = spy(LayoutInflater.from(mContext)) - - overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - - whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID) - whenever(decorProvider.inflateView( - eq(layoutInflater), - eq(overlay.rootView), - anyInt()) - ).then { - val layoutInflater = it.getArgument<LayoutInflater>(0) - val parent = it.getArgument<ViewGroup>(1) - layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent) - return@then parent.getChildAt(parent.childCount - 1) - } - } + decorProvider1 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_1, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + layoutId = R.layout.privacy_dot_top_left)) + decorProvider2 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_2, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + layoutId = R.layout.privacy_dot_bottom_left)) + decorProvider3 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_3, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + layoutId = R.layout.privacy_dot_bottom_right)) - @Test - fun testAnyBoundsPositionShallNoExceptionForConstructor() { - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM) + overlay = OverlayWindow(mContext) } @Test fun testAddProvider() { @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - verify(decorProvider, Mockito.times(1)).inflateView( - eq(layoutInflater), eq(overlay.rootView), eq(rotation)) - val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID) - Assert.assertNotNull(viewFoundFromRootView) - Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID)) + overlay.addDecorProvider(decorProvider1, rotation) + overlay.addDecorProvider(decorProvider2, rotation) + + verify(decorProvider1, times(1)).inflateView( + mContext, overlay.rootView, rotation) + verify(decorProvider2, times(1)).inflateView( + mContext, overlay.rootView, rotation) + + val view1FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1) + Assert.assertNotNull(view1FoundFromRootView) + Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1)) + val view2FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_2) + Assert.assertNotNull(view2FoundFromRootView) + Assert.assertEquals(view2FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_2)) } @Test fun testRemoveView() { - @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - overlay.removeView(TEST_DECOR_VIEW_ID) - val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.removeView(TEST_DECOR_VIEW_ID_1) + + val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1) Assert.assertNull(viewFoundFromRootView) - Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + } + + @Test + fun testOnReloadResAndMeasureWithoutIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testOnReloadResAndMeasureWithIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + filterIds = arrayOf(TEST_DECOR_VIEW_ID_2), + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, never()).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testRemoveRedundantViewsWithNullParameter() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + + overlay.removeRedundantViews(null) + + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2)) + } + + @Test + fun testRemoveRedundantViewsWith2Providers() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + + overlay.removeRedundantViews(IntArray(2).apply { + this[0] = TEST_DECOR_VIEW_ID_3 + this[1] = TEST_DECOR_VIEW_ID_1 + }) + + Assert.assertNotNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + Assert.assertNotNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2)) + } + + @Test + fun testHasSameProviders() { + Assert.assertTrue(overlay.hasSameProviders(emptyList())) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) + + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + Assert.assertFalse(overlay.hasSameProviders(emptyList())) + Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) + + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + Assert.assertFalse(overlay.hasSameProviders(emptyList())) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt index bac08176d2eb..171b76748d26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.decor import android.content.res.Resources import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import androidx.test.filters.SmallTest import com.android.systemui.R @@ -32,7 +31,6 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) @SmallTest class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt new file mode 100644 index 000000000000..621bcf69bb03 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -0,0 +1,142 @@ +/* + * 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.decor + +import android.testing.AndroidTestingRunner +import android.util.Size +import android.view.DisplayCutout +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.spy + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { + + @Mock private lateinit var roundedCornerResDelegate: RoundedCornerResDelegate + private lateinit var roundedCornerDecorProviderFactory: RoundedCornerDecorProviderFactory + + @Before + fun setUp() { + roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null)) + } + + @Test + fun testNoRoundedCorners() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(false, roundedCornerDecorProviderFactory.hasProviders) + Assert.assertEquals(0, roundedCornerDecorProviderFactory.providers.size) + } + + @Test + fun testHasRoundedCornersIfTopWidthLargerThan0() { + Mockito.doReturn(Size(1, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun testHasRoundedCornersIfBottomWidthLargerThan0() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(1, 1)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun test4CornerDecorProvidersInfo() { + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(true).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(4, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 2effaec58a86..1fec38018f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -45,7 +45,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { } @Test - fun testReloadAllAndDefaultRadius() { + fun testUpdateDisplayUniqueId() { mContext.orCreateTestableResources.addOverrides( mockTypeArray = mockTypedArray, radius = 3, @@ -65,7 +65,34 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { radiusTop = 6, radiusBottom = 0) - roundedCornerResDelegate.reloadAll("test") + roundedCornerResDelegate.updateDisplayUniqueId("test", null) + + assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) + } + + @Test + fun testNotUpdateDisplayUniqueIdButChangeRefreshToken() { + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 3, + radiusTop = 0, + radiusBottom = 4, + multipleRadius = false) + + roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + + assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) + assertEquals(false, roundedCornerResDelegate.isMultipleRadius) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 5, + radiusTop = 6, + radiusBottom = 0) + + roundedCornerResDelegate.updateDisplayUniqueId(null, 1) assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) @@ -82,11 +109,21 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) val factor = 5 - roundedCornerResDelegate.updateTuningSizeFactor(factor) + roundedCornerResDelegate.updateTuningSizeFactor(factor, 1) val length = (factor * mContext.resources.displayMetrics.density).toInt() assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radiusTop = 1, + radiusBottom = 2, + multipleRadius = false) + roundedCornerResDelegate.updateTuningSizeFactor(null, 2) + + assertEquals(Size(1, 1), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 6453c204342c..d70467ddeebe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -177,7 +177,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { final float bouncerHideAmount = 0.05f; final float scaledFraction = - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(bouncerHideAmount); + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(bouncerHideAmount); bouncerExpansionCaptor.getValue().onExpansionChanged(bouncerHideAmount); verify(mBlurUtils).blurRadiusOfRatio(1 - scaledFraction); 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 829445eb92be..4fbdb7c512bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -214,7 +214,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { assertThat(mQsFragmentView.getAlpha()) .isEqualTo( - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion( + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( transitionProgress)); } @@ -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/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 324f0ac08fad..b1f10751119e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -44,12 +44,15 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.PendingIntent; +import android.app.Person; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -116,11 +119,15 @@ public class NotificationInfoTest extends SysuiTestCase { private ChannelEditorDialogController mChannelEditorDialogController; @Mock private AssistantFeedbackController mAssistantFeedbackController; + @Mock + private TelecomManager mTelecomManager; @Before public void setUp() throws Exception { mTestableLooper = TestableLooper.get(this); + mContext.addMockSystemService(TelecomManager.class, mTelecomManager); + mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); // Inflate the layout @@ -161,7 +168,7 @@ public class NotificationInfoTest extends SysuiTestCase { IMPORTANCE_LOW); mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, - new Notification(), UserHandle.CURRENT, null, 0); + new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false); when(mAssistantFeedbackController.getInlineDescriptionResource(any())) @@ -632,6 +639,92 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_whenCurrentlyInCall() throws Exception { + when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true); + + Person person = new Person.Builder() + .setName("caller") + .build(); + Notification.Builder nb = new Notification.Builder( + mContext, mNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(Notification.CallStyle.forOngoingCall( + person, mock(PendingIntent.class))) + .setFullScreenIntent(mock(PendingIntent.class), true) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); + mEntry.setSbn(mSbn); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + null, + mUiEventLogger, + true, + false, + true, + mAssistantFeedbackController); + final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text); + assertEquals(View.VISIBLE, view.getVisibility()); + assertEquals(mContext.getString(R.string.notification_unblockable_call_desc), + view.getText()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility()); + } + + @Test + public void testBindNotification_whenCurrentlyInCall_notCall() throws Exception { + when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true); + + Person person = new Person.Builder() + .setName("caller") + .build(); + Notification.Builder nb = new Notification.Builder( + mContext, mNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setFullScreenIntent(mock(PendingIntent.class), true) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); + mEntry.setSbn(mSbn); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + null, + mUiEventLogger, + true, + false, + true, + mAssistantFeedbackController); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility()); + assertEquals(VISIBLE, + mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility()); + } + + @Test public void testBindNotification_automaticIsVisible() throws Exception { when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(true); mNotificationInfo.bindNotification( 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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 509fa3b01b0a..69d7932a81fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -1244,11 +1244,11 @@ public class ScrimControllerTest extends SysuiTestCase { float expansion = 0.8f; float expectedAlpha = - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion); expansion = 0.2f; - expectedAlpha = BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + expectedAlpha = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion); } @@ -1284,7 +1284,7 @@ public class ScrimControllerTest extends SysuiTestCase { // Verify normal behavior after mScrimController.setUnocclusionAnimationRunning(false); float expansion = 0.4f; - float alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); } @@ -1316,15 +1316,15 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.transitionTo(ScrimState.KEYGUARD); float expansion = 0.8f; - float alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); expansion = 0.4f; - alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); expansion = 0.2f; - alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index 799dafcd01b8..e3d2a2951c97 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -28,6 +28,7 @@ import android.hardware.fingerprint.FingerprintManager import android.os.Handler import android.os.UserHandle import android.os.UserManager +import android.provider.Settings import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.ThreadedRenderer @@ -51,6 +52,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.NotificationShadeWindowView import com.android.systemui.telephony.TelephonyListenerManager import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals @@ -67,6 +69,7 @@ import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn +import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -95,6 +98,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView @Mock private lateinit var threadedRenderer: ThreadedRenderer @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Mock private lateinit var globalSettings: GlobalSettings private lateinit var testableLooper: TestableLooper private lateinit var bgExecutor: FakeExecutor private lateinit var longRunningExecutor: FakeExecutor @@ -148,6 +152,22 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userTracker.userId).thenReturn(ownerId) `when`(userTracker.userInfo).thenReturn(ownerInfo) + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.ADD_USERS_WHEN_LOCKED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(1) + setupController() } @@ -168,6 +188,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { falsingManager, telephonyListenerManager, secureSettings, + globalSettings, bgExecutor, longRunningExecutor, uiExecutor, @@ -469,4 +490,43 @@ class UserSwitcherControllerTest : SysuiTestCase() { // THEN a supervised user can NOT be constructed assertFalse(userSwitcherController.canCreateSupervisedUser()) } + + @Test + fun testCannotCreateUserWhenUserSwitcherDisabled() { + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + setupController() + assertFalse(userSwitcherController.canCreateUser()) + } + + @Test + fun testCannotCreateGuestUserWhenUserSwitcherDisabled() { + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + setupController() + assertFalse(userSwitcherController.canCreateGuest(false)) + } + + @Test + fun testCannotCreateSupervisedUserWhenUserSwitcherDisabled() { + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + setupController() + assertFalse(userSwitcherController.canCreateSupervisedUser()) + } } 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/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 2d328d8b0949..210532a88a8c 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -153,7 +153,7 @@ import java.util.concurrent.TimeUnit; public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); - private static final int LOCAL_LOG_LINE_COUNT = 128; + private static final int LOCAL_LOG_LINE_COUNT = 512; // Public for use in all other VCN classes @NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT); @@ -456,7 +456,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; mLastSnapshot = snapshot; - logDbg("new snapshot: " + mLastSnapshot); + logInfo("new snapshot: " + mLastSnapshot); // Start any VCN instances as necessary for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { @@ -522,6 +522,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) { + logInfo("Stopping VCN config for subGrp: " + uuidToTeardown); + // Remove in 2 steps. Make sure teardownAsync is triggered before removing from the map. final Vcn vcnToTeardown = mVcns.get(uuidToTeardown); if (vcnToTeardown == null) { @@ -567,7 +569,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { - logDbg("Starting VCN config for subGrp: " + subscriptionGroup); + logInfo("Starting VCN config for subGrp: " + subscriptionGroup); // TODO(b/193687515): Support multiple VCNs active at the same time if (!mVcns.isEmpty()) { @@ -626,7 +628,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (!config.getProvisioningPackageName().equals(opPkgName)) { throw new IllegalArgumentException("Mismatched caller and VcnConfig creator"); } - logDbg("VCN config updated for subGrp: " + subscriptionGroup); + logInfo("VCN config updated for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); @@ -652,7 +654,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(opPkgName, "opPkgName was null"); - logDbg("VCN config cleared for subGrp: " + subscriptionGroup); + logInfo("VCN config cleared for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), opPkgName); @@ -1050,24 +1052,34 @@ public class VcnManagementService extends IVcnManagementService.Stub { Slog.d(TAG, msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, msg); + LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, msg, tr); + LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg + tr); + } + private void logErr(String msg) { Slog.e(TAG, msg); - LOCAL_LOG.log(TAG + " ERR: " + msg); + LOCAL_LOG.log("[ERR] [" + TAG + "] " + msg); } private void logErr(String msg, Throwable tr) { Slog.e(TAG, msg, tr); - LOCAL_LOG.log(TAG + " ERR: " + msg + tr); + LOCAL_LOG.log("[ERR ] [" + TAG + "] " + msg + tr); } private void logWtf(String msg) { Slog.wtf(TAG, msg); - LOCAL_LOG.log(TAG + " WTF: " + msg); + LOCAL_LOG.log("[WTF] [" + TAG + "] " + msg); } private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, msg, tr); - LOCAL_LOG.log(TAG + " WTF: " + msg + tr); + LOCAL_LOG.log("[WTF ] [" + TAG + "] " + msg + tr); } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index efde2a52f4dc..35f7e064e358 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -49,6 +49,12 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; +import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; +import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.os.FactoryTest.FACTORY_TEST_OFF; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; @@ -4815,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) { @@ -4963,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; @@ -17306,8 +17312,22 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO: We can reuse this data in // ProcessList#incrementProcStateSeqAndNotifyAppsLOSP instead of calling into // NetworkManagementService. - return mUidNetworkBlockedReasons.get(uid, BLOCKED_REASON_NONE) - != BLOCKED_REASON_NONE; + final int uidBlockedReasons = mUidNetworkBlockedReasons.get( + uid, BLOCKED_REASON_NONE); + if (uidBlockedReasons == BLOCKED_REASON_NONE) { + return false; + } + final int topExemptedBlockedReasons = BLOCKED_REASON_BATTERY_SAVER + | BLOCKED_REASON_DOZE + | BLOCKED_REASON_APP_STANDBY + | BLOCKED_REASON_LOW_POWER_STANDBY + | BLOCKED_METERED_REASON_DATA_SAVER + | BLOCKED_METERED_REASON_USER_RESTRICTED; + final int effectiveBlockedReasons = + uidBlockedReasons & ~topExemptedBlockedReasons; + // Only consider it as blocked if it is not blocked by a reason + // that is not exempted by app being in the top state. + return effectiveBlockedReasons == BLOCKED_REASON_NONE; } } 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/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b886196755ea..c04377389e8e 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -173,7 +173,7 @@ class UserController implements Handler.Callback { // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not // complete within {@link USER_JOURNEY_TIMEOUT}. - private static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200; + static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200; // Wait time for completing the user journey. If a user journey is not complete within this // time, the remaining lifecycle events for the journey would not be logged in statsd. // Timeout set for 90 seconds. @@ -209,12 +209,15 @@ class UserController implements Handler.Callback { FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; private static final int USER_JOURNEY_USER_CREATE = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; + private static final int USER_JOURNEY_USER_STOP = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; @IntDef(prefix = { "USER_JOURNEY" }, value = { USER_JOURNEY_UNKNOWN, USER_JOURNEY_USER_SWITCH_FG, USER_JOURNEY_USER_SWITCH_UI, USER_JOURNEY_USER_START, USER_JOURNEY_USER_CREATE, + USER_JOURNEY_USER_STOP }) @interface UserJourney {} @@ -233,6 +236,8 @@ class UserController implements Handler.Callback { FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; + private static final int USER_LIFECYCLE_EVENT_STOP_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = { USER_LIFECYCLE_EVENT_UNKNOWN, USER_LIFECYCLE_EVENT_SWITCH_USER, @@ -241,6 +246,7 @@ class UserController implements Handler.Callback { USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, USER_LIFECYCLE_EVENT_UNLOCKING_USER, USER_LIFECYCLE_EVENT_UNLOCKED_USER, + USER_LIFECYCLE_EVENT_STOP_USER }) @interface UserLifecycleEvent {} @@ -1008,6 +1014,10 @@ class UserController implements Handler.Callback { return; } + logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP); + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_BEGIN); + if (stopUserCallback != null) { uss.mStopCallbacks.add(stopUserCallback); } @@ -1066,6 +1076,9 @@ class UserController implements Handler.Callback { synchronized (mLock) { if (uss.state != UserState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_NONE); + clearSessionId(userId); return; } uss.setState(UserState.STATE_SHUTDOWN); @@ -1165,10 +1178,18 @@ class UserController implements Handler.Callback { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_FINISH); + clearSessionId(userId); + if (!lockUser) { return; } dispatchUserLocking(userIdToLock, keyEvictedCallbacks); + } else { + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_NONE); + clearSessionId(userId); } } @@ -2962,13 +2983,13 @@ class UserController implements Handler.Callback { if (userJourneySession != null) { // TODO(b/157007231): Move this logic to a separate class/file. if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI - && journey == USER_JOURNEY_USER_START) - || (userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG - && journey == USER_JOURNEY_USER_START)) { + || userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG) + && (journey == USER_JOURNEY_USER_START + || journey == USER_JOURNEY_USER_STOP)) { /* - * There is already a user switch journey, and a user start journey for the same - * target user received. User start journey is most likely a part of user switch - * journey so no need to create a new journey for user start. + * There is already a user switch journey, and a user start or stop journey for + * the same target user received. New journey is most likely a part of user + * switch journey so no need to create a new journey. */ if (DEBUG_MU) { Slogf.d(TAG, journey + " not logged as it is expected to be part of " diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 465e5e9d8453..b1b5d3ffb2c7 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -9712,7 +9712,7 @@ public class AudioService extends IAudioService.Stub //========================================================================================== static final int LOG_NB_EVENTS_LIFECYCLE = 20; static final int LOG_NB_EVENTS_PHONE_STATE = 20; - static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30; + static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50; static final int LOG_NB_EVENTS_FORCE_USE = 20; static final int LOG_NB_EVENTS_VOLUME = 40; static final int LOG_NB_EVENTS_DYN_POLICY = 10; 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/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 4c9b28b1bd18..d9e4828e7eb4 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.Manifest.permission.CONTROL_VPN; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; @@ -2549,6 +2550,7 @@ public class Vpn { req = new NetworkRequest.Builder() .clearCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .addCapability(NET_CAPABILITY_NOT_VPN) .build(); } else { // Basically, the request here is referring to the default request which is defined diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index a155095c0725..982ac3c84482 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -76,6 +76,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final boolean mIsBootDisplayModeSupported; + private Context mOverlayContext; + // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { @@ -1222,7 +1224,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { /** Supplies a context whose Resources apply runtime-overlays */ Context getOverlayContext() { - return ActivityThread.currentActivityThread().getSystemUiContext(); + if (mOverlayContext == null) { + mOverlayContext = ActivityThread.currentActivityThread().getSystemUiContext(); + } + return mOverlayContext; } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java index 1c296e5b5640..8647680e52a6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java @@ -83,7 +83,9 @@ public final class HdmiCecStandbyModeHandler { private final HdmiCecLocalDevice mDevice; private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>(); - private final CecMessageHandler mDefaultHandler = new Aborter( + private final CecMessageHandler mDefaultHandler; + + private final CecMessageHandler mAborterUnrecognizedOpcode = new Aborter( Constants.ABORT_UNRECOGNIZED_OPCODE); private final CecMessageHandler mAborterIncorrectMode = new Aborter( Constants.ABORT_NOT_IN_CORRECT_MODE); @@ -95,6 +97,10 @@ public final class HdmiCecStandbyModeHandler { mUserControlProcessedHandler = new UserControlProcessedHandler(); private void addCommonHandlers() { + addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); + } + + private void addTvHandlers() { addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander); addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander); addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander); @@ -118,17 +124,13 @@ public final class HdmiCecStandbyModeHandler { addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser); addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser); - addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); - addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser); addHandler(Constants.MESSAGE_ABORT, mBypasser); addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser); addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode); addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode); - } - private void addTvHandlers() { addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler); addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler); @@ -153,6 +155,9 @@ public final class HdmiCecStandbyModeHandler { addCommonHandlers(); if (mDevice.getType() == HdmiDeviceInfo.DEVICE_TV) { addTvHandlers(); + mDefaultHandler = mAborterUnrecognizedOpcode; + } else { + mDefaultHandler = mBypasser; } } diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java index 629011a86bb4..90a2f480643d 100644 --- a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java +++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java @@ -115,5 +115,13 @@ public class HealthRegCallbackAidl { public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException { mServiceInfoCallback.update(healthInfo); } + @Override + public String getInterfaceHash() { + return IHealthInfoCallback.HASH; + } + @Override + public int getInterfaceVersion() { + return IHealthInfoCallback.VERSION; + } } } diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index a1ee46b4c943..acc0746764c5 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -389,6 +389,15 @@ public abstract class IContextHubWrapper { mCallback.handleTransactionResult(transactionId, success); }); } + @Override + public String getInterfaceHash() { + return android.hardware.contexthub.IContextHubCallback.HASH; + } + + @Override + public int getInterfaceVersion() { + return android.hardware.contexthub.IContextHubCallback.VERSION; + } } ContextHubWrapperAidl(android.hardware.contexthub.IContextHub hub) { diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java index b45bfb1c2d92..79088d0398d2 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java @@ -116,6 +116,10 @@ public class LogAccessDialogActivity extends Activity implements } mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + if (mPackageName == null || mPackageName.length() == 0) { + throw new NullPointerException("Package Name is null"); + } + mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); @@ -154,12 +158,17 @@ public class LogAccessDialogActivity extends Activity implements CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO, UserHandle.getUserId(uid)).loadLabel(pm); - if (appLabel == null) { + if (appLabel == null || appLabel.length() == 0) { throw new NameNotFoundException("Application Label is null"); } - return context.getString(com.android.internal.R.string.log_access_confirmation_title, - appLabel); + String titleString = context.getString( + com.android.internal.R.string.log_access_confirmation_title, appLabel); + if (titleString == null || titleString.length() == 0) { + throw new NullPointerException("Title is null"); + } + + return titleString; } /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f42e734b96ec..8ed145c8d1b1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -240,6 +240,7 @@ import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeProto; +import android.telecom.TelecomManager; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -509,6 +510,7 @@ public class NotificationManagerService extends SystemService { private ShortcutHelper mShortcutHelper; private PermissionHelper mPermissionHelper; private UsageStatsManagerInternal mUsageStatsManagerInternal; + private TelecomManager mTelecomManager; final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; @@ -2100,7 +2102,8 @@ public class NotificationManagerService extends SystemService { NotificationHistoryManager historyManager, StatsManager statsManager, TelephonyManager telephonyManager, ActivityManagerInternal ami, MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper, - UsageStatsManagerInternal usageStatsManagerInternal) { + UsageStatsManagerInternal usageStatsManagerInternal, + TelecomManager telecomManager) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2129,6 +2132,7 @@ public class NotificationManagerService extends SystemService { mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class); mDpm = dpm; mUm = userManager; + mTelecomManager = telecomManager; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -2420,7 +2424,8 @@ public class NotificationManagerService extends SystemService { PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(), AppGlobals.getPermissionManager(), mEnableAppSettingMigration, mForceUserSetOnUpgrade), - LocalServices.getService(UsageStatsManagerInternal.class)); + LocalServices.getService(UsageStatsManagerInternal.class), + getContext().getSystemService(TelecomManager.class)); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -5535,6 +5540,12 @@ public class NotificationManagerService extends SystemService { } @Override + public boolean isInCall(String pkg, int uid) { + checkCallerIsSystemOrSystemUiOrShell(); + return isCallNotification(pkg, uid); + } + + @Override public void setPrivateNotificationsAllowed(boolean allow) { if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission( @@ -6846,7 +6857,7 @@ public class NotificationManagerService extends SystemService { synchronized (mNotificationLock) { isBlocked |= isRecordBlockedLocked(r); } - if (isBlocked && !n.isMediaNotification()) { + if (isBlocked && !(n.isMediaNotification() || isCallNotification(pkg, uid, n))) { if (DBG) { Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName() + " by user request."); @@ -6858,6 +6869,23 @@ public class NotificationManagerService extends SystemService { return true; } + private boolean isCallNotification(String pkg, int uid, Notification n) { + if (n.isStyle(Notification.CallStyle.class)) { + return isCallNotification(pkg, uid); + } + return false; + } + + private boolean isCallNotification(String pkg, int uid) { + final long identity = Binder.clearCallingIdentity(); + try { + return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall( + pkg, UserHandle.getUserHandleForUid(uid)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { if (mEnableAppSettingMigration) { return mPermissionHelper.hasPermission(uid); diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index b4230c11bcab..a09aa7cea0a4 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -55,8 +55,10 @@ public final class PermissionHelper { private final PermissionManagerServiceInternal mPmi; private final IPackageManager mPackageManager; private final IPermissionManager mPermManager; - // TODO (b/194833441): Remove when the migration is enabled + // TODO (b/194833441): Remove this boolean (but keep the isMigrationEnabled() method) + // when the migration is enabled private final boolean mMigrationEnabled; + private final boolean mIsTv; private final boolean mForceUserSetOnUpgrade; public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager, @@ -67,10 +69,17 @@ public final class PermissionHelper { mPermManager = permManager; mMigrationEnabled = migrationEnabled; mForceUserSetOnUpgrade = forceUserSetOnUpgrade; + boolean isTv; + try { + isTv = mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0); + } catch (RemoteException e) { + isTv = false; + } + mIsTv = isTv; } public boolean isMigrationEnabled() { - return mMigrationEnabled; + return mMigrationEnabled && !mIsTv; } /** diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index 5865adb96333..dff7100e90e3 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -76,6 +76,7 @@ import com.android.server.utils.Watcher; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; @@ -102,6 +103,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * application B is implicitly allowed to query for application A; regardless of any manifest * entries. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mImplicitlyQueryable; private final SnapshotCache<WatchedSparseSetArray<Integer>> mImplicitQueryableSnapshot; @@ -111,6 +113,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * interacted with it, but could keep across package updates. For example, if application A * grants persistable uri permission to application B; regardless of any manifest entries. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable; private final SnapshotCache<WatchedSparseSetArray<Integer>> @@ -120,6 +123,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query other App IDs via package name to the * list of packages that they can see. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueriesViaPackage; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaPackageSnapshot; @@ -128,6 +132,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query others via component match to the list * of packages that the they resolve to. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueriesViaComponent; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaComponentSnapshot; @@ -136,6 +141,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query other App IDs via library name to the * list of packages that they can see. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot; @@ -158,6 +164,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A set of App IDs that are always queryable by any package, regardless of their manifest * content. */ + @GuardedBy("mLock") @Watched private final WatchedArraySet<Integer> mForceQueryable; private final SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot; @@ -173,9 +180,9 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private final FeatureConfig mFeatureConfig; private final OverlayReferenceMapper mOverlayReferenceMapper; private final StateProvider mStateProvider; - private final PackageManagerInternal mPmInternal; private SigningDetails mSystemSigningDetails; + @GuardedBy("mLock") @Watched private final WatchedArrayList<String> mProtectedBroadcasts; private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; @@ -197,6 +204,11 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private volatile boolean mSystemReady = false; /** + * Guards the accesses for the list/set fields except for {@link #mShouldFilterCache} + */ + private final Object mLock = new Object(); + + /** * A cached snapshot. */ private final SnapshotCache<AppsFilterImpl> mSnapshot; @@ -216,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 @@ -284,15 +287,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable String[] forceQueryableList, boolean systemAppsQueryable, @Nullable OverlayReferenceMapper.Provider overlayProvider, - Executor backgroundExecutor, - PackageManagerInternal pmInternal) { + Executor backgroundExecutor) { mFeatureConfig = featureConfig; mForceQueryableByDevicePackageNames = forceQueryableList; mSystemAppsQueryable = systemAppsQueryable; mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/, overlayProvider); mStateProvider = stateProvider; - mPmInternal = pmInternal; mBackgroundExecutor = backgroundExecutor; mShouldFilterCache = new WatchedSparseBooleanMatrix(); mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>( @@ -321,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(); } @@ -330,20 +329,22 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * The copy constructor is used by PackageManagerService to construct a snapshot. */ private AppsFilterImpl(AppsFilterImpl orig) { - mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); - mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); - mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); - mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); - mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); - mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); - mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); - mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); - mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); - mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); - mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); - mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); - mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); - mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + synchronized (orig.mLock) { + mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); + mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); + mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); + mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); + mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); + mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); + mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); + mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); + mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + } mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; mForceQueryableByDevicePackageNames = Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, @@ -359,23 +360,10 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } mBackgroundExecutor = null; - mPmInternal = null; mSnapshot = new SnapshotCache.Sealed<>(); 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 @@ -397,6 +385,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable interface CurrentStateCallback { void currentState(ArrayMap<String, ? extends PackageStateInternal> settings, + Collection<SharedUserSetting> sharedUserSettings, UserInfo[] users); } } @@ -588,12 +577,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable final StateProvider stateProvider = command -> { synchronized (injector.getLock()) { command.currentState(injector.getSettings().getPackagesLocked().untrackedStorage(), + injector.getSettings().getAllSharedUsersLPw(), injector.getUserManagerInternal().getUserInfos()); } }; AppsFilterImpl appsFilter = new AppsFilterImpl(stateProvider, featureConfig, forcedQueryablePackageNames, forceSystemAppsQueryable, null, - injector.getBackgroundExecutor(), pmInt); + injector.getBackgroundExecutor()); featureConfig.setAppsFilter(appsFilter); return appsFilter; } @@ -743,9 +733,11 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return false; } final boolean changed; - changed = retainOnUpdate - ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) - : mImplicitlyQueryable.add(recipientUid, visibleUid); + synchronized (mLock) { + changed = retainOnUpdate + ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) + : mImplicitlyQueryable.add(recipientUid, visibleUid); + } if (changed && DEBUG_LOGGING) { Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: " + recipientUid + " -> " + visibleUid); @@ -758,9 +750,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable mShouldFilterCache.put(recipientUid, visibleUid, false); } } - if (changed) { - onChanged(); - } + onChanged(); return changed; } @@ -788,7 +778,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // let's first remove any prior rules for this package removePackage(newPkgSetting, true /*isReplace*/); } - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { ArraySet<String> additionalChangedPackages = addPackageInternal(newPkgSetting, settings); if (mSystemReady) { @@ -806,9 +796,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable continue; } - updateShouldFilterCacheForPackage(null, - changedPkgSetting, settings, users, USER_ALL, - settings.size()); + updateShouldFilterCacheForPackage(null, changedPkgSetting, + settings, users, USER_ALL, settings.size()); } } } // else, rebuild entire cache when system is ready @@ -835,7 +824,9 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // packages for signature matches for (PackageStateInternal setting : existingSettings.values()) { if (isSystemSigned(mSystemSigningDetails, setting)) { - mForceQueryable.add(setting.getAppId()); + synchronized (mLock) { + mForceQueryable.add(setting.getAppId()); + } } } } @@ -845,75 +836,76 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return null; } - if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { - mQueriesViaComponentRequireRecompute = true; - } - - final boolean newIsForceQueryable = - mForceQueryable.contains(newPkgSetting.getAppId()) - /* shared user that is already force queryable */ - || newPkgSetting.isForceQueryableOverride() /* adb override */ - || (newPkgSetting.isSystem() && (mSystemAppsQueryable - || newPkg.isForceQueryable() - || ArrayUtils.contains(mForceQueryableByDevicePackageNames, - newPkg.getPackageName()))); - if (newIsForceQueryable - || (mSystemSigningDetails != null - && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { - mForceQueryable.add(newPkgSetting.getAppId()); - } + synchronized (mLock) { + if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { + mQueriesViaComponentRequireRecompute = true; + } - for (int i = existingSettings.size() - 1; i >= 0; i--) { - final PackageStateInternal existingSetting = existingSettings.valueAt(i); - if (existingSetting.getAppId() == newPkgSetting.getAppId() - || existingSetting.getPkg() - == null) { - continue; + final boolean newIsForceQueryable = + mForceQueryable.contains(newPkgSetting.getAppId()) + /* shared user that is already force queryable */ + || newPkgSetting.isForceQueryableOverride() /* adb override */ + || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() + || ArrayUtils.contains(mForceQueryableByDevicePackageNames, + newPkg.getPackageName()))); + if (newIsForceQueryable + || (mSystemSigningDetails != null + && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { + mForceQueryable.add(newPkgSetting.getAppId()); } - final AndroidPackage existingPkg = existingSetting.getPkg(); - // let's evaluate the ability of already added packages to see this new package - if (!newIsForceQueryable) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, - newPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); - } - if (canQueryViaPackage(existingPkg, newPkg) - || canQueryAsInstaller(existingSetting, newPkg)) { - mQueriesViaPackage.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); - } - if (canQueryViaUsesLibrary(existingPkg, newPkg)) { - mQueryableViaUsesLibrary.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); + + for (int i = existingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal existingSetting = existingSettings.valueAt(i); + if (existingSetting.getAppId() == newPkgSetting.getAppId() + || existingSetting.getPkg() + == null) { + continue; } - } - // now we'll evaluate our new package's ability to see existing packages - if (!mForceQueryable.contains(existingSetting.getAppId())) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, - existingPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + final AndroidPackage existingPkg = existingSetting.getPkg(); + // let's evaluate the ability of already added packages to see this new package + if (!newIsForceQueryable) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, + newPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaPackage(existingPkg, newPkg) + || canQueryAsInstaller(existingSetting, newPkg)) { + mQueriesViaPackage.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaUsesLibrary(existingPkg, newPkg)) { + mQueryableViaUsesLibrary.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } } - if (canQueryViaPackage(newPkg, existingPkg) - || canQueryAsInstaller(newPkgSetting, existingPkg)) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + // now we'll evaluate our new package's ability to see existing packages + if (!mForceQueryable.contains(existingSetting.getAppId())) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, + existingPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaPackage(newPkg, existingPkg) + || canQueryAsInstaller(newPkgSetting, existingPkg)) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaUsesLibrary(newPkg, existingPkg)) { + mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } } - if (canQueryViaUsesLibrary(newPkg, existingPkg)) { - mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + // if either package instruments the other, mark both as visible to one another + if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null + && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) + || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); } } - // if either package instruments the other, mark both as visible to one another - if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null - && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) - || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); - mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); - } } - int existingSize = existingSettings.size(); ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize); for (int index = 0; index < existingSize; index++) { @@ -954,7 +946,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } private void updateEntireShouldFilterCache(int subjectUserId) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { int userId = USER_NULL; for (int u = 0; u < users.length; u++) { if (subjectUserId == users[u].id) { @@ -969,10 +961,12 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } updateEntireShouldFilterCacheInner(settings, users, userId); }); + onChanged(); } private void updateEntireShouldFilterCacheInner( - ArrayMap<String, ? extends PackageStateInternal> settings, UserInfo[] users, + ArrayMap<String, ? extends PackageStateInternal> settings, + UserInfo[] users, int subjectUserId) { synchronized (mCacheLock) { if (subjectUserId == USER_ALL) { @@ -982,16 +976,18 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } for (int i = settings.size() - 1; i >= 0; i--) { updateShouldFilterCacheForPackage( - null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i); + null /*skipPackage*/, settings.valueAt(i), settings, users, + subjectUserId, i); } } private void updateEntireShouldFilterCacheAsync() { mBackgroundExecutor.execute(() -> { final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>(); + final Collection<SharedUserSetting> sharedUserSettingsCopy = new ArraySet<>(); final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>(); final UserInfo[][] usersRef = new UserInfo[1][]; - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { packagesCache.ensureCapacity(settings.size()); settingsCopy.putAll(settings); usersRef[0] = users; @@ -1001,11 +997,12 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable final AndroidPackage pkg = settings.valueAt(i).getPkg(); packagesCache.put(settings.keyAt(i), pkg); } + sharedUserSettingsCopy.addAll(sharedUserSettings); }); boolean[] changed = new boolean[1]; // We have a cache, let's make sure the world hasn't changed out from under us. - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { if (settings.size() != settingsCopy.size()) { changed[0] = true; return; @@ -1025,7 +1022,9 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Slog.i(TAG, "Rebuilding cache with lock due to package change."); } } else { - updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0], USER_ALL); + updateEntireShouldFilterCacheInner(settingsCopy, + usersRef[0], USER_ALL); + onChanged(); } }); } @@ -1035,7 +1034,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return; } updateEntireShouldFilterCache(newUserId); - onChanged(); } public void onUserDeleted(@UserIdInt int userId) { @@ -1047,7 +1045,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } private void updateShouldFilterCacheForPackage(String packageName) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { if (!mSystemReady) { return; } @@ -1055,6 +1053,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable settings.get(packageName), settings, users, USER_ALL, settings.size() /*maxIndex*/); }); + onChanged(); } private void updateShouldFilterCacheForPackage( @@ -1134,16 +1133,18 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private void collectProtectedBroadcasts( ArrayMap<String, ? extends PackageStateInternal> existingSettings, @Nullable String excludePackage) { - mProtectedBroadcasts.clear(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( - excludePackage)) { - continue; - } - final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); - if (!protectedBroadcasts.isEmpty()) { - mProtectedBroadcasts.addAll(protectedBroadcasts); + synchronized (mLock) { + mProtectedBroadcasts.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( + excludePackage)) { + continue; + } + final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); + if (!protectedBroadcasts.isEmpty()) { + mProtectedBroadcasts.addAll(protectedBroadcasts); + } } } } @@ -1154,24 +1155,26 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable */ private void recomputeComponentVisibility( ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - mQueriesViaComponent.clear(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { - continue; - } - for (int j = existingSettings.size() - 1; j >= 0; j--) { - if (i == j) { - continue; - } - final PackageStateInternal otherSetting = existingSettings.valueAt(j); - if (otherSetting.getPkg() == null || mForceQueryable.contains( - otherSetting.getAppId())) { + synchronized (mLock) { + mQueriesViaComponent.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { continue; } - if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(), - mProtectedBroadcasts)) { - mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); + for (int j = existingSettings.size() - 1; j >= 0; j--) { + if (i == j) { + continue; + } + final PackageStateInternal otherSetting = existingSettings.valueAt(j); + if (otherSetting.getPkg() == null || mForceQueryable.contains( + otherSetting.getAppId())) { + continue; + } + if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(), + mProtectedBroadcasts)) { + mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); + } } } } @@ -1185,8 +1188,10 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable @Nullable public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - if (mForceQueryable.contains(setting.getAppId())) { - return null; + synchronized (mLock) { + if (mForceQueryable.contains(setting.getAppId())) { + return null; + } } // let's reserve max memory to limit the number of allocations SparseArray<int[]> result = new SparseArray<>(users.length); @@ -1249,57 +1254,59 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * @param isReplace if the package is being replaced. */ public void removePackage(PackageStateInternal setting, boolean isReplace) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { final ArraySet<String> additionalChangedPackages; final int userCount = users.length; - for (int u = 0; u < userCount; u++) { - final int userId = users[u].id; - final int removingUid = UserHandle.getUid(userId, setting.getAppId()); - mImplicitlyQueryable.remove(removingUid); - for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { - mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), - removingUid); - } + synchronized (mLock) { + for (int u = 0; u < userCount; u++) { + final int userId = users[u].id; + final int removingUid = UserHandle.getUid(userId, setting.getAppId()); + mImplicitlyQueryable.remove(removingUid); + for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { + mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), + removingUid); + } - if (isReplace) { - continue; - } + if (isReplace) { + continue; + } - mRetainedImplicitlyQueryable.remove(removingUid); - for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { - mRetainedImplicitlyQueryable.remove( - mRetainedImplicitlyQueryable.keyAt(i), removingUid); + mRetainedImplicitlyQueryable.remove(removingUid); + for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { + mRetainedImplicitlyQueryable.remove( + mRetainedImplicitlyQueryable.keyAt(i), removingUid); + } } - } - if (!mQueriesViaComponentRequireRecompute) { - mQueriesViaComponent.remove(setting.getAppId()); - for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { - mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + if (!mQueriesViaComponentRequireRecompute) { + mQueriesViaComponent.remove(setting.getAppId()); + for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { + mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + setting.getAppId()); + } + } + mQueriesViaPackage.remove(setting.getAppId()); + for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { + mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), + setting.getAppId()); + } + mQueryableViaUsesLibrary.remove(setting.getAppId()); + for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { + mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.getAppId()); } - } - mQueriesViaPackage.remove(setting.getAppId()); - for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { - mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), - setting.getAppId()); - } - mQueryableViaUsesLibrary.remove(setting.getAppId()); - for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { - mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), - setting.getAppId()); - } - mForceQueryable.remove(setting.getAppId()); + mForceQueryable.remove(setting.getAppId()); - if (setting.getPkg() != null - && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { - final String removingPackageName = setting.getPkg().getPackageName(); - final ArrayList<String> protectedBroadcasts = new ArrayList<>(); - protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); - collectProtectedBroadcasts(settings, removingPackageName); - if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { - mQueriesViaComponentRequireRecompute = true; + if (setting.getPkg() != null + && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { + final String removingPackageName = setting.getPkg().getPackageName(); + final ArrayList<String> protectedBroadcasts = new ArrayList<>(); + protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); + collectProtectedBroadcasts(settings, removingPackageName); + if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { + mQueriesViaComponentRequireRecompute = true; + } } } @@ -1314,8 +1321,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // update the // cache if (setting.hasSharedUser()) { - final ArraySet<PackageStateInternal> sharedUserPackages = - mPmInternal.getSharedUserPackages(setting.getSharedUserAppId()); + final ArraySet<? extends PackageStateInternal> sharedUserPackages = + getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings); for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { if (sharedUserPackages.valueAt(i) == setting) { continue; @@ -1327,8 +1334,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable removeAppIdFromVisibilityCache(setting.getAppId()); if (mSystemReady && setting.hasSharedUser()) { - final ArraySet<PackageStateInternal> sharedUserPackages = - mPmInternal.getSharedUserPackages(setting.getSharedUserAppId()); + final ArraySet<? extends PackageStateInternal> sharedUserPackages = + getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings); for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { PackageStateInternal siblingSetting = sharedUserPackages.valueAt(i); @@ -1336,8 +1343,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable continue; } updateShouldFilterCacheForPackage( - setting.getPackageName(), siblingSetting, settings, users, - USER_ALL, settings.size()); + setting.getPackageName(), siblingSetting, settings, + users, USER_ALL, settings.size()); } } @@ -1353,14 +1360,24 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable continue; } - updateShouldFilterCacheForPackage(null, - changedPkgSetting, settings, users, USER_ALL, settings.size()); + updateShouldFilterCacheForPackage(null, changedPkgSetting, + settings, users, USER_ALL, settings.size()); } } } - - onChanged(); }); + onChanged(); + } + + private ArraySet<? extends PackageStateInternal> getSharedUserPackages(int sharedUserAppId, + Collection<SharedUserSetting> sharedUserSettings) { + for (SharedUserSetting setting : sharedUserSettings) { + if (setting.mAppId != sharedUserAppId) { + continue; + } + return setting.getPackageStates(); + } + return new ArraySet<>(); } /** @@ -1441,23 +1458,26 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return true; } final PackageStateInternal callingPkgSetting; - final ArraySet<? extends PackageStateInternal> callingSharedPkgSettings; if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof"); } + final ArraySet<PackageStateInternal> callingSharedPkgSettings = new ArraySet<>(); + if (callingSetting instanceof PackageStateInternal) { final PackageStateInternal packageState = (PackageStateInternal) callingSetting; if (packageState.hasSharedUser()) { callingPkgSetting = null; - callingSharedPkgSettings = mPmInternal.getSharedUserPackages( - packageState.getSharedUserAppId()); + mStateProvider.runWithState((settings, sharedUserSettings, users) -> + callingSharedPkgSettings.addAll(getSharedUserPackages( + packageState.getSharedUserAppId(), sharedUserSettings))); + } else { callingPkgSetting = packageState; - callingSharedPkgSettings = null; } } else { callingPkgSetting = null; - callingSharedPkgSettings = ((SharedUserSetting) callingSetting).getPackageStates(); + callingSharedPkgSettings.addAll( + ((SharedUserSetting) callingSetting).getPackageStates()); } if (DEBUG_TRACING) { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -1545,11 +1565,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); } - if (mForceQueryable.contains(targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "force queryable"); + synchronized (mLock) { + if (mForceQueryable.contains(targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "force queryable"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1560,11 +1582,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); } - if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries package"); + synchronized (mLock) { + if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries package"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1576,15 +1600,20 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent"); } if (mQueriesViaComponentRequireRecompute) { - mStateProvider.runWithState((settings, users) -> { - recomputeComponentVisibility(settings); + final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>(); + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { + settingsCopy.putAll(settings); }); + recomputeComponentVisibility(settingsCopy); + onChanged(); } - if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); + synchronized (mLock) { + if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries component"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1597,11 +1626,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable"); } final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + synchronized (mLock) { + if (mImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1614,12 +1645,14 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable"); } final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, - "retained implicitly queryable for user"); + synchronized (mLock) { + if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, + "retained implicitly queryable for user"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1632,7 +1665,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); } final String targetName = targetPkg.getPackageName(); - if (callingSharedPkgSettings != null) { + if (!callingSharedPkgSettings.isEmpty()) { int size = callingSharedPkgSettings.size(); for (int index = 0; index < size; index++) { PackageStateInternal pkgSetting = callingSharedPkgSettings.valueAt(index); @@ -1665,11 +1698,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); } - if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queryable for library users"); + synchronized (mLock) { + if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queryable for library users"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1785,23 +1820,25 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable pw.println(" system apps queryable: " + mSystemAppsQueryable); dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(), "forceQueryable", " ", expandPackages); - pw.println(" queries via package name:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); - pw.println(" queries via component:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); - pw.println(" queryable via interaction:"); - for (int user : users) { - pw.append(" User ").append(Integer.toString(user)).println(":"); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mImplicitlyQueryable, " ", expandPackages); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mRetainedImplicitlyQueryable, " ", expandPackages); - } - pw.println(" queryable via uses-library:"); - dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", - expandPackages); + synchronized (mLock) { + pw.println(" queries via package name:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); + pw.println(" queries via component:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); + pw.println(" queryable via interaction:"); + for (int user : users) { + pw.append(" User ").append(Integer.toString(user)).println(":"); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mImplicitlyQueryable, " ", expandPackages); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mRetainedImplicitlyQueryable, " ", expandPackages); + } + pw.println(" queryable via uses-library:"); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", + expandPackages); + } } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, 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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index ee0fdc07f841..cb08c79a7048 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -4317,16 +4317,9 @@ public class UserManagerService extends IUserManager.Stub { private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType, @UserInfoFlag int flags) { - final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, + return logUserJourneyBegin( FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags); - // log the event atom to indicate the event start - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER, - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); - return sessionId; + userId, userType, flags); } private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) { @@ -4336,6 +4329,46 @@ public class UserManagerService extends IUserManager.Stub { : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); } + private long logUserRemoveJourneyBegin(@UserIdInt int userId, String userType, + @UserInfoFlag int flags) { + return logUserJourneyBegin( + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, + userId, userType, flags); + } + + private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER, + finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH + : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); + } + + private long logUserJourneyBegin(int journey, @UserIdInt int userId, String userType, + @UserInfoFlag int flags) { + final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); + // log the journey atom with the user metadata + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, + journey, /* origin_user= */ -1, userId, + UserManager.getUserTypeForStatsd(userType), flags); + + // log the event atom to indicate the event start + int event; + switch (journey) { + case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: + event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; + break; + case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: + event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; + break; + default: + throw new IllegalArgumentException("Journey " + journey + " not expected."); + } + + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, + event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); + return sessionId; + } + /** Register callbacks for statsd pulled atoms. */ private void registerStatsCallbacks() { final StatsManager statsManager = mContext.getSystemService(StatsManager.class); @@ -4578,6 +4611,10 @@ public class UserManagerService extends IUserManager.Stub { userData.info.flags |= UserInfo.FLAG_DISABLED; writeUserLP(userData); } + + final long sessionId = logUserRemoveJourneyBegin( + userId, userData.info.userType, userData.info.flags); + try { mAppOpsService.removeUser(userId); } catch (RemoteException e) { @@ -4600,9 +4637,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public void userStopped(int userIdParam) { finishRemoveUser(userIdParam); + logUserRemoveJourneyFinish(sessionId, userIdParam, true); } @Override public void userStopAborted(int userIdParam) { + logUserRemoveJourneyFinish(sessionId, userIdParam, false); } }); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index c524fb7ae9e5..d11ea532f140 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -297,6 +297,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt .OnRuntimePermissionStateChangedListener> mRuntimePermissionStateChangedListeners = new ArrayList<>(); + private final boolean mIsLeanback; + @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; @@ -380,6 +382,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt mContext = context; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); + mIsLeanback = availableFeatures.containsKey(PackageManager.FEATURE_LEANBACK); mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME); // PackageManager.hasSystemFeature() is not used here because PackageManagerService @@ -2822,6 +2825,14 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } } } + if (mIsLeanback && NOTIFICATION_PERMISSIONS.contains(permName)) { + uidState.grantPermission(bp); + if (origPermState == null || !origPermState.isGranted()) { + if (uidState.grantPermission(bp)) { + wasChanged = true; + } + } + } } else { if (origPermState == null) { // New permission 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/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index f29c40f74353..37f04501bf28 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -341,6 +341,9 @@ public class Vcn extends Handler { if (gatewayConnection == null) { logWtf("Found gatewayConnectionConfig without GatewayConnection"); } else { + logInfo( + "Config updated, restarting gateway " + + gatewayConnection.getLogPrefix()); gatewayConnection.teardownAsynchronously(); } } @@ -397,7 +400,7 @@ public class Vcn extends Handler { // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - logDbg("Request already satisfied by existing VcnGatewayConnection: " + request); + logVdbg("Request already satisfied by existing VcnGatewayConnection: " + request); return; } } @@ -407,8 +410,6 @@ public class Vcn extends Handler { for (VcnGatewayConnectionConfig gatewayConnectionConfig : mConfig.getGatewayConnectionConfigs()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - logDbg("Bringing up new VcnGatewayConnection for request " + request); - if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) { // Skip; this network does not provide any services if mobile data is disabled. continue; @@ -424,6 +425,7 @@ public class Vcn extends Handler { return; } + logInfo("Bringing up new VcnGatewayConnection for request " + request); final VcnGatewayConnection vcnGatewayConnection = mDeps.newVcnGatewayConnection( mVcnContext, @@ -455,7 +457,7 @@ public class Vcn extends Handler { } private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { - logDbg("VcnGatewayConnection quit: " + config); + logInfo("VcnGatewayConnection quit: " + config); mVcnGatewayConnections.remove(config); // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied @@ -534,7 +536,7 @@ public class Vcn extends Handler { // Trigger re-evaluation of all requests; mobile data state impacts supported caps. mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); - logDbg("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); + logInfo("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); } } @@ -569,11 +571,11 @@ public class Vcn extends Handler { } private String getLogPrefix() { - return "[" + return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "-" + System.identityHashCode(this) - + "] "; + + ") "; } private void logVdbg(String msg) { diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index be38005abb63..cefd8efe9658 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -732,14 +732,11 @@ public class VcnGatewayConnection extends StateMachine { logDbg("Triggering async teardown"); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */); - - // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this - // is also called asynchronously when a NetworkAgent becomes unwanted } @Override protected void onQuitting() { - logDbg("Quitting VcnGatewayConnection"); + logInfo("Quitting VcnGatewayConnection"); if (mNetworkAgent != null) { logWtf("NetworkAgent was non-null in onQuitting"); @@ -794,7 +791,7 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); - logDbg( + logInfo( "Selected underlying network changed: " + (underlying == null ? null : underlying.network)); @@ -1335,7 +1332,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) { // TODO(b/180526152): notify VcnStatusCallback for Network loss - logDbg("Tearing down. Cause: " + info.reason); + logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit); if (info.shouldQuit) { mIsQuitting.setTrue(); } @@ -1353,7 +1350,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleSafeModeTimeoutExceeded() { mSafeModeTimeoutAlarm = null; - logDbg("Entering safe mode after timeout exceeded"); + logInfo("Entering safe mode after timeout exceeded"); // Connectivity for this GatewayConnection is broken; tear down the Network. teardownNetwork(); @@ -1362,7 +1359,7 @@ public class VcnGatewayConnection extends StateMachine { } protected void logUnexpectedEvent(int what) { - logDbg( + logVdbg( "Unexpected event code " + what + " in state " @@ -1672,7 +1669,7 @@ public class VcnGatewayConnection extends StateMachine { return; } - logDbg("NetworkAgent was unwanted"); + logInfo("NetworkAgent was unwanted"); teardownAsynchronously(); } /* networkUnwantedCallback */, (status) -> { @@ -1748,7 +1745,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface, IpSecManager.DIRECTION_FWD, transform); } } catch (IOException e) { - logDbg("Transform application failed for network " + token, e); + logInfo("Transform application failed for network " + token, e); sessionLost(token, e); } } @@ -1782,7 +1779,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength()); } } catch (IOException e) { - logDbg("Adding address to tunnel failed for token " + token, e); + logInfo("Adding address to tunnel failed for token " + token, e); sessionLost(token, e); } } @@ -1862,7 +1859,7 @@ public class VcnGatewayConnection extends StateMachine { } private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) { - logDbg("Migration completed: " + mUnderlying.network); + logInfo("Migration completed: " + mUnderlying.network); applyTransform( mCurrentToken, @@ -1890,7 +1887,7 @@ public class VcnGatewayConnection extends StateMachine { mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; if (mUnderlying == null) { - logDbg("Underlying network lost"); + logInfo("Underlying network lost"); // Ignored for now; a new network may be coming up. If none does, the delayed // NETWORK_LOST disconnect will be fired, and tear down the session + network. @@ -1900,7 +1897,7 @@ public class VcnGatewayConnection extends StateMachine { // mUnderlying assumed non-null, given check above. // If network changed, migrate. Otherwise, update any existing networkAgent. if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) { - logDbg("Migrating to new network: " + mUnderlying.network); + logInfo("Migrating to new network: " + mUnderlying.network); mIkeSession.setNetwork(mUnderlying.network); } else { // oldUnderlying is non-null & underlying network itself has not changed @@ -2168,13 +2165,13 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosedExceptionally(@NonNull IkeException exception) { - logDbg("IkeClosedExceptionally for token " + mToken, exception); + logInfo("IkeClosedExceptionally for token " + mToken, exception); sessionClosed(mToken, exception); } @Override public void onError(@NonNull IkeProtocolException exception) { - logDbg("IkeError for token " + mToken, exception); + logInfo("IkeError for token " + mToken, exception); // Non-fatal, log and continue. } } @@ -2208,7 +2205,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosedExceptionally(@NonNull IkeException exception) { - logDbg("ChildClosedExceptionally for token " + mToken, exception); + logInfo("ChildClosedExceptionally for token " + mToken, exception); sessionLost(mToken, exception); } @@ -2234,14 +2231,19 @@ public class VcnGatewayConnection extends StateMachine { } } - private String getLogPrefix() { - return "[" + // Used in Vcn.java, but must be public for mockito to mock this. + public String getLogPrefix() { + return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "-" + mConnectionConfig.getGatewayConnectionName() + "-" + System.identityHashCode(this) - + "] "; + + ") "; + } + + private String getTagLogPrefix() { + return "[ " + TAG + " " + getLogPrefix() + "]"; } private void logVdbg(String msg) { @@ -2258,34 +2260,44 @@ public class VcnGatewayConnection extends StateMachine { Slog.d(TAG, getLogPrefix() + msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); + } + private void logWarn(String msg) { Slog.w(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg); + LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg); } private void logWarn(String msg, Throwable tr) { Slog.w(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg + tr); + LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr); } private void logErr(String msg) { Slog.e(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg); + LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg); } private void logErr(String msg, Throwable tr) { Slog.e(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg + tr); + LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr); } private void logWtf(String msg) { Slog.wtf(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg); + LOCAL_LOG.log("[WTF ] " + msg); } private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg + tr); + LOCAL_LOG.log("[WTF ] " + msg + tr); } /** diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index ca2e449ffc25..a3babf7c9fff 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; +import com.android.server.vcn.util.LogUtils; import java.util.ArrayList; import java.util.Collections; @@ -368,6 +369,18 @@ public class UnderlyingNetworkController { return; } + String allNetworkPriorities = ""; + for (UnderlyingNetworkRecord record : sorted) { + if (!allNetworkPriorities.isEmpty()) { + allNetworkPriorities += ", "; + } + allNetworkPriorities += record.network + ": " + record.getPriorityClass(); + } + logInfo( + "Selected network changed to " + + (candidate == null ? null : candidate.network) + + ", selected from list: " + + allNetworkPriorities); mCurrentRecord = candidate; mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); } @@ -478,14 +491,38 @@ public class UnderlyingNetworkController { } } - private static void logWtf(String msg) { + private String getLogPrefix() { + return "(" + + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + + "-" + + mConnectionConfig.getGatewayConnectionName() + + "-" + + System.identityHashCode(this) + + ") "; + } + + private String getTagLogPrefix() { + return "[ " + TAG + " " + getLogPrefix() + "]"; + } + + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); + } + + private void logWtf(String msg) { Slog.wtf(TAG, msg); - LOCAL_LOG.log(TAG + " WTF: " + msg); + LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg); } - private static void logWtf(String msg, Throwable tr) { + private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, msg, tr); - LOCAL_LOG.log(TAG + " WTF: " + msg + tr); + LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr); } /** Dumps the state of this record for logging and debugging purposes. */ diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index c0488b18cb65..06f92805ad2b 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -41,11 +41,15 @@ import java.util.Objects; * @hide */ public class UnderlyingNetworkRecord { + private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE; + @NonNull public final Network network; @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; public final boolean isBlocked; + private int mPriorityClass = PRIORITY_CLASS_INVALID; + @VisibleForTesting(visibility = Visibility.PRIVATE) public UnderlyingNetworkRecord( @NonNull Network network, @@ -58,6 +62,34 @@ public class UnderlyingNetworkRecord { this.isBlocked = isBlocked; } + private int getOrCalculatePriorityClass( + VcnContext vcnContext, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + // Never changes after the underlying network record is created. + if (mPriorityClass == PRIORITY_CLASS_INVALID) { + mPriorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + vcnContext, + this, + underlyingNetworkTemplates, + subscriptionGroup, + snapshot, + currentlySelected, + carrierConfig); + } + + return mPriorityClass; + } + + // Used in UnderlyingNetworkController + int getPriorityClass() { + return mPriorityClass; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -84,18 +116,16 @@ public class UnderlyingNetworkRecord { PersistableBundle carrierConfig) { return (left, right) -> { final int leftIndex = - NetworkPriorityClassifier.calculatePriorityClass( + left.getOrCalculatePriorityClass( vcnContext, - left, underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, carrierConfig); final int rightIndex = - NetworkPriorityClassifier.calculatePriorityClass( + right.getOrCalculatePriorityClass( vcnContext, - right, underlyingNetworkTemplates, subscriptionGroup, snapshot, @@ -142,16 +172,15 @@ public class UnderlyingNetworkRecord { pw.increaseIndent(); final int priorityIndex = - NetworkPriorityClassifier.calculatePriorityClass( + getOrCalculatePriorityClass( vcnContext, - this, underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, carrierConfig); - pw.println("Priority index:" + priorityIndex); + pw.println("Priority index: " + priorityIndex); pw.println("mNetwork: " + network); pw.println("mNetworkCapabilities: " + networkCapabilities); pw.println("mLinkProperties: " + linkProperties); diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 18e9904142eb..ac635a0746c5 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -238,8 +238,6 @@ final class VibrationSettings { // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity. registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_ON)); - registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); - registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER)); registerSettingsObserver(Settings.System.getUriFor( Settings.System.HAPTIC_FEEDBACK_ENABLED)); registerSettingsObserver( @@ -449,19 +447,12 @@ final class VibrationSettings { mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity); mCurrentVibrationIntensities.put(USAGE_MEDIA, mediaIntensity); mCurrentVibrationIntensities.put(USAGE_UNKNOWN, mediaIntensity); + mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity); // Communication request is not disabled by the notification setting. mCurrentVibrationIntensities.put(USAGE_COMMUNICATION_REQUEST, positiveNotificationIntensity); - if (!loadBooleanSetting(Settings.System.VIBRATE_WHEN_RINGING) - && !loadBooleanSetting(Settings.System.APPLY_RAMPING_RINGER)) { - // Make sure deprecated boolean setting still disables ringtone vibrations. - mCurrentVibrationIntensities.put(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_OFF); - } else { - mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity); - } - // This should adapt the behavior preceding the introduction of this new setting // key, which is to apply HAPTIC_FEEDBACK_INTENSITY, unless it's disabled. mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity); 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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c5251296b91e..dc4e1174edf3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6300,7 +6300,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // starting window is drawn, the transition can start earlier. Exclude finishing and bubble // because it may be a trampoline. if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble - && !mDisplayContent.mAppTransition.isReady() + && mVisibleRequested && !mDisplayContent.mAppTransition.isReady() && !mDisplayContent.mAppTransition.isRunning() && mDisplayContent.isNextTransitionForward()) { // The pending transition state will be cleared after the transition is started, so diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index eb912d4c2747..75d462131aae 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2481,6 +2481,12 @@ class ActivityStarter { if (inTaskFragment == null) { inTaskFragment = TaskFragment.fromTaskFragmentToken( mOptions.getLaunchTaskFragmentToken(), mService); + if (inTaskFragment != null && inTaskFragment.isEmbeddedTaskFragmentInPip()) { + // Do not start activity in TaskFragment in a PIP Task. + Slog.w(TAG, "Can not start activity in TaskFragment in PIP: " + + inTaskFragment); + inTaskFragment = null; + } } } @@ -2638,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 f70dc52bf5e7..0ed671804fe4 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -94,12 +94,22 @@ class BackNavigationController { } int backType = BackNavigationInfo.TYPE_UNDEFINED; + + // The currently visible activity (if any). + ActivityRecord currentActivity = null; + + // The currently visible task (if any). + Task currentTask = null; + + // The previous task we're going back to. Can be the same as currentTask, if there are + // multiple Activities in the Stack. Task prevTask = null; - ActivityRecord prev; + + // The previous activity we're going back to. This can be either a child of currentTask + // if there are more than one Activity in currentTask, or a child of prevTask, if + // currentActivity is the last child of currentTask. + ActivityRecord prevActivity; WindowContainer<?> removedWindowContainer = null; - ActivityRecord activityRecord = null; - ActivityRecord prevTaskTopActivity = null; - Task task = null; SurfaceControl animationLeashParent = null; HardwareBuffer screenshotBuffer = null; RemoteAnimationTarget topAppTarget = null; @@ -143,20 +153,37 @@ class BackNavigationController { } if (window == null) { - // We don't have any focused window, fallback ont the top task of the focused + // We don't have any focused window, fallback ont the top currentTask of the focused // display. ProtoLog.w(WM_DEBUG_BACK_PREVIEW, - "No focused window, defaulting to top task's window"); - task = wmService.mAtmService.getTopDisplayFocusedRootTask(); - window = task.getWindow(WindowState::isFocused); + "No focused window, defaulting to top current task's window"); + currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask(); + window = currentTask.getWindow(WindowState::isFocused); } // Now let's find if this window has a callback from the client side. OnBackInvokedCallbackInfo callbackInfo = null; if (window != null) { - activityRecord = window.mActivityRecord; - task = window.getTask(); + 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; @@ -167,9 +194,9 @@ class BackNavigationController { infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); } - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " + "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - task, activityRecord, callbackInfo, window); + currentTask, currentActivity, callbackInfo, window); if (window == null) { Slog.e(TAG, "Window is null, returning null."); @@ -179,21 +206,19 @@ 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 - || activityRecord == null - || task == null - || task.getDisplayContent().getImeContainer().isVisible() - || activityRecord.isActivityTypeHome()) { + || currentActivity == null + || currentTask == null + || currentActivity.isActivityTypeHome()) { return infoBuilder .setType(backType) .build(); } // We don't have an application callback, let's find the destination of the back gesture - Task finalTask = task; - prev = task.getActivity( + Task finalTask = currentTask; + prevActivity = currentTask.getActivity( (r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity()); if (window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0) != window) { @@ -201,24 +226,24 @@ class BackNavigationController { // activity, we won't close the activity. backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; removedWindowContainer = window; - } else if (prev != null) { - // We have another Activity in the same task to go to + } else if (prevActivity != null) { + // We have another Activity in the same currentTask to go to backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; - removedWindowContainer = activityRecord; - } else if (task.returnsToHomeRootTask()) { + removedWindowContainer = currentActivity; + } else if (currentTask.returnsToHomeRootTask()) { // Our Task should bring back to home - removedWindowContainer = task; + removedWindowContainer = currentTask; backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; - } else if (activityRecord.isRootOfTask()) { + } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer - // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic - prevTask = task.mRootWindowContainer.getTaskBelow(task); - removedWindowContainer = task; + // TODO: Also check Task.shouldUpRecreateTaskLocked() for prevActivity logic + prevTask = currentTask.mRootWindowContainer.getTaskBelow(currentTask); + removedWindowContainer = currentTask; + prevActivity = prevTask.getTopNonFinishingActivity(); if (prevTask.isActivityTypeHome()) { backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; } else { - prev = prevTask.getTopNonFinishingActivity(); backType = BackNavigationInfo.TYPE_CROSS_TASK; } } @@ -229,7 +254,7 @@ class BackNavigationController { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " + "removedContainer:%s, backType=%s", - prev != null ? prev.mActivityComponent : null, + prevActivity != null ? prevActivity.mActivityComponent : null, prevTask != null ? prevTask.getName() : null, removedWindowContainer, BackNavigationInfo.typeToString(backType)); @@ -241,7 +266,8 @@ class BackNavigationController { && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); if (prepareAnimation) { - taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; + taskWindowConfiguration = + currentTask.getTaskInfo().configuration.windowConfiguration; infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration); // Prepare a leash to animate the current top window @@ -254,32 +280,36 @@ class BackNavigationController { removedWindowContainer.reparentSurfaceControl(tx, animLeash); animationLeashParent = removedWindowContainer.getAnimationLeashParent(); topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer, - activityRecord, - task, animLeash); + currentActivity, + currentTask, animLeash); infoBuilder.setDepartingAnimationTarget(topAppTarget); } //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is // implemented. For now we simply have the mBackScreenshots hash map that dumbly // saves the screenshots. - if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) { - screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent); + if (needsScreenshot(backType) && prevActivity != null + && prevActivity.mActivityComponent != null) { + screenshotBuffer = + getActivitySnapshot(currentTask, prevActivity.mActivityComponent); } - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { - task.mBackGestureStarted = true; + // Special handling for back to home animation + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled() + && prevTask != null) { + currentTask.mBackGestureStarted = true; // Make launcher show from behind by marking its top activity as visible and // launch-behind to bump its visibility for the duration of the back gesture. - prevTaskTopActivity = prevTask.getTopNonFinishingActivity(); - if (prevTaskTopActivity != null) { - if (!prevTaskTopActivity.mVisibleRequested) { - prevTaskTopActivity.setVisibility(true); + prevActivity = prevTask.getTopNonFinishingActivity(); + if (prevActivity != null) { + if (!prevActivity.mVisibleRequested) { + prevActivity.setVisibility(true); } - prevTaskTopActivity.mLaunchTaskBehind = true; + prevActivity.mLaunchTaskBehind = true; ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to true. Activity=%s", - prevTaskTopActivity); - prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible( + prevActivity); + prevActivity.mRootWindowContainer.ensureActivitiesVisible( null /* starting */, 0 /* configChanges */, false /* preserveWindows */); } @@ -290,7 +320,7 @@ class BackNavigationController { if (topAppTarget != null && needsScreenshot(backType) && prevTask != null && screenshotBuffer == null) { SurfaceControl.Builder builder = new SurfaceControl.Builder() - .setName("BackPreview Screenshot for " + prev) + .setName("BackPreview Screenshot for " + prevActivity) .setParent(animationLeashParent) .setHidden(false) .setBLASTLayer(); @@ -302,12 +332,12 @@ class BackNavigationController { // The Animation leash needs to be above the screenshot surface, but the animation leash // needs to be added before to be in the synchronized block. tx.setLayer(topAppTarget.leash, 1); - tx.apply(); - + } - WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; + WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; + if (finalRemovedWindowContainer != null) { try { - activityRecord.token.linkToDeath( + currentActivity.token.linkToDeath( () -> resetSurfaces(finalRemovedWindowContainer), 0); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death", e); @@ -315,11 +345,16 @@ class BackNavigationController { return null; } - RemoteCallback onBackNavigationDone = new RemoteCallback( - result -> resetSurfaces(finalRemovedWindowContainer - )); + int finalBackType = backType; + ActivityRecord finalprevActivity = prevActivity; + Task finalTask = currentTask; + RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( + result, finalRemovedWindowContainer, finalBackType, finalTask, + finalprevActivity)); infoBuilder.setOnBackNavigationDone(onBackNavigationDone); } + + tx.apply(); return infoBuilder.build(); } @@ -348,14 +383,13 @@ class BackNavigationController { } private void onBackNavigationDone( - Bundle result, WindowContainer windowContainer, int backType, - Task task, ActivityRecord prevTaskTopActivity) { + Bundle result, WindowContainer<?> windowContainer, int backType, + Task task, ActivityRecord prevActivity) { SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); - boolean triggerBack = result != null - ? result.getBoolean(BackNavigationInfo.KEY_TRIGGER_BACK) - : false; + boolean triggerBack = result != null && result.getBoolean( + BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " - + "task=%s, prevTaskTopActivity=%s", backType, task, prevTaskTopActivity); + + "task=%s, prevActivity=%s", backType, task, prevActivity); if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { if (triggerBack) { @@ -367,13 +401,13 @@ class BackNavigationController { t.apply(); } } - if (prevTaskTopActivity != null && !triggerBack) { + if (prevActivity != null && !triggerBack) { // Restore the launch-behind state. - task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevTaskTopActivity.token); - prevTaskTopActivity.mLaunchTaskBehind = false; + task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token); + prevActivity.mLaunchTaskBehind = false; ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - prevTaskTopActivity); + prevActivity); } } else { task.mBackGestureStarted = false; diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 08a9da467162..dbc08cd5d1a9 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -190,6 +190,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * @see #mFullConfiguration */ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + updateRequestedOverrideConfiguration(overrideConfiguration); + // Update full configuration of this container and all its children. + final ConfigurationContainer parent = getParent(); + onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); + } + + /** Updates override configuration without recalculate full config. */ + void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) { // Pre-compute this here, so we don't need to go through the entire Configuration when // writing to proto (which has significant cost if we write a lot of empty configurations). mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration); @@ -199,9 +207,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) { mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds); } - // Update full configuration of this container and all its children. - final ConfigurationContainer parent = getParent(); - onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); } /** diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 566ed6076526..eaf82b625f71 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1146,8 +1146,13 @@ public class DisplayPolicy { mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowContainer, inOutFrame) -> { if (!mNavButtonForcedVisible) { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets); + final Insets[] providedInternalInsets = win.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets; + if (providedInternalInsets != null + && providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + inOutFrame.inset(providedInternalInsets[ITYPE_NAVIGATION_BAR]); + } inOutFrame.inset(win.mGivenContentInsets); } }, @@ -1193,13 +1198,16 @@ public class DisplayPolicy { if (attrs.providesInsetsTypes != null) { for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider = - !attrs.providedInternalImeInsets.equals(Insets.NONE) - ? (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation) - .providedInternalImeInsets); - } - : null; + (displayFrames, windowContainer, inOutFrame) -> { + final Insets[] providedInternalImeInsets = + win.getLayoutingAttrs(displayFrames.mRotation) + .providedInternalImeInsets; + if (providedInternalImeInsets != null + && providedInternalImeInsets.length > insetsType + && providedInternalImeInsets[insetsType] != null) { + inOutFrame.inset(providedInternalImeInsets[insetsType]); + } + }; switch (insetsType) { case ITYPE_STATUS_BAR: mStatusBarAlt = win; @@ -1220,8 +1228,13 @@ public class DisplayPolicy { } mDisplayContent.setInsetProvider(insetsType, win, (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets); + final Insets[] providedInternalInsets = win.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets; + if (providedInternalInsets != null + && providedInternalInsets.length > insetsType + && providedInternalInsets[insetsType] != null) { + inOutFrame.inset(providedInternalInsets[insetsType]); + } inOutFrame.inset(win.mGivenContentInsets); }, imeFrameProvider); mInsetsSourceWindowsExceptIme.add(win); @@ -1937,15 +1950,23 @@ public class DisplayPolicy { && lp.paramsForRotation[rotation] != null) { lp = lp.paramsForRotation[rotation]; } + final Insets providedInternalInsets; + if (lp.providedInternalInsets != null + && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; + } else { + providedInternalInsets = Insets.NONE; + } if (position == NAV_BAR_LEFT) { - if (lp.width > lp.providedInternalInsets.right) { - return lp.width - lp.providedInternalInsets.right; + if (lp.width > providedInternalInsets.right) { + return lp.width - providedInternalInsets.right; } else { return 0; } } else if (position == NAV_BAR_RIGHT) { - if (lp.width > lp.providedInternalInsets.left) { - return lp.width - lp.providedInternalInsets.left; + if (lp.width > providedInternalInsets.left) { + return lp.width - providedInternalInsets.left; } else { return 0; } @@ -1994,10 +2015,18 @@ public class DisplayPolicy { return 0; } LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation); - if (lp.height < lp.providedInternalInsets.top) { + final Insets providedInternalInsets; + if (lp.providedInternalInsets != null + && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; + } else { + providedInternalInsets = Insets.NONE; + } + if (lp.height < providedInternalInsets.top) { return 0; } - return lp.height - lp.providedInternalInsets.top; + return lp.height - providedInternalInsets.top; } /** 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 cc99f377bfee..6f69e0324b0b 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.content.res.Configuration.EMPTY; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -2005,7 +2006,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // of the activity entering PIP r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); - final boolean singleActivity = task.getChildCount() == 1; + 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; if (singleActivity) { rootTask = task; @@ -2041,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 @@ -2086,6 +2097,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // TODO(task-org): Figure-out more structured way to do this long term. r.setWindowingMode(intermediateWindowingMode); r.mWaitForEnteringPinnedMode = true; + rootTask.forAllTaskFragments(tf -> { + // When the Task is entering picture-in-picture, we should clear all override from + // the client organizer, so the PIP activity can get the correct config from the + // Task, and prevent conflict with the PipTaskOrganizer. + if (tf.isOrganizedTaskFragment()) { + tf.resetAdjacentTaskFragment(); + tf.updateRequestedOverrideConfiguration(EMPTY); + } + }); rootTask.setWindowingMode(WINDOWING_MODE_PINNED); // Set the launch bounds for launch-into-pip Activity on the root task. if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) { @@ -2096,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 a46544d6c902..bd078d880980 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1380,6 +1380,14 @@ class Task extends TaskFragment { return getActivity(ActivityRecord::canBeTopRunning); } + int getActivityCount() { + final int[] activityCount = new int[1]; + forAllActivities(ar -> { + activityCount[0]++; + }); + return activityCount[0]; + } + /** * Return true if any activities in this task belongs to input uid. */ @@ -2035,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(); } @@ -2780,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 b96b461f2da5..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. * @@ -339,7 +345,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } - private void resetAdjacentTaskFragment() { + void resetAdjacentTaskFragment() { // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment. if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) { mAdjacentTaskFragment.mAdjacentTaskFragment = null; @@ -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 @@ -2317,6 +2328,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { mMinHeight = minHeight; } + /** + * Whether this is an embedded TaskFragment in PIP Task. We don't allow any client config + * override for such TaskFragment to prevent flight with PipTaskOrganizer. + */ + boolean isEmbeddedTaskFragmentInPip() { + return isOrganizedTaskFragment() && getTask() != null && getTask().inPinnedWindowingMode(); + } + boolean shouldRemoveSelfOnLastChildRemoval() { return !mCreatedByOrganizer || mIsRemovalRequested; } 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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5969e856a56b..4dbcea1e4751 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2761,7 +2761,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< boolean canStartChangeTransition() { return !mWmService.mDisableTransitionAnimation && mDisplayContent != null && getSurfaceControl() != null && !mDisplayContent.inTransition() - && isVisible() && isVisibleRequested() && okToAnimate(); + && isVisible() && isVisibleRequested() && okToAnimate() + // Pip animation will be handled by PipTaskOrganizer. + && !inPinnedWindowingMode() && getParent() != null + && !getParent().inPinnedWindowingMode(); } /** @@ -3860,7 +3863,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @AnimationType int type, @Nullable AnimationAdapter snapshotAnim); } - void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) { + void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay, + @Nullable WindowState initialWindowState) { if (mOverlayHost == null) { mOverlayHost = new TrustedOverlayHost(mWmService); } @@ -3876,6 +3880,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< "Error sending initial configuration change to WindowContainer overlay"); removeTrustedOverlay(overlay); } + + // Emit an initial WindowState so that proper insets are available to overlay views + // shortly after the overlay is added. + if (initialWindowState != null) { + final InsetsState insetsState = initialWindowState.getInsetsState(); + final Rect dispBounds = getBounds(); + try { + overlay.getRemoteInterface().onInsetsChanged(insetsState, dispBounds); + } catch (Exception e) { + ProtoLog.e(WM_DEBUG_ANIM, + "Error sending initial insets change to WindowContainer overlay"); + removeTrustedOverlay(overlay); + } + } } void removeTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) { 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 77d31df0bee7..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) { @@ -8135,7 +8114,7 @@ public class WindowManagerService extends IWindowManager.Stub if (task == null) { throw new IllegalArgumentException("no task with taskId" + taskId); } - task.addTrustedOverlay(overlay); + task.addTrustedOverlay(overlay, task.getTopVisibleAppMainWindow()); } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index b5cf708eb46d..c1c8b81f2c32 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -677,15 +677,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { final IBinder fragmentToken = hop.getContainer(); - if (!mLaunchTaskFragments.containsKey(fragmentToken)) { + final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); + if (tf == null) { final Throwable exception = new IllegalArgumentException( "Not allowed to operate with invalid fragment token"); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (tf.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to start activity in PIP TaskFragment"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } final Intent activityIntent = hop.getActivityIntent(); final Bundle activityOptions = hop.getLaunchOptions(); - final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); final int result = mService.getActivityStartController() .startActivityInTaskFragment(tf, activityIntent, activityOptions, hop.getCallingActivity(), caller.mUid, caller.mPid); @@ -707,6 +713,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (parent.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to reparent activity to PIP TaskFragment"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } if (!parent.isAllowedToEmbedActivity(activity)) { final Throwable exception = new SecurityException( "The task fragment is not trusted to embed the given activity."); @@ -730,6 +742,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (tf1.isEmbeddedTaskFragmentInPip() + || (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to set adjacent on TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */); effects |= TRANSACT_EFFECTS_LIFECYCLE; @@ -1092,6 +1111,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); } + if (root1.isEmbeddedTaskFragmentInPip() || root2.isEmbeddedTaskFragmentInPip()) { + Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task"); + return 0; + } root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether()); return TRANSACT_EFFECTS_LIFECYCLE; } @@ -1105,6 +1128,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c) { sanitizeWindowContainer(wc); + if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) { + // No override from organizer for embedded TaskFragment in a PIP Task. + return 0; + } int effects = applyChanges(wc, c); @@ -1420,21 +1447,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } // The ownerActivity has to belong to the same app as the target Task. - if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid() - || ownerActivity.getTask().effectiveUid != caller.mUid) { + final Task ownerTask = ownerActivity.getTask(); + if (ownerTask.effectiveUid != ownerActivity.getUid() + || ownerTask.effectiveUid != caller.mUid) { final Throwable exception = new SecurityException("Not allowed to operate with the ownerToken while " + "the root activity of the target task belong to the different app"); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return; } + if (ownerTask.inPinnedWindowingMode()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to create TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return; + } final TaskFragment taskFragment = new TaskFragment(mService, creationParams.getFragmentToken(), true /* createdByOrganizer */); // Set task fragment organizer immediately, since it might have to be notified about further // actions. taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(), ownerActivity.getUid(), ownerActivity.info.processName); - ownerActivity.getTask().addChild(taskFragment, POSITION_TOP); + ownerTask.addChild(taskFragment, POSITION_TOP); taskFragment.setWindowingMode(creationParams.getWindowingMode()); taskFragment.setBounds(creationParams.getInitialBounds()); mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); @@ -1467,6 +1501,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } } + if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new SecurityException( + "Not allow to reparent in TaskFragment in PIP Task."); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return; + } while (oldParent.hasChild()) { oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP); } @@ -1482,6 +1522,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return 0; } + if (taskFragment.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to delete TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return 0; + } mLaunchTaskFragments.removeAt(index); taskFragment.remove(true /* withTransition */, "deleteTaskFragment"); return TRANSACT_EFFECTS_LIFECYCLE; 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/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index e4f1a9645e5c..db1209224bd5 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -28,6 +28,7 @@ import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareC import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.am.UserController.CLEAR_USER_JOURNEY_SESSION_MSG; import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG; import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; @@ -410,6 +411,7 @@ public class UserControllerTest { expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); if (backgroundUserStopping) { + expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG); expectedCodes.add(0); // this is for directly posting in stopping. } Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes(); diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java index d8f4349b95bf..3be2aacc75cb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java @@ -30,7 +30,6 @@ import android.annotation.Nullable; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.UserInfo; @@ -48,7 +47,6 @@ import com.android.server.om.OverlayReferenceMapper; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedActivityImpl; import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; @@ -69,6 +67,7 @@ import org.mockito.stubbing.Answer; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -103,10 +102,9 @@ public class AppsFilterImplTest { AppsFilterImpl.StateProvider mStateProvider; @Mock Executor mMockExecutor; - @Mock - PackageManagerInternal mMockPmInternal; private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>(); + private Collection<SharedUserSetting> mSharedUserSettings = new ArraySet<>(); private static ParsingPackage pkg(String packageName) { return PackageImpl.forTesting(packageName) @@ -205,7 +203,7 @@ public class AppsFilterImplTest { MockitoAnnotations.initMocks(this); doAnswer(invocation -> { ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST); + .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST); return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); @@ -226,7 +224,7 @@ public class AppsFilterImplTest { public void testSystemReadyPropogates() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); appsFilter.onSystemReady(); @@ -238,7 +236,7 @@ public class AppsFilterImplTest { public void testQueriesAction_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -261,7 +259,7 @@ public class AppsFilterImplTest { public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -310,7 +308,7 @@ public class AppsFilterImplTest { public void testQueriesProvider_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -335,7 +333,7 @@ public class AppsFilterImplTest { public void testOnUserUpdated_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -357,7 +355,7 @@ public class AppsFilterImplTest { // adds new user doAnswer(invocation -> { ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST_WITH_ADDED); + .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST_WITH_ADDED); return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); @@ -374,7 +372,7 @@ public class AppsFilterImplTest { // delete user doAnswer(invocation -> { ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST); + .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST); return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); @@ -393,7 +391,7 @@ public class AppsFilterImplTest { public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -418,7 +416,7 @@ public class AppsFilterImplTest { public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -437,7 +435,7 @@ public class AppsFilterImplTest { public void testQueriesAction_NoMatchingAction_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -454,7 +452,7 @@ public class AppsFilterImplTest { public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -475,7 +473,7 @@ public class AppsFilterImplTest { public void testNoQueries_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -492,7 +490,7 @@ public class AppsFilterImplTest { public void testNoUsesLibrary_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -518,7 +516,7 @@ public class AppsFilterImplTest { public void testUsesLibrary_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -545,7 +543,7 @@ public class AppsFilterImplTest { public void testUsesOptionalLibrary_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -572,7 +570,7 @@ public class AppsFilterImplTest { public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -604,7 +602,7 @@ public class AppsFilterImplTest { public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -623,7 +621,7 @@ public class AppsFilterImplTest { public void testForceQueryable_NonSystemFilters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -641,7 +639,7 @@ public class AppsFilterImplTest { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -660,7 +658,7 @@ public class AppsFilterImplTest { public void testSystemSignedTarget_DoesntFilter() throws CertificateException { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); appsFilter.onSystemReady(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -690,7 +688,7 @@ public class AppsFilterImplTest { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -708,8 +706,7 @@ public class AppsFilterImplTest { public void testSystemQueryable_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, - true /* system force queryable */, null, mMockExecutor, - mMockPmInternal); + true /* system force queryable */, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -727,7 +724,7 @@ public class AppsFilterImplTest { public void testQueriesPackage_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -746,7 +743,7 @@ public class AppsFilterImplTest { .thenReturn(false); final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -763,7 +760,7 @@ public class AppsFilterImplTest { public void testSystemUid_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -779,7 +776,7 @@ public class AppsFilterImplTest { public void testSystemUidSecondaryUser_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -796,7 +793,7 @@ public class AppsFilterImplTest { public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -811,7 +808,7 @@ public class AppsFilterImplTest { public void testNoTargetPackage_filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -869,7 +866,7 @@ public class AppsFilterImplTest { return Collections.emptyMap(); } }, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -925,16 +922,11 @@ public class AppsFilterImplTest { .setOverlayTargetOverlayableName("overlayableName"); ParsingPackage actorOne = pkg("com.some.package.actor.one"); ParsingPackage actorTwo = pkg("com.some.package.actor.two"); - ArraySet<PackageStateInternal> actorSharedSettingPackages = new ArraySet<>(); PackageSetting ps1 = getPackageSettingFromParsingPackage(actorOne, DUMMY_ACTOR_APPID, null /*settingBuilder*/); PackageSetting ps2 = getPackageSettingFromParsingPackage(actorTwo, DUMMY_ACTOR_APPID, null /*settingBuilder*/); - actorSharedSettingPackages.add(ps1); - actorSharedSettingPackages.add(ps2); - when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn( - actorSharedSettingPackages - ); + final AppsFilterImpl appsFilter = new AppsFilterImpl( mStateProvider, mFeatureConfigMock, @@ -965,7 +957,7 @@ public class AppsFilterImplTest { return Collections.emptyMap(); } }, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -989,7 +981,7 @@ public class AppsFilterImplTest { public void testInitiatingApp_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1007,7 +999,7 @@ public class AppsFilterImplTest { public void testUninstalledInitiatingApp_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1025,7 +1017,7 @@ public class AppsFilterImplTest { public void testOriginatingApp_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1050,7 +1042,7 @@ public class AppsFilterImplTest { public void testInstallingApp_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1075,7 +1067,7 @@ public class AppsFilterImplTest { public void testInstrumentation_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1104,7 +1096,7 @@ public class AppsFilterImplTest { public void testWhoCanSee() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1177,7 +1169,7 @@ public class AppsFilterImplTest { public void testOnChangeReport() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1250,7 +1242,7 @@ public class AppsFilterImplTest { public void testOnChangeReportedFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter"); @@ -1276,7 +1268,7 @@ public class AppsFilterImplTest { public void testAppsFilterRead() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1379,6 +1371,7 @@ public class AppsFilterImplTest { if (sharedUserSetting != null) { sharedUserSetting.addPackage(setting); setting.setSharedUserAppId(sharedUserSetting.mAppId); + mSharedUserSettings.add(sharedUserSetting); } filter.addPackage(setting); } 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/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index 0c28d8c761ab..0a50e790215f 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -193,7 +193,7 @@ public class VibrationSettingsTest { public void removeListener_noMoreCallbacksToListener() { mVibrationSettings.addListener(mListenerMock); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, 0); verify(mListenerMock).onChange(); mVibrationSettings.removeListener(mListenerMock); @@ -291,8 +291,6 @@ public class VibrationSettingsTest { public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndNotification() { // Vibrating settings on are overruled by ringer mode. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_SILENT); for (int usage : ALL_USAGES) { @@ -360,44 +358,25 @@ public class VibrationSettingsTest { assertVibrationNotIgnoredForUsage(usage); } } + @Test - public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() { + public void shouldIgnoreVibration_withRingSettingsOff_allowsAllVibrations() { + // VIBRATE_WHEN_RINGING is deprecated and should have no effect on the ring vibration + // setting. The ramping ringer is also independent now, instead of a 3-state setting. setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); for (int usage : ALL_USAGES) { - if (usage == USAGE_RINGTONE) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); - } else { - assertVibrationNotIgnoredForUsage(usage); - } + assertVibrationNotIgnoredForUsage(usage); assertVibrationNotIgnoredForUsageAndFlags(usage, VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF); } } @Test - public void shouldIgnoreVibration_withRingSettingsOn_allowsAllVibrations() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); - - for (int usage : ALL_USAGES) { - assertVibrationNotIgnoredForUsage(usage); - } - } - - @Test - public void shouldIgnoreVibration_withRampingRingerOn_allowsAllVibrations() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); - - for (int usage : ALL_USAGES) { - assertVibrationNotIgnoredForUsage(usage); - } - } - - @Test public void shouldIgnoreVibration_withHapticFeedbackDisabled_ignoresTouchVibration() { + // HAPTIC_FEEDBACK_ENABLED is deprecated but it was the only setting used to disable touch + // feedback vibrations. Continue to apply this on top of the intensity setting. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0); for (int usage : ALL_USAGES) { @@ -459,8 +438,6 @@ public class VibrationSettingsTest { @Test public void shouldIgnoreVibration_withRingSettingsOff_ignoresRingtoneVibrations() { // Vibrating settings on are overruled by ring intensity setting. - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_VIBRATE); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); @@ -479,7 +456,6 @@ public class VibrationSettingsTest { public void shouldIgnoreVibration_updateTriggeredAfterInternalRingerModeChanged() { // Vibrating settings on are overruled by ringer mode. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_NORMAL); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 4fbf0065f78d..c735bb7add0a 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -559,30 +559,26 @@ public class VibratorManagerServiceTest { } @Test - public void vibrate_withRingtone_usesRingtoneSettings() throws Exception { + public void vibrate_withRingtone_usesRingerModeSettings() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_SILENT); VibratorManagerService service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); // Wait before checking it never played. assertFalse(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service, /* timeout= */ 50)); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); + setRingerMode(AudioManager.RINGER_MODE_NORMAL); service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_VIBRATE); service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2, @@ -1225,7 +1221,6 @@ public class VibratorManagerServiceTest { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); setRingerMode(AudioManager.RINGER_MODE_NORMAL); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); createSystemReadyService(); IBinder firstToken = mock(IBinder.class); @@ -1296,21 +1291,17 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs, mock(IExternalVibrationController.class)); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_SILENT); createSystemReadyService(); int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); assertEquals(IExternalVibratorService.SCALE_MUTE, scale); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); + setRingerMode(AudioManager.RINGER_MODE_NORMAL); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_VIBRATE); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 1cf96972c225..b987c692bddb 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -170,6 +170,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -290,6 +291,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock ActivityManager mActivityManager; @Mock + TelecomManager mTelecomManager; + @Mock Resources mResources; @Mock RankingHandler mRankingHandler; @@ -494,7 +497,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), - mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class)); + mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), + mTelecomManager); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -9127,6 +9131,54 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCallNotificationsBypassBlock() throws Exception { + when(mAmi.getPendingIntentFlags(any(IIntentSender.class))) + .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + Notification.Builder nb = new Notification.Builder( + mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.setNotificationsEnabledForPackage( + r.getSbn().getPackageName(), r.getUid(), false); + + // normal blocked notifications - blocked + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); + + // just using the style - blocked + Person person = new Person.Builder() + .setName("caller") + .build(); + nb.setStyle(Notification.CallStyle.forOngoingCall( + person, mock(PendingIntent.class))); + nb.setFullScreenIntent(mock(PendingIntent.class), true); + sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); + + // style + managed call - bypasses block + when(mTelecomManager.isInManagedCall()).thenReturn(true); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + + // style + self managed call - bypasses block + when(mTelecomManager.isInSelfManagedCall( + r.getSbn().getPackageName(), r.getUser())).thenReturn(true); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + } + + @Test public void testGetAllUsersNotificationPermissions_migrationNotEnabled() { // make sure we don't bother if the migration is not enabled assertThat(mService.getAllUsersNotificationPermissions()).isNull(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java index 0f6d5a56c667..b751c7fc73ea 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java @@ -97,6 +97,7 @@ import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerFilter; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -379,7 +380,7 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase { mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, - mock(UsageStatsManagerInternal.class)); + mock(UsageStatsManagerInternal.class), mock(TelecomManager.class)); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 1d25b54207c4..0bfd2020622f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -52,6 +52,7 @@ import android.content.pm.PackageManagerInternal; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -167,7 +168,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(StatsManager.class), mock(TelephonyManager.class), mock(ActivityManagerInternal.class), mock(MultiRateLimiter.class), mock(PermissionHelper.class), - mock(UsageStatsManagerInternal.class)); + mock(UsageStatsManagerInternal.class), mock (TelecomManager.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; 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 fe59185a6893..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; @@ -29,9 +31,12 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -39,6 +44,7 @@ import android.annotation.Nullable; import android.hardware.HardwareBuffer; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; import android.view.WindowManager; import android.window.BackEvent; import android.window.BackNavigationInfo; @@ -77,22 +83,29 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backNavInfo_HomeWhenBackToLauncher() { - IOnBackInvokedCallback callback = withSystemCallback(createTopTaskWithActivity()); + IOnBackInvokedCallback callback = + withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_SYSTEM); - BackNavigationInfo backNavigationInfo = startBackNavigation(); + SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class); + BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(mWm, + tx); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); + + verify(tx, atLeastOnce()).apply(); + verify(tx, times(1)).reparent(any(), + eq(backNavigationInfo.getDepartingAnimationTarget().leash)); } @Test 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())) @@ -144,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())) @@ -154,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())) @@ -215,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/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 762c08f99d94..8ef9ada8995f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -300,6 +300,81 @@ public class RootWindowContainerTests extends WindowTestsBase { assertTrue(firstActivity.mRequestForceTransition); } + /** + * When there is only one activity in the Task, and the activity is requesting to enter PIP, the + * whole Task should enter PIP. + */ + @Test + public void testSingleActivityTaskEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask) + .build(); + final Task task = activity.getTask(); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure a task has moved over. + ensureTaskPlacement(task, activity); + assertTrue(task.inPinnedWindowingMode()); + } + + /** + * When there is only one activity in the Task, and the activity is requesting to enter PIP, the + * whole Task should enter PIP even if the activity is in a TaskFragment. + */ + @Test + public void testSingleActivityInTaskFragmentEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(fullscreenTask) + .createActivityCount(1) + .build(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + final Task task = activity.getTask(); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure a task has moved over. + ensureTaskPlacement(task, activity); + assertTrue(task.inPinnedWindowingMode()); + } + + /** + * When there is one TaskFragment with two activities in the Task, the activity requests to + * enter PIP, that activity will be move to PIP root task. + */ + @Test + public void testMultipleActivitiesInTaskFragmentEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(fullscreenTask) + .createActivityCount(2) + .build(); + final ActivityRecord firstActivity = taskFragment.getTopMostActivity(); + final ActivityRecord secondActivity = taskFragment.getBottomMostActivity(); + + // Move first activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, + null /* launchIntoPipHostActivity */, "test"); + + final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); + final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); + + // Ensure a task has moved over. + ensureTaskPlacement(pinnedRootTask, firstActivity); + ensureTaskPlacement(fullscreenTask, secondActivity); + assertTrue(pinnedRootTask.inPinnedWindowingMode()); + assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode()); + } + private static void ensureTaskPlacement(Task task, ActivityRecord... activities) { final ArrayList<ActivityRecord> taskActivities = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 37719fecd7e4..d135de0fc921 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -28,6 +31,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -435,6 +440,107 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testTaskFragmentInPip_startActivityInTaskFragment() { + setupTaskFragmentInPip(); + final ActivityRecord activity = mTaskFragment.getTopMostActivity(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.getActivityStartController()); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to start activity in a TaskFragment that is in a PIP Task. + mTransaction.startActivityInTaskFragment( + mFragmentToken, activity.token, new Intent(), null /* activityOptions */) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(), + any(), any(), anyInt(), anyInt()); + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + } + + @Test + public void testTaskFragmentInPip_reparentActivityToTaskFragment() { + setupTaskFragmentInPip(); + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to reparent activity to a TaskFragment that is in a PIP Task. + mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNull(activity.getOrganizedTaskFragment()); + } + + @Test + public void testTaskFragmentInPip_setAdjacentTaskFragment() { + setupTaskFragmentInPip(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to set adjacent on a TaskFragment that is in a PIP Task. + mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */, + null /* options */) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean()); + } + + @Test + public void testTaskFragmentInPip_createTaskFragment() { + mController.registerOrganizer(mIOrganizer); + final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD); + final ActivityRecord activity = createActivityRecord(pipTask); + final IBinder fragmentToken = new Binder(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to create TaskFragment in a PIP Task. + createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken); + mTransaction.setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + } + + @Test + public void testTaskFragmentInPip_deleteTaskFragment() { + setupTaskFragmentInPip(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to delete a TaskFragment that is in a PIP Task. + mTransaction.deleteTaskFragment(mFragmentWindowToken) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken)); + } + + @Test + public void testTaskFragmentInPip_setConfig() { + setupTaskFragmentInPip(); + spyOn(mAtm.mWindowOrganizerController); + + // Set bounds is ignored on a TaskFragment that is in a PIP Task. + mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); + + verify(mTaskFragment, never()).setBounds(any()); + } + + @Test public void testDeferPendingTaskFragmentEventsOfInvisibleTask() { // Task - TaskFragment - Activity. final Task task = createTask(mDisplayContent); @@ -643,4 +749,20 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { fail(); } } + + /** Setups an embedded TaskFragment in a PIP Task. */ + private void setupTaskFragmentInPip() { + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .setFragmentToken(mFragmentToken) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); + mAtm.mWindowOrganizerController.mLaunchTaskFragments + .put(mFragmentToken, mTaskFragment); + mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED); + } } 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 2101b6e767c8..54fa4e4bf7ec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.clearInvocations; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; @@ -207,6 +209,71 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() { + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final Task task = taskFragment.getTask(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + taskFragment.setBounds(taskFragmentBounds); + + assertEquals(taskFragmentBounds, activity.getBounds()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure taskFragment requested config is reset. + assertEquals(taskFragment, activity.getOrganizedTaskFragment()); + assertEquals(task, activity.getTask()); + assertTrue(task.inPinnedWindowingMode()); + assertTrue(taskFragment.inPinnedWindowingMode()); + final Rect taskBounds = task.getBounds(); + assertEquals(taskBounds, taskFragment.getBounds()); + assertEquals(taskBounds, activity.getBounds()); + assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration()); + } + + @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/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index b2cc104834e6..1b07e9a69446 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -237,7 +237,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser @Override public void onUEvent(UEventObserver.UEvent event) { if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); - sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + event.toString())); + if (sEventLogger != null) { + sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + + event.toString())); + } else { + if (DEBUG) Slog.d(TAG, "sEventLogger == null"); + } String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java index 600fc27f5103..94273a37abcd 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java @@ -725,5 +725,15 @@ public final class UsbPortAidl implements UsbPortHal { e); } } + + @Override + public String getInterfaceHash() { + return IUsbCallback.HASH; + } + + @Override + public int getInterfaceVersion() { + return IUsbCallback.VERSION; + } } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 8d4a0176e3be..5b6e6863c0df 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -39,10 +39,13 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENT import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK; import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight; import android.annotation.NonNull; @@ -133,6 +136,13 @@ final class HotwordDetectionConnection { private static final int METRICS_INIT_CALLBACK_STATE_SUCCESS = HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS; + private static final int METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION = + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION; + private static final int METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK = + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK; + private static final int METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK = + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK; + private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); // TODO: This may need to be a Handler(looper) private final ScheduledExecutorService mScheduledExecutorService = @@ -575,7 +585,7 @@ final class HotwordDetectionConnection { Slog.i(TAG, "Ignoring #onDetected due to a process restart"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK); return; } mValidatingDspTrigger = false; @@ -584,7 +594,7 @@ final class HotwordDetectionConnection { } catch (SecurityException e) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION); throw e; } externalCallback.onKeyphraseDetected(recognitionEvent, result); @@ -614,7 +624,7 @@ final class HotwordDetectionConnection { Slog.i(TAG, "Ignoring #onRejected due to a process restart"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK); return; } mValidatingDspTrigger = false; @@ -687,6 +697,9 @@ final class HotwordDetectionConnection { // rejection. This also allows the Interactor to startReco again try { mCallback.onRejected(new HotwordRejectedResult.Builder().build()); + HotwordMetricsLogger.writeKeyphraseTriggerEvent( + mDetectorType, + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART); } catch (RemoteException e) { Slog.w(TAG, "Failed to call #rejected"); } 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, }; diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py index 297d9c3bcca0..d53c12734d23 100644..100755 --- a/tools/apilint/deprecated_at_birth.py +++ b/tools/apilint/deprecated_at_birth.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (C) 2021 The Android Open Source Project # @@ -44,6 +44,7 @@ def ident(raw): can be used to identify members across API levels.""" raw = raw.replace(" deprecated ", " ") raw = raw.replace(" synchronized ", " ") + raw = raw.replace(" abstract ", " ") raw = raw.replace(" final ", " ") raw = re.sub("<.+?>", "", raw) raw = re.sub("@[A-Za-z]+ ", "", raw) @@ -208,17 +209,17 @@ def _parse_stream(f, api={}): def _parse_stream_path(path): api = {} - print "Parsing", path + print("Parsing %s" % path) for f in os.listdir(path): f = os.path.join(path, f) if not os.path.isfile(f): continue if not f.endswith(".txt"): continue if f.endswith("removed.txt"): continue - print "\t", f + print("\t%s" % f) with open(f) as s: api = _parse_stream(s, api) - print "Parsed", len(api), "APIs" - print + print("Parsed %d APIs" % len(api)) + print() return api @@ -306,8 +307,8 @@ if __name__ == "__main__": if "@Deprecated " in i.raw: error(clazz, i, None, "Found API deprecation at birth " + i.ident) - print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), - format(reset=True))) + print("%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), + format(reset=True)))) for f in sorted(failures): - print failures[f] - print + print(failures[f]) + print() |