summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java36
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java59
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java257
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java85
-rw-r--r--core/api/current.txt38
-rw-r--r--core/api/system-current.txt9
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java8
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java161
-rw-r--r--core/java/android/content/Context.java8
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java5
-rw-r--r--core/java/android/os/SystemProperties.java5
-rw-r--r--core/java/android/os/storage/StorageManager.java32
-rw-r--r--core/java/android/service/autofill/FillRequest.java2
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java14
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java8
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java35
-rw-r--r--core/java/android/speech/RecognitionSupport.java330
-rw-r--r--core/java/android/view/Choreographer.java42
-rw-r--r--core/java/android/view/Display.java123
-rw-r--r--core/java/android/view/DisplayEventReceiver.java8
-rw-r--r--core/java/android/view/IWindowSession.aidl35
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--core/java/android/view/ViewRootImpl.java88
-rw-r--r--core/java/android/view/WindowlessWindowManager.java15
-rw-r--r--core/java/android/view/autofill/AutofillManager.java92
-rw-r--r--core/java/android/view/autofill/AutofillRequestCallback.java72
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl8
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java118
-rw-r--r--core/java/android/widget/TextView.java40
-rw-r--r--core/java/android/window/TransitionInfo.java2
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java19
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java1
-rw-r--r--core/java/com/android/internal/widget/CachingIconView.java110
-rw-r--r--core/java/com/android/internal/widget/LocalImageResolver.java202
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java4
-rw-r--r--core/java/com/android/internal/widget/LockscreenCredential.java21
-rw-r--r--core/jni/android_util_Binder.cpp3
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp88
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/layout/notification_template_header.xml2
-rw-r--r--core/res/res/layout/notification_template_material_base.xml2
-rw-r--r--core/res/res/values/attrs.xml8
-rw-r--r--core/res/res/values/public-staging.xml4
-rw-r--r--core/tests/coretests/res/drawable/big_a.pngbin0 -> 11905 bytes
-rw-r--r--core/tests/coretests/res/layout/caching_icon_view_test_max_size.xml24
-rw-r--r--core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml22
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java259
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java212
-rw-r--r--data/etc/services.core.protolog.json183
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java22
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java12
-rw-r--r--media/java/android/media/AudioDeviceVolumeManager.java74
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl6
-rw-r--r--media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl31
-rw-r--r--media/java/android/media/tv/StreamEventResponse.java16
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java21
-rw-r--r--packages/CompanionDeviceManager/res/drawable/helper_back_button.xml (renamed from packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml)0
-rw-r--r--packages/CompanionDeviceManager/res/drawable/ic_info.xml26
-rw-r--r--packages/CompanionDeviceManager/res/layout/helper_confirmation.xml6
-rw-r--r--packages/CompanionDeviceManager/res/layout/vendor_header.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml7
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java16
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java31
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java12
-rw-r--r--packages/ConnectivityT/OWNERS7
-rw-r--r--packages/SettingsLib/SettingsSpinner/Android.bp1
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml20
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml11
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml2
-rw-r--r--packages/SettingsLib/res/values/strings.xml18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java50
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java71
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java127
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java19
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt (renamed from packages/SystemUI/animation/src/com/android/systemui/animation/ViewBoundAnimator.kt)2
-rw-r--r--packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/layout/combined_qs_header.xml10
-rw-r--r--packages/SystemUI/res/layout/large_screen_shade_header.xml (renamed from packages/SystemUI/res/layout/split_shade_header.xml)8
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res/values-sw720dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml24
-rw-r--r--packages/SystemUI/res/xml/combined_qs_header_scene.xml8
-rw-r--r--packages/SystemUI/res/xml/large_screen_shade_header.xml (renamed from packages/SystemUI/res/xml/split_header.xml)4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt)90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/animation/ViewBoundAnimatorTest.kt)28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt)16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java6
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java15
-rw-r--r--services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java293
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java188
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java17
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java126
-rw-r--r--services/core/java/com/android/server/pm/Installer.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java9
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java23
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java8
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java68
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java25
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java33
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java22
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java6
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java46
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java11
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java28
-rw-r--r--services/core/java/com/android/server/wm/Session.java25
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java21
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java37
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java36
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java30
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java18
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java382
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java167
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java56
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java62
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java192
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java4
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java5
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java38
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java2
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java10
-rw-r--r--telephony/java/android/telephony/ims/feature/ImsFeature.java45
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java96
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java152
-rw-r--r--tests/InputMethodStressTest/AndroidTest.xml4
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java52
-rw-r--r--tests/TrustTests/src/android/trust/BaseTrustAgentService.kt2
-rw-r--r--tests/TrustTests/src/android/trust/test/LockUserTest.kt1
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt32
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt42
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt13
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/utils.kt51
-rw-r--r--tools/locked_region_code_injection/Android.bp8
-rw-r--r--tools/sdkparcelables/Android.bp2
-rw-r--r--tools/traceinjection/Android.bp8
226 files changed, 4858 insertions, 3042 deletions
diff --git a/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java b/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java
index 3b4f72b2e50a..e20f30d1810b 100644
--- a/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java
+++ b/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java
@@ -17,10 +17,12 @@
package android.content.pm;
import android.Manifest;
+import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.util.Log;
@@ -39,7 +41,10 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -203,13 +208,33 @@ public class PackageInstallerBenchmark {
}
private void uninstallSession(BenchmarkState state, String...packageNames)
- throws InterruptedException {
+ throws Exception {
state.pauseTiming();
uninstall(true /* stop at fail */, packageNames);
mPackageInstaller.unregisterSessionCallback(mSessionCallback);
+ executeShellCommand("pm gc");
state.resumeTiming();
}
+ private static String executeShellCommand(String command) throws IOException {
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ final ParcelFileDescriptor stdout = uiAutomation.executeShellCommand(command);
+ try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout);
+ ByteArrayOutputStream result = new ByteArrayOutputStream()) {
+ writeFullStream(inputStream, result);
+ return result.toString("UTF-8");
+ }
+ }
+
+ private static void writeFullStream(InputStream inputStream, OutputStream outputStream)
+ throws IOException {
+ final byte[] buffer = new byte[1024];
+ int length;
+ while ((length = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, length);
+ }
+ }
+
@Test(timeout = 600_000L)
public void commit_aSingleApkSession_untilFinishBenchmark() throws Exception {
uninstall(false /* stop at fail */, TestApp.A);
@@ -247,8 +272,7 @@ public class PackageInstallerBenchmark {
}
@Test(timeout = 600_000L)
- public void commit_aMultiplePackagesSession_untilFinishBenchmark()
- throws IOException, InterruptedException {
+ public void commit_aMultiplePackagesSession_untilFinishBenchmark() throws Exception {
uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -269,8 +293,7 @@ public class PackageInstallerBenchmark {
}
@Test(timeout = 600_000L)
- public void commit_threeMultiplePackageSessions_untilFinishBenchmark()
- throws Exception {
+ public void commit_threeMultiplePackageSessions_untilFinishBenchmark() throws Exception {
uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -293,8 +316,7 @@ public class PackageInstallerBenchmark {
}
@Test(timeout = 600_000L)
- public void commit_aMultipleApksSession_untilFinishBenchmark()
- throws IOException, InterruptedException {
+ public void commit_aMultipleApksSession_untilFinishBenchmark() throws Exception {
uninstall(false /* stop at fail */, TestApp.A);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 88082f7dfa4b..dd0d07f68547 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -284,9 +284,9 @@ public class EconomyManager {
/** @hide */
public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440;
/** @hide */
- public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 28800;
+ public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 4000;
/** @hide */
- public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 52000;
+ public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 28_800;
// TODO: add AlarmManager modifier default values
/** @hide */
public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
@@ -373,9 +373,9 @@ public class EconomyManager {
/** @hide */
public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000;
/** @hide */
- public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 460_000;
+ public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 100_000;
/** @hide */
- public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 900_000;
+ public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 460_000;
// TODO: add JobScheduler modifier default values
/** @hide */
public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 23056b5670b9..405b12d6d13a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -68,7 +68,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -578,7 +577,7 @@ class JobConcurrencyManager {
Slog.d(TAG, printPendingQueueLocked());
}
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue;
final List<JobServiceContext> activeServices = mActiveServices;
// To avoid GC churn, we recycle the arrays.
@@ -597,7 +596,7 @@ class JobConcurrencyManager {
// Update the priorities of jobs that aren't running, and also count the pending work types.
// Do this before the following loop to hopefully reduce the cost of
// shouldStopRunningJobLocked().
- updateNonRunningPrioritiesLocked(pendingJobs, true);
+ updateNonRunningPrioritiesLocked(pendingJobQueue, true);
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = activeServices.get(i);
@@ -620,9 +619,9 @@ class JobConcurrencyManager {
mWorkCountTracker.onCountDone();
- for (int i = 0; i < pendingJobs.size(); i++) {
- final JobStatus nextPending = pendingJobs.get(i);
-
+ JobStatus nextPending;
+ pendingJobQueue.resetIterator();
+ while ((nextPending = pendingJobQueue.next()) != null) {
if (mRunningJobs.contains(nextPending)) {
continue;
}
@@ -841,10 +840,11 @@ class JobConcurrencyManager {
}
@GuardedBy("mLock")
- private void updateNonRunningPrioritiesLocked(@NonNull final List<JobStatus> pendingJobs,
+ private void updateNonRunningPrioritiesLocked(@NonNull final PendingJobQueue jobQueue,
boolean updateCounter) {
- for (int i = 0; i < pendingJobs.size(); i++) {
- final JobStatus pending = pendingJobs.get(i);
+ JobStatus pending;
+ jobQueue.resetIterator();
+ while ((pending = jobQueue.next()) != null) {
// If job is already running, go to next job.
if (mRunningJobs.contains(pending)) {
@@ -882,7 +882,8 @@ class JobConcurrencyManager {
}
// Use < instead of <= as that gives us a little wiggle room in case a new job comes
// along very shortly.
- if (mService.mPendingJobs.size() + mRunningJobs.size() < mWorkTypeConfig.getMaxTotal()) {
+ if (mService.mPendingJobQueue.size() + mRunningJobs.size()
+ < mWorkTypeConfig.getMaxTotal()) {
// Don't artificially limit a single package if we don't even have enough jobs to use
// the maximum number of slots. We'll preempt the job later if we need the slot.
return false;
@@ -936,8 +937,7 @@ class JobConcurrencyManager {
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
packageStats);
}
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
- if (pendingJobs.remove(jobStatus)) {
+ if (mService.mPendingJobQueue.remove(jobStatus)) {
mService.mJobPackageTracker.noteNonpending(jobStatus);
}
} finally {
@@ -962,11 +962,11 @@ class JobConcurrencyManager {
}
}
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue;
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
updateCounterConfigLocked();
// Preemption case needs special care.
- updateNonRunningPrioritiesLocked(pendingJobs, false);
+ updateNonRunningPrioritiesLocked(pendingJobQueue, false);
JobStatus highestBiasJob = null;
int highBiasWorkType = workType;
@@ -974,9 +974,10 @@ class JobConcurrencyManager {
JobStatus backupJob = null;
int backupWorkType = WORK_TYPE_NONE;
int backupAllWorkTypes = WORK_TYPE_NONE;
- for (int i = 0; i < pendingJobs.size(); i++) {
- final JobStatus nextPending = pendingJobs.get(i);
+ JobStatus nextPending;
+ pendingJobQueue.resetIterator();
+ while ((nextPending = pendingJobQueue.next()) != null) {
if (mRunningJobs.contains(nextPending)) {
continue;
}
@@ -1041,16 +1042,18 @@ class JobConcurrencyManager {
startJobLocked(worker, backupJob, backupWorkType);
}
}
- } else if (pendingJobs.size() > 0) {
+ } else if (pendingJobQueue.size() > 0) {
updateCounterConfigLocked();
- updateNonRunningPrioritiesLocked(pendingJobs, false);
+ updateNonRunningPrioritiesLocked(pendingJobQueue, false);
// This slot is now free and we have pending jobs. Start the highest bias job we find.
JobStatus highestBiasJob = null;
int highBiasWorkType = workType;
int highBiasAllWorkTypes = workType;
- for (int i = 0; i < pendingJobs.size(); i++) {
- final JobStatus nextPending = pendingJobs.get(i);
+
+ JobStatus nextPending;
+ pendingJobQueue.resetIterator();
+ while ((nextPending = pendingJobQueue.next()) != null) {
if (mRunningJobs.contains(nextPending)) {
continue;
@@ -1127,8 +1130,8 @@ class JobConcurrencyManager {
return "too many jobs running";
}
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
- final int numPending = pendingJobs.size();
+ final PendingJobQueue pendingJobQueue = mService.mPendingJobQueue;
+ final int numPending = pendingJobQueue.size();
if (numPending == 0) {
// All quiet. We can let this job run to completion.
return null;
@@ -1163,8 +1166,9 @@ class JobConcurrencyManager {
// Harder check. We need to see if a different work type can replace this job.
int remainingWorkTypes = ALL_WORK_TYPES;
- for (int i = 0; i < numPending; ++i) {
- final JobStatus pending = pendingJobs.get(i);
+ JobStatus pending;
+ pendingJobQueue.resetIterator();
+ while ((pending = pendingJobQueue.next()) != null) {
final int workTypes = getJobWorkTypes(pending);
if ((workTypes & remainingWorkTypes) > 0
&& mWorkCountTracker.canJobStart(workTypes, workType) != WORK_TYPE_NONE) {
@@ -1201,9 +1205,10 @@ class JobConcurrencyManager {
@GuardedBy("mLock")
private String printPendingQueueLocked() {
StringBuilder s = new StringBuilder("Pending queue: ");
- Iterator<JobStatus> it = mService.mPendingJobs.iterator();
- while (it.hasNext()) {
- JobStatus js = it.next();
+ PendingJobQueue pendingJobQueue = mService.mPendingJobQueue;
+ JobStatus js;
+ pendingJobQueue.resetIterator();
+ while ((js = pendingJobQueue.next()) != null) {
s.append("(")
.append(js.getJob().getId())
.append(", ")
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 3d74bc98ad32..2028be738238 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -81,7 +80,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import android.util.SparseLongArray;
import android.util.SparseSetArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -288,7 +286,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
* when ready to execute them.
*/
- final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
+ final PendingJobQueue mPendingJobQueue = new PendingJobQueue();
int[] mStartedUsers = EmptyArray.INT;
@@ -828,189 +826,6 @@ public class JobSchedulerService extends com.android.server.SystemService
final Constants mConstants;
final ConstantsObserver mConstantsObserver;
- @VisibleForTesting
- class PendingJobComparator implements Comparator<JobStatus> {
- private static final int EJ_PRIORITY_MODIFIER = 10;
-
- /** Cache of the earliest non-PRIORITY_MAX enqueue time found per UID. */
- private final SparseLongArray mEarliestNonMaxEnqueueTimeCache = new SparseLongArray();
- /**
- * Cache of the last enqueue time of each priority for each UID. The SparseArray is keyed
- * by UID and the SparseLongArray is keyed by the priority.
- */
- private final SparseArray<SparseLongArray> mLastPriorityEnqueueTimeCache =
- new SparseArray<>();
- /**
- * The earliest enqueue time each UID's priority's jobs should use. The SparseArray is keyed
- * by UID and the SparseLongArray is keyed by the value returned from
- * {@link #getPriorityIndex(int, boolean)}.
- */
- private final SparseArray<SparseLongArray> mEarliestAllowedEnqueueTimes =
- new SparseArray<>();
-
- private int getPriorityIndex(int priority, boolean isEJ) {
- // We need to separate HIGH priority EJs from HIGH priority regular jobs.
- if (isEJ) {
- return priority * EJ_PRIORITY_MODIFIER;
- }
- return priority;
- }
-
- /**
- * Refresh sorting determinants based on the current state of {@link #mPendingJobs}.
- */
- @GuardedBy("mLock")
- @VisibleForTesting
- void refreshLocked() {
- mEarliestNonMaxEnqueueTimeCache.clear();
- for (int i = 0; i < mPendingJobs.size(); ++i) {
- final JobStatus job = mPendingJobs.get(i);
- final int uid = job.getSourceUid();
- if (job.getEffectivePriority() < JobInfo.PRIORITY_MAX) {
- final long earliestEnqueueTime =
- mEarliestNonMaxEnqueueTimeCache.get(uid, Long.MAX_VALUE);
- mEarliestNonMaxEnqueueTimeCache.put(uid,
- Math.min(earliestEnqueueTime, job.enqueueTime));
- }
-
- final int pIdx =
- getPriorityIndex(job.getEffectivePriority(), job.isRequestedExpeditedJob());
- SparseLongArray lastPriorityEnqueueTime = mLastPriorityEnqueueTimeCache.get(uid);
- if (lastPriorityEnqueueTime == null) {
- lastPriorityEnqueueTime = new SparseLongArray();
- mLastPriorityEnqueueTimeCache.put(uid, lastPriorityEnqueueTime);
- }
- lastPriorityEnqueueTime.put(pIdx,
- Math.max(job.enqueueTime, lastPriorityEnqueueTime.get(pIdx, 0)));
- }
-
- // Move lower priority jobs behind higher priority jobs (instead of moving higher
- // priority jobs ahead of lower priority jobs), except for EJs.
- for (int i = 0; i < mLastPriorityEnqueueTimeCache.size(); ++i) {
- final int uid = mLastPriorityEnqueueTimeCache.keyAt(i);
- SparseLongArray lastEnqueueTimes = mLastPriorityEnqueueTimeCache.valueAt(i);
- SparseLongArray earliestAllowedEnqueueTimes = new SparseLongArray();
- mEarliestAllowedEnqueueTimes.put(uid, earliestAllowedEnqueueTimes);
- long earliestAllowedEnqueueTime = mEarliestNonMaxEnqueueTimeCache.get(uid,
- lastEnqueueTimes.get(getPriorityIndex(JobInfo.PRIORITY_MAX, true), -1));
- earliestAllowedEnqueueTimes.put(getPriorityIndex(JobInfo.PRIORITY_MAX, true),
- earliestAllowedEnqueueTime);
- earliestAllowedEnqueueTime = 1
- + Math.max(earliestAllowedEnqueueTime,
- lastEnqueueTimes.get(getPriorityIndex(JobInfo.PRIORITY_HIGH, true), -1));
- earliestAllowedEnqueueTimes.put(getPriorityIndex(JobInfo.PRIORITY_HIGH, true),
- earliestAllowedEnqueueTime);
- earliestAllowedEnqueueTime++;
- for (int p = JobInfo.PRIORITY_HIGH; p >= JobInfo.PRIORITY_MIN; --p) {
- final int pIdx = getPriorityIndex(p, false);
- earliestAllowedEnqueueTimes.put(pIdx, earliestAllowedEnqueueTime);
- final long lastEnqueueTime = lastEnqueueTimes.get(pIdx, -1);
- if (lastEnqueueTime != -1) {
- // Add additional millisecond for the next priority to ensure sorting is
- // stable/accurate when comparing to other apps.
- earliestAllowedEnqueueTime = 1
- + Math.max(earliestAllowedEnqueueTime, lastEnqueueTime);
- }
- }
- }
-
- // Clear intermediate state that we don't need to reduce steady state memory usage.
- mLastPriorityEnqueueTimeCache.clear();
- }
-
- @ElapsedRealtimeLong
- private long getEffectiveEnqueueTime(@NonNull JobStatus job) {
- // Move lower priority jobs behind higher priority jobs (instead of moving higher
- // priority jobs ahead of lower priority jobs), except for MAX EJs.
- final int uid = job.getSourceUid();
- if (job.isRequestedExpeditedJob()
- && job.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
- return Math.min(job.enqueueTime,
- mEarliestNonMaxEnqueueTimeCache.get(uid, Long.MAX_VALUE));
- }
- final int priorityIdx =
- getPriorityIndex(job.getEffectivePriority(), job.isRequestedExpeditedJob());
- final SparseLongArray earliestAllowedEnqueueTimes =
- mEarliestAllowedEnqueueTimes.get(uid);
- if (earliestAllowedEnqueueTimes == null) {
- // We're probably trying to insert directly without refreshing the internal arrays.
- // Since we haven't seen this UID before, we can just use the job's enqueue time.
- return job.enqueueTime;
- }
- return Math.max(job.enqueueTime, earliestAllowedEnqueueTimes.get(priorityIdx));
- }
-
- @Override
- public int compare(JobStatus o1, JobStatus o2) {
- if (o1 == o2) {
- return 0;
- }
- // Jobs with an override state set (via adb) should be put first as tests/developers
- // expect the jobs to run immediately.
- if (o1.overrideState != o2.overrideState) {
- // Higher override state (OVERRIDE_FULL) should be before lower state
- // (OVERRIDE_SOFT)
- return o2.overrideState - o1.overrideState;
- }
- final boolean o1EJ = o1.isRequestedExpeditedJob();
- final boolean o2EJ = o2.isRequestedExpeditedJob();
- if (o1.getSourceUid() == o2.getSourceUid()) {
- if (o1EJ != o2EJ) {
- // Attempt to run requested expedited jobs ahead of regular jobs, regardless of
- // expedited job quota.
- return o1EJ ? -1 : 1;
- }
- if (o1.getEffectivePriority() != o2.getEffectivePriority()) {
- // Use the priority set by an app for intra-app job ordering. Higher
- // priority should be before lower priority.
- return o2.getEffectivePriority() - o1.getEffectivePriority();
- }
- } else {
- // TODO: see if we can simplify this using explicit topological sorting
- // Since we order jobs within a UID by the job's priority, in order to satisfy the
- // transitivity constraint of the comparator, we must ensure consistent/appropriate
- // ordering between apps as well. That is, if a job is ordered before or behind
- // another job because of its priority, that ordering must translate to the
- // relative ordering against other jobs.
- // The effective ordering implementation here is to use HIGH priority EJs as a
- // pivot point. MAX priority EJs are moved *ahead* of HIGH priority EJs. All
- // regular jobs are moved *behind* HIGH priority EJs. The intention for moving jobs
- // "behind" the EJs instead of moving all high priority jobs before lower priority
- // jobs is to reduce any potential abuse (or just unfortunate execution) cases where
- // there are early low priority jobs that don't get to run because so many of the
- // app's high priority jobs are pushed before low priority job. This may still
- // happen because of the job ordering mechanism, but moving jobs back prevents
- // one app's jobs from always being at the front (due to the early scheduled low
- // priority job and our base case of sorting by enqueue time).
-
- final long o1EffectiveEnqueueTime = getEffectiveEnqueueTime(o1);
- final long o2EffectiveEnqueueTime = getEffectiveEnqueueTime(o2);
-
- if (o1EffectiveEnqueueTime < o2EffectiveEnqueueTime) {
- return -1;
- } else if (o1EffectiveEnqueueTime > o2EffectiveEnqueueTime) {
- return 1;
- }
- }
-
- if (o1.enqueueTime < o2.enqueueTime) {
- return -1;
- }
- return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
- }
- }
-
- @VisibleForTesting
- final PendingJobComparator mPendingJobComparator = new PendingJobComparator();
-
- static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
- int where = Collections.binarySearch(array, newItem, comparator);
- if (where < 0) {
- where = ~where;
- }
- array.add(where, newItem);
- }
-
/**
* Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
* still clean up. On reinstall the package will have a new uid.
@@ -1434,7 +1249,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// This is a new job, we can just immediately put it on the pending
// list and try to run it.
mJobPackageTracker.notePending(jobStatus);
- addOrderedItem(mPendingJobs, jobStatus, mPendingJobComparator);
+ mPendingJobQueue.add(jobStatus);
maybeRunPendingJobsLocked();
} else {
evaluateControllerStatesLocked(jobStatus);
@@ -1563,7 +1378,7 @@ public class JobSchedulerService extends com.android.server.SystemService
cancelled.unprepareLocked();
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
// Remove from pending queue.
- if (mPendingJobs.remove(cancelled)) {
+ if (mPendingJobQueue.remove(cancelled)) {
mJobPackageTracker.noteNonpending(cancelled);
}
// Cancel if running.
@@ -1658,8 +1473,8 @@ public class JobSchedulerService extends com.android.server.SystemService
void reportActiveLocked() {
// active is true if pending queue contains jobs OR some job is running.
- boolean active = mPendingJobs.size() > 0;
- if (mPendingJobs.size() <= 0) {
+ boolean active = mPendingJobQueue.size() > 0;
+ if (!active) {
final ArraySet<JobStatus> runningJobs = mConcurrencyManager.getRunningJobsLocked();
for (int i = runningJobs.size() - 1; i >= 0; --i) {
final JobStatus job = runningJobs.valueAt(i);
@@ -1952,10 +1767,13 @@ public class JobSchedulerService extends com.android.server.SystemService
mJobPackageTracker.noteNonpending(job);
}
- void noteJobsNonpending(List<JobStatus> jobs) {
- for (int i = jobs.size() - 1; i >= 0; i--) {
- noteJobNonPending(jobs.get(i));
+ private void clearPendingJobQueue() {
+ JobStatus job;
+ mPendingJobQueue.resetIterator();
+ while ((job = mPendingJobQueue.next()) != null) {
+ noteJobNonPending(job);
}
+ mPendingJobQueue.clear();
}
/**
@@ -2236,7 +2054,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (js != null) {
if (isReadyToBeExecutedLocked(js)) {
mJobPackageTracker.notePending(js);
- addOrderedItem(mPendingJobs, js, mPendingJobComparator);
+ mPendingJobQueue.add(js);
}
mChangedJobList.remove(js);
} else {
@@ -2382,14 +2200,13 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
- noteJobsNonpending(mPendingJobs);
- mPendingJobs.clear();
+ clearPendingJobQueue();
stopNonReadyActiveJobsLocked();
mJobs.forEachJob(mReadyQueueFunctor);
mReadyQueueFunctor.postProcessLocked();
if (DEBUG) {
- final int queuedJobs = mPendingJobs.size();
+ final int queuedJobs = mPendingJobQueue.size();
if (queuedJobs == 0) {
Slog.d(TAG, "No jobs pending.");
} else {
@@ -2416,11 +2233,7 @@ public class JobSchedulerService extends com.android.server.SystemService
@GuardedBy("mLock")
private void postProcessLocked() {
noteJobsPending(newReadyJobs);
- mPendingJobs.addAll(newReadyJobs);
- mPendingJobComparator.refreshLocked();
- if (mPendingJobs.size() > 1) {
- mPendingJobs.sort(mPendingJobComparator);
- }
+ mPendingJobQueue.addAll(newReadyJobs);
newReadyJobs.clear();
}
@@ -2453,7 +2266,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mHandler.obtainMessage(MSG_STOP_JOB,
JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
.sendToTarget();
- } else if (mPendingJobs.remove(job)) {
+ } else if (mPendingJobQueue.remove(job)) {
noteJobNonPending(job);
}
return;
@@ -2530,7 +2343,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
mConcurrencyManager.stopJobOnServiceContextLocked(job, job.getStopReason(),
internalStopReason, debugReason);
- } else if (mPendingJobs.remove(job)) {
+ } else if (mPendingJobQueue.remove(job)) {
noteJobNonPending(job);
}
evaluateControllerStatesLocked(job);
@@ -2546,11 +2359,7 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
}
noteJobsPending(runnableJobs);
- mPendingJobs.addAll(runnableJobs);
- mPendingJobComparator.refreshLocked();
- if (mPendingJobs.size() > 1) {
- mPendingJobs.sort(mPendingJobComparator);
- }
+ mPendingJobQueue.addAll(runnableJobs);
} else {
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
@@ -2574,14 +2383,13 @@ public class JobSchedulerService extends com.android.server.SystemService
@GuardedBy("mLock")
private void maybeQueueReadyJobsForExecutionLocked() {
mHandler.removeMessages(MSG_CHECK_JOB);
- // This method will evaluate all jobs, so we don't need to keep any messages for a suubset
+ // This method will evaluate all jobs, so we don't need to keep any messages for a subset
// of jobs in the queue.
mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
mChangedJobList.clear();
if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
- noteJobsNonpending(mPendingJobs);
- mPendingJobs.clear();
+ clearPendingJobQueue();
stopNonReadyActiveJobsLocked();
mJobs.forEachJob(mMaybeQueueFunctor);
mMaybeQueueFunctor.postProcessLocked();
@@ -2682,7 +2490,7 @@ public class JobSchedulerService extends com.android.server.SystemService
return false;
}
- final boolean jobPending = mPendingJobs.contains(job);
+ final boolean jobPending = mPendingJobQueue.contains(job);
final boolean jobActive = rejectActive && mConcurrencyManager.isJobRunningLocked(job);
if (DEBUG) {
@@ -2802,7 +2610,7 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
void maybeRunPendingJobsLocked() {
if (DEBUG) {
- Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
+ Slog.d(TAG, "pending queue: " + mPendingJobQueue.size() + " jobs.");
}
mConcurrencyManager.assignJobsToContextsLocked();
reportActiveLocked();
@@ -3634,7 +3442,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
boolean printed = false;
- if (mPendingJobs.contains(js)) {
+ if (mPendingJobQueue.contains(js)) {
pw.print("pending");
printed = true;
}
@@ -3836,7 +3644,7 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(" !restricted=");
pw.print(!isRestricted);
pw.print(" !pending=");
- pw.print(!mPendingJobs.contains(job));
+ pw.print(!mPendingJobQueue.contains(job));
pw.print(" !active=");
pw.print(!mConcurrencyManager.isJobRunningLocked(job));
pw.print(" !backingup=");
@@ -3929,8 +3737,11 @@ public class JobSchedulerService extends com.android.server.SystemService
boolean pendingPrinted = false;
pw.println("Pending queue:");
pw.increaseIndent();
- for (int i = 0; i < mPendingJobs.size(); i++) {
- JobStatus job = mPendingJobs.get(i);
+ JobStatus job;
+ int pendingIdx = 0;
+ mPendingJobQueue.resetIterator();
+ while ((job = mPendingJobQueue.next()) != null) {
+ pendingIdx++;
if (!predicate.test(job)) {
continue;
}
@@ -3938,7 +3749,7 @@ public class JobSchedulerService extends com.android.server.SystemService
pendingPrinted = true;
}
- pw.print("Pending #"); pw.print(i); pw.print(": ");
+ pw.print("Pending #"); pw.print(pendingIdx); pw.print(": ");
pw.println(job.toShortString());
pw.increaseIndent();
@@ -3969,7 +3780,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// Print most recent first
final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r)
% NUM_COMPLETED_JOB_HISTORY;
- final JobStatus job = mLastCompletedJobs[idx];
+ job = mLastCompletedJobs[idx];
if (job != null) {
if (!predicate.test(job)) {
continue;
@@ -4062,7 +3873,7 @@ public class JobSchedulerService extends com.android.server.SystemService
JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
checkIfRestricted(job) != null);
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
- mPendingJobs.contains(job));
+ mPendingJobQueue.contains(job));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
mConcurrencyManager.isJobRunningLocked(job));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
@@ -4109,7 +3920,9 @@ public class JobSchedulerService extends com.android.server.SystemService
mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
filterAppId);
- for (JobStatus job : mPendingJobs) {
+ JobStatus job;
+ mPendingJobQueue.resetIterator();
+ while ((job = mPendingJobQueue.next()) != null) {
final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
job.writeToShortProto(proto, PendingJob.INFO);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
index 993e178de0ee..f91472bdd160 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.util.Pools;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.job.controllers.JobStatus;
import java.util.ArrayList;
@@ -54,11 +55,33 @@ class PendingJobQueue {
} else if (t2 == AppJobQueue.NO_NEXT_TIMESTAMP) {
return -1;
}
+ final int o1 = ajq1.peekNextOverrideState();
+ final int o2 = ajq2.peekNextOverrideState();
+ if (o1 != o2) {
+ // Higher override state (OVERRIDE_FULL) should be before lower state
+ // (OVERRIDE_SOFT)
+ return Integer.compare(o2, o1);
+ }
return Long.compare(t1, t2);
});
private int mSize = 0;
+ /**
+ * Whether to batch iteration so that we pull several of an app's jobs from the queue at the
+ * same time (resulting in some out of order pulls) instead of pulling purely based on the
+ * sort order. Batching it this way will mean we try to run several jobs of the same app at the
+ * same, resulting in fewer process restarts, and can allow the iteration runtime to amortize
+ * to O(A*J) instead of O(A*J*log(A)), where A = # apps and J = average # jobs per app.
+ */
+ private boolean mOptimizeIteration = true;
+
+ /**
+ * Number of jobs that have been pulled from the queue in succession. Used when
+ * {@link #mOptimizeIteration} is true to know when to switch to the next AppJobQueue.
+ */
+ private int mPullCount = 0;
+
private boolean mNeedToResetIterators = false;
void add(@NonNull JobStatus job) {
@@ -132,16 +155,44 @@ class PendingJobQueue {
mOrderedQueues.offer(ajq);
}
mNeedToResetIterators = false;
+ // Reset the pull count when the front of the queue changes.
+ mPullCount = 0;
} else if (mOrderedQueues.size() == 0) {
+ // Something significant changed, so the priority queue was cleared. Lazily regenerate
+ // the queue.
for (int i = mCurrentQueues.size() - 1; i >= 0; --i) {
final AppJobQueue ajq = mCurrentQueues.valueAt(i);
mOrderedQueues.offer(ajq);
}
+ // Reset the pull count when the front of the queue changes.
+ mPullCount = 0;
+ }
+ final int numQueues = mOrderedQueues.size();
+ if (numQueues == 0) {
+ return null;
}
- final AppJobQueue earliestQueue = mOrderedQueues.poll();
+
+ // Increase the pull limit at a slightly faster rate than log(A) increases (until A>=33).
+ // The pull limit increase is intended to balance fairness (one app can't starve out others)
+ // with efficiency (reducing process restarts).
+ // 1-4 apps --> pullLimit = 1, 5-8 apps --> pullLimit = 2, 9+ apps --> pullLimit = 3
+ final int pullLimit = mOptimizeIteration ? Math.min(3, ((numQueues - 1) >>> 2) + 1) : 1;
+
+ final AppJobQueue earliestQueue = mOrderedQueues.peek();
if (earliestQueue != null) {
- JobStatus job = earliestQueue.next();
- mOrderedQueues.offer(earliestQueue);
+ final JobStatus job = earliestQueue.next();
+ // Change the front of the queue if we've pulled pullLimit jobs from the current head
+ // or the current head has no more jobs to provide.
+ if (++mPullCount >= pullLimit
+ || earliestQueue.peekNextTimestamp() == AppJobQueue.NO_NEXT_TIMESTAMP) {
+ mOrderedQueues.poll();
+ if (earliestQueue.peekNextTimestamp() != AppJobQueue.NO_NEXT_TIMESTAMP) {
+ // No need to put back in the queue if it has no more jobs to give.
+ mOrderedQueues.offer(earliestQueue);
+ }
+ // Reset the pull count when the front of the queue changes.
+ mPullCount = 0;
+ }
return job;
}
return null;
@@ -179,12 +230,18 @@ class PendingJobQueue {
mNeedToResetIterators = true;
}
+ @VisibleForTesting
+ void setOptimizeIteration(boolean optimize) {
+ mOptimizeIteration = optimize;
+ }
+
int size() {
return mSize;
}
private static final class AppJobQueue {
static final long NO_NEXT_TIMESTAMP = -1L;
+ static final int NO_NEXT_OVERRIDE_STATE = -1;
private static class AdjustedJobStatus {
public long adjustedEnqueueTime;
@@ -207,7 +264,7 @@ class PendingJobQueue {
if (job1.overrideState != job2.overrideState) {
// Higher override state (OVERRIDE_FULL) should be before lower state
// (OVERRIDE_SOFT)
- return job2.overrideState - job1.overrideState;
+ return Integer.compare(job2.overrideState, job1.overrideState);
}
final boolean job1EJ = job1.isRequestedExpeditedJob();
@@ -223,18 +280,15 @@ class PendingJobQueue {
if (job1Priority != job2Priority) {
// Use the priority set by an app for intra-app job ordering. Higher
// priority should be before lower priority.
- return job2Priority - job1Priority;
+ return Integer.compare(job2Priority, job1Priority);
}
if (job1.lastEvaluatedBias != job2.lastEvaluatedBias) {
// Higher bias should go first.
- return job2.lastEvaluatedBias - job1.lastEvaluatedBias;
+ return Integer.compare(job2.lastEvaluatedBias, job1.lastEvaluatedBias);
}
- if (job1.enqueueTime < job2.enqueueTime) {
- return -1;
- }
- return job1.enqueueTime > job2.enqueueTime ? 1 : 0;
+ return Long.compare(job1.enqueueTime, job2.enqueueTime);
};
private static final Pools.Pool<AdjustedJobStatus> mAdjustedJobStatusPool =
@@ -344,9 +398,14 @@ class PendingJobQueue {
if (mCurIndex >= mJobs.size()) {
return null;
}
- JobStatus next = mJobs.get(mCurIndex).job;
- mCurIndex++;
- return next;
+ return mJobs.get(mCurIndex++).job;
+ }
+
+ int peekNextOverrideState() {
+ if (mCurIndex >= mJobs.size()) {
+ return NO_NEXT_OVERRIDE_STATE;
+ }
+ return mJobs.get(mCurIndex).job.overrideState;
}
long peekNextTimestamp() {
diff --git a/core/api/current.txt b/core/api/current.txt
index 5022f4ab11db..226c7d58d911 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -39631,22 +39631,34 @@ package android.speech {
public final class RecognitionSupport implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getInstalledLanguages();
- method @NonNull public java.util.List<java.lang.String> getPendingLanguages();
- method @NonNull public java.util.List<java.lang.String> getSupportedLanguages();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getInstalledLanguages();
+ method @NonNull public java.util.List<java.lang.String> getInstalledOnDeviceLanguages();
+ method @NonNull public java.util.List<java.lang.String> getOnlineLanguages();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getPendingLanguages();
+ method @NonNull public java.util.List<java.lang.String> getPendingOnDeviceLanguages();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getSupportedLanguages();
+ method @NonNull public java.util.List<java.lang.String> getSupportedOnDeviceLanguages();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.speech.RecognitionSupport> CREATOR;
}
public static final class RecognitionSupport.Builder {
ctor public RecognitionSupport.Builder();
- method @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguage(@NonNull String);
- method @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguage(@NonNull String);
- method @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguage(@NonNull String);
+ method @Deprecated @NonNull public android.speech.RecognitionSupport.Builder addInstalledLanguage(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addInstalledOnDeviceLanguage(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addOnlineLanguage(@NonNull String);
+ method @Deprecated @NonNull public android.speech.RecognitionSupport.Builder addPendingLanguage(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addPendingOnDeviceLanguage(@NonNull String);
+ method @Deprecated @NonNull public android.speech.RecognitionSupport.Builder addSupportedLanguage(@NonNull String);
+ method @NonNull public android.speech.RecognitionSupport.Builder addSupportedOnDeviceLanguage(@NonNull String);
method @NonNull public android.speech.RecognitionSupport build();
- method @NonNull public android.speech.RecognitionSupport.Builder setInstalledLanguages(@NonNull java.util.List<java.lang.String>);
- method @NonNull public android.speech.RecognitionSupport.Builder setPendingLanguages(@NonNull java.util.List<java.lang.String>);
- method @NonNull public android.speech.RecognitionSupport.Builder setSupportedLanguages(@NonNull java.util.List<java.lang.String>);
+ method @Deprecated @NonNull public android.speech.RecognitionSupport.Builder setInstalledLanguages(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.speech.RecognitionSupport.Builder setInstalledOnDeviceLanguages(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.speech.RecognitionSupport.Builder setOnlineLanguages(@NonNull java.util.List<java.lang.String>);
+ method @Deprecated @NonNull public android.speech.RecognitionSupport.Builder setPendingLanguages(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.speech.RecognitionSupport.Builder setPendingOnDeviceLanguages(@NonNull java.util.List<java.lang.String>);
+ method @Deprecated @NonNull public android.speech.RecognitionSupport.Builder setSupportedLanguages(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.speech.RecognitionSupport.Builder setSupportedOnDeviceLanguages(@NonNull java.util.List<java.lang.String>);
}
public interface RecognitionSupportCallback {
@@ -52527,7 +52539,6 @@ package android.view.autofill {
public final class AutofillManager {
method public void cancel();
- method public void clearAutofillRequestCallback();
method public void commit();
method public void disableAutofillServices();
method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -52553,7 +52564,6 @@ package android.view.autofill {
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
- method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
method public boolean showAutofillDialog(@NonNull android.view.View);
method public boolean showAutofillDialog(@NonNull android.view.View, int);
@@ -52574,10 +52584,6 @@ package android.view.autofill {
field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
}
- public interface AutofillRequestCallback {
- method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
- }
-
public final class AutofillValue implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutofillValue forDate(long);
@@ -52965,12 +52971,10 @@ package android.view.inputmethod {
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index dd4d4dcd3d03..57107c286b89 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2805,8 +2805,11 @@ package android.companion.virtual {
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @NonNull public java.util.Set<android.content.ComponentName> getAllowedCrossTaskNavigations();
method @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
method public int getDefaultActivityPolicy();
+ method public int getDefaultNavigationPolicy();
method public int getLockState();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -2815,13 +2818,17 @@ package android.companion.virtual {
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
+ field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
}
public static final class VirtualDeviceParams.Builder {
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
}
@@ -5248,7 +5255,7 @@ package android.hardware.soundtrigger {
public static final class SoundTrigger.KeyphraseRecognitionExtra implements android.os.Parcelable {
method public int describeContents();
- method public int getCoarseConfidenceLevel();
+ method @IntRange(from=0, to=100) public int getCoarseConfidenceLevel();
method public int getKeyphraseId();
method public int getRecognitionModes();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fe0edfea59c8..49a61580ab3b 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -449,7 +449,7 @@ interface IActivityManager {
void requestInteractiveBugReport();
void requestFullBugReport();
- void requestRemoteBugReport();
+ void requestRemoteBugReport(long nonce);
boolean launchBugReportHandlerApp();
List<String> getBugreportWhitelistedPackages();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 487674066815..82e5ebf755c2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -908,6 +908,14 @@ public class DevicePolicyManager {
"android.intent.extra.REMOTE_BUGREPORT_HASH";
/**
+ * Extra for shared bugreport's nonce in long integer type.
+ *
+ * @hide
+ */
+ public static final String EXTRA_REMOTE_BUGREPORT_NONCE =
+ "android.intent.extra.REMOTE_BUGREPORT_NONCE";
+
+ /**
* Extra for remote bugreport notification shown type.
*
* @hide
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index cbb51838507b..3b1ff3fa65b5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -83,8 +83,31 @@ public final class VirtualDeviceParams implements Parcelable {
*/
public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1;
+ /** @hide */
+ @IntDef(prefix = "NAVIGATION_POLICY_",
+ value = {NAVIGATION_POLICY_DEFAULT_ALLOWED, NAVIGATION_POLICY_DEFAULT_BLOCKED})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface NavigationPolicy {}
+
+ /**
+ * Indicates that tasks are allowed to navigate to other tasks on this virtual device,
+ * unless they are explicitly blocked by {@link Builder#setBlockedCrossTaskNavigations}.
+ */
+ public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0;
+
+ /**
+ * Indicates that tasks are blocked from navigating to other tasks by default on this virtual
+ * device, unless allowed by {@link Builder#setAllowedCrossTaskNavigations}.
+ */
+ public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1;
+
private final int mLockState;
@NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
+ @NonNull private final ArraySet<ComponentName> mAllowedCrossTaskNavigations;
+ @NonNull private final ArraySet<ComponentName> mBlockedCrossTaskNavigations;
+ @NavigationPolicy
+ private final int mDefaultNavigationPolicy;
@NonNull private final ArraySet<ComponentName> mAllowedActivities;
@NonNull private final ArraySet<ComponentName> mBlockedActivities;
@ActivityPolicy
@@ -93,15 +116,23 @@ public final class VirtualDeviceParams implements Parcelable {
private VirtualDeviceParams(
@LockState int lockState,
@NonNull Set<UserHandle> usersWithMatchingAccounts,
+ @NonNull Set<ComponentName> allowedCrossTaskNavigations,
+ @NonNull Set<ComponentName> blockedCrossTaskNavigations,
+ @NavigationPolicy int defaultNavigationPolicy,
@NonNull Set<ComponentName> allowedActivities,
@NonNull Set<ComponentName> blockedActivities,
@ActivityPolicy int defaultActivityPolicy) {
Preconditions.checkNotNull(usersWithMatchingAccounts);
+ Preconditions.checkNotNull(allowedCrossTaskNavigations);
+ Preconditions.checkNotNull(blockedCrossTaskNavigations);
Preconditions.checkNotNull(allowedActivities);
Preconditions.checkNotNull(blockedActivities);
mLockState = lockState;
mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
+ mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations);
+ mBlockedCrossTaskNavigations = new ArraySet<>(blockedCrossTaskNavigations);
+ mDefaultNavigationPolicy = defaultNavigationPolicy;
mAllowedActivities = new ArraySet<>(allowedActivities);
mBlockedActivities = new ArraySet<>(blockedActivities);
mDefaultActivityPolicy = defaultActivityPolicy;
@@ -111,6 +142,9 @@ public final class VirtualDeviceParams implements Parcelable {
private VirtualDeviceParams(Parcel parcel) {
mLockState = parcel.readInt();
mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null);
+ mAllowedCrossTaskNavigations = (ArraySet<ComponentName>) parcel.readArraySet(null);
+ mBlockedCrossTaskNavigations = (ArraySet<ComponentName>) parcel.readArraySet(null);
+ mDefaultNavigationPolicy = parcel.readInt();
mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
mDefaultActivityPolicy = parcel.readInt();
@@ -136,6 +170,45 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
+ * Returns the set of tasks that are allowed to navigate from current task,
+ * or empty set if all tasks are allowed, except the ones explicitly blocked.
+ * If neither allowed or blocked tasks are provided, all task navigations will
+ * be be allowed by default.
+ *
+ * @see Builder#setAllowedCrossTaskNavigations(Set)
+ */
+ @NonNull
+ public Set<ComponentName> getAllowedCrossTaskNavigations() {
+ return Collections.unmodifiableSet(mAllowedCrossTaskNavigations);
+ }
+
+ /**
+ * Returns the set of tasks that are blocked from navigating from the current task,
+ * or empty set to indicate that all tasks in {@link #getAllowedCrossTaskNavigations}
+ * are allowed. If neither allowed or blocked tasks are provided, all task navigations
+ * will be be allowed by default.
+ *
+ * @see Builder#setBlockedCrossTaskNavigations(Set)
+ */
+ @NonNull
+ public Set<ComponentName> getBlockedCrossTaskNavigations() {
+ return Collections.unmodifiableSet(mBlockedCrossTaskNavigations);
+ }
+
+ /**
+ * Returns {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED} if tasks are allowed to navigate on
+ * this virtual device by default, or {@link #NAVIGATION_POLICY_DEFAULT_BLOCKED} if tasks
+ * must be allowed by {@link Builder#setAllowedCrossTaskNavigations} to navigate here.
+ *
+ * @see Builder#setAllowedCrossTaskNavigations
+ * @see Builder#setBlockedCrossTaskNavigations
+ */
+ @NavigationPolicy
+ public int getDefaultNavigationPolicy() {
+ return mDefaultNavigationPolicy;
+ }
+
+ /**
* Returns the set of activities allowed to be streamed, or empty set if all activities are
* allowed, except the ones explicitly blocked.
*
@@ -179,6 +252,9 @@ public final class VirtualDeviceParams implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mLockState);
dest.writeArraySet(mUsersWithMatchingAccounts);
+ dest.writeArraySet(mAllowedCrossTaskNavigations);
+ dest.writeArraySet(mBlockedCrossTaskNavigations);
+ dest.writeInt(mDefaultNavigationPolicy);
dest.writeArraySet(mAllowedActivities);
dest.writeArraySet(mBlockedActivities);
dest.writeInt(mDefaultActivityPolicy);
@@ -195,6 +271,9 @@ public final class VirtualDeviceParams implements Parcelable {
VirtualDeviceParams that = (VirtualDeviceParams) o;
return mLockState == that.mLockState
&& mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
+ && Objects.equals(mAllowedCrossTaskNavigations, that.mAllowedCrossTaskNavigations)
+ && Objects.equals(mBlockedCrossTaskNavigations, that.mBlockedCrossTaskNavigations)
+ && mDefaultNavigationPolicy == that.mDefaultNavigationPolicy
&& Objects.equals(mAllowedActivities, that.mAllowedActivities)
&& Objects.equals(mBlockedActivities, that.mBlockedActivities)
&& mDefaultActivityPolicy == that.mDefaultActivityPolicy;
@@ -203,8 +282,9 @@ public final class VirtualDeviceParams implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(
- mLockState, mUsersWithMatchingAccounts, mAllowedActivities, mBlockedActivities,
- mDefaultActivityPolicy);
+ mLockState, mUsersWithMatchingAccounts, mAllowedCrossTaskNavigations,
+ mBlockedCrossTaskNavigations, mDefaultNavigationPolicy, mAllowedActivities,
+ mBlockedActivities, mDefaultActivityPolicy);
}
@Override
@@ -213,6 +293,9 @@ public final class VirtualDeviceParams implements Parcelable {
return "VirtualDeviceParams("
+ " mLockState=" + mLockState
+ " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts
+ + " mAllowedCrossTaskNavigations=" + mAllowedCrossTaskNavigations
+ + " mBlockedCrossTaskNavigations=" + mBlockedCrossTaskNavigations
+ + " mDefaultNavigationPolicy=" + mDefaultNavigationPolicy
+ " mAllowedActivities=" + mAllowedActivities
+ " mBlockedActivities=" + mBlockedActivities
+ " mDefaultActivityPolicy=" + mDefaultActivityPolicy
@@ -237,7 +320,12 @@ public final class VirtualDeviceParams implements Parcelable {
public static final class Builder {
private @LockState int mLockState = LOCK_STATE_DEFAULT;
- @NonNull private Set<UserHandle> mUsersWithMatchingAccounts = Collections.emptySet();;
+ @NonNull private Set<UserHandle> mUsersWithMatchingAccounts = Collections.emptySet();
+ @NonNull private Set<ComponentName> mAllowedCrossTaskNavigations = Collections.emptySet();
+ @NonNull private Set<ComponentName> mBlockedCrossTaskNavigations = Collections.emptySet();
+ @NavigationPolicy
+ private int mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_ALLOWED;
+ private boolean mDefaultNavigationPolicyConfigured = false;
@NonNull private Set<ComponentName> mBlockedActivities = Collections.emptySet();
@NonNull private Set<ComponentName> mAllowedActivities = Collections.emptySet();
@ActivityPolicy
@@ -288,6 +376,70 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
+ * Sets the tasks allowed to navigate from current task in the virtual device. Tasks
+ * not in {@code allowedCrossTaskNavigations} will be blocked from navigating to a new
+ * task. Calling this method will cause {@link #getDefaultNavigationPolicy()} to be
+ * {@link #NAVIGATION_POLICY_DEFAULT_BLOCKED}, meaning tasks not in
+ * {@code allowedCrossTaskNavigations} will be blocked from navigating here.
+ *
+ * <p>This method must not be called if {@link #setBlockedCrossTaskNavigations(Set)} has
+ * been called.
+ *
+ * @throws IllegalArgumentException if {@link #setBlockedCrossTaskNavigations(Set)} has been
+ * called.
+ *
+ * @param allowedCrossTaskNavigations A set of tasks {@link ComponentName} allowed to
+ * navigate to new tasks in the virtual device.
+ */
+ @NonNull
+ public Builder setAllowedCrossTaskNavigations(
+ @NonNull Set<ComponentName> allowedCrossTaskNavigations) {
+ Preconditions.checkNotNull(allowedCrossTaskNavigations);
+ if (mDefaultNavigationPolicyConfigured
+ && mDefaultNavigationPolicy != NAVIGATION_POLICY_DEFAULT_BLOCKED) {
+ throw new IllegalArgumentException(
+ "Allowed cross task navigation and blocked task navigation cannot "
+ + " both be set.");
+ }
+ mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_BLOCKED;
+ mDefaultNavigationPolicyConfigured = true;
+ mAllowedCrossTaskNavigations = allowedCrossTaskNavigations;
+ return this;
+ }
+
+ /**
+ * Sets the tasks blocked from navigating from current task in the virtual device.
+ * Tasks are allowed to navigate unless they are in
+ * {@code blockedCrossTaskNavigations}. Calling this method will cause
+ * {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch
+ * unless they are in {@code blockedCrossTaskNavigations}.
+ *
+ * <p> This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
+ * been called.
+ *
+ * @throws IllegalArgumentException if {@link #setAllowedCrossTaskNavigations(Set)} has
+ * been called.
+ *
+ * @param blockedCrossTaskNavigations A set of tasks {@link ComponentName} to be
+ * blocked from navigating to new tasks in the virtual device.
+ */
+ @NonNull
+ public Builder setBlockedCrossTaskNavigations(
+ @NonNull Set<ComponentName> blockedCrossTaskNavigations) {
+ Preconditions.checkNotNull(blockedCrossTaskNavigations);
+ if (mDefaultNavigationPolicyConfigured
+ && mDefaultNavigationPolicy != NAVIGATION_POLICY_DEFAULT_ALLOWED) {
+ throw new IllegalArgumentException(
+ "Allowed cross task navigation and blocked task navigation cannot "
+ + " be set.");
+ }
+ mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_ALLOWED;
+ mDefaultNavigationPolicyConfigured = true;
+ mBlockedCrossTaskNavigations = blockedCrossTaskNavigations;
+ return this;
+ }
+
+ /**
* Sets the activities allowed to be launched in the virtual device. Calling this method
* will cause {@link #getDefaultActivityPolicy()} to be
* {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED}, meaning activities not in
@@ -349,6 +501,9 @@ public final class VirtualDeviceParams implements Parcelable {
return new VirtualDeviceParams(
mLockState,
mUsersWithMatchingAccounts,
+ mAllowedCrossTaskNavigations,
+ mBlockedCrossTaskNavigations,
+ mDefaultNavigationPolicy,
mAllowedActivities,
mBlockedActivities,
mDefaultActivityPolicy);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 24b1b6adb450..5dd68a9fcfc9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3913,6 +3913,7 @@ public abstract class Context {
MEDIA_METRICS_SERVICE,
//@hide: ATTESTATION_VERIFICATION_SERVICE,
//@hide: SAFETY_CENTER_SERVICE,
+ DISPLAY_HASH_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3999,6 +4000,8 @@ public abstract class Context {
* <dt> {@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification")
* <dd> A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing
* web domain approval state.
+ * <dt> {@link #DISPLAY_HASH_SERVICE} ("display_hash")
+ * <dd> A {@link android.view.displayhash.DisplayHashManager} for management of display hashes.
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -4082,6 +4085,8 @@ public abstract class Context {
* @see #HARDWARE_PROPERTIES_SERVICE
* @see #DOMAIN_VERIFICATION_SERVICE
* @see android.content.pm.verify.domain.DomainVerificationManager
+ * @see #DISPLAY_HASH_SERVICE
+ * @see android.view.displayhash.DisplayHashManager
*/
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
@@ -4102,7 +4107,8 @@ public abstract class Context {
* {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
* {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
* {@link android.app.usage.NetworkStatsManager},
- * {@link android.content.pm.verify.domain.DomainVerificationManager}.
+ * {@link android.content.pm.verify.domain.DomainVerificationManager},
+ * {@link android.view.displayhash.DisplayHashManager}.
* </p>
*
* <p>
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index c36390917cf1..a9d665c8b8a5 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -29,6 +29,7 @@ import static android.system.OsConstants.EPIPE;
import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -1630,7 +1631,8 @@ public class SoundTrigger {
* @hide
*/
@UnsupportedAppUsage
- public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
+ public KeyphraseRecognitionExtra(int id, int recognitionModes,
+ @IntRange(from = 0, to = 100) int coarseConfidenceLevel,
@Nullable ConfidenceLevel[] confidenceLevels) {
this.id = id;
this.recognitionModes = recognitionModes;
@@ -1660,6 +1662,7 @@ public class SoundTrigger {
*
* <p>The confidence level is expressed in percent (0% -100%).
*/
+ @IntRange(from = 0, to = 100)
public int getCoarseConfidenceLevel() {
return coarseConfidenceLevel;
}
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index ab741990430f..82d4443ea724 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -218,14 +218,15 @@ public class SystemProperties {
/**
* Set the value for the given {@code key} to {@code val}.
*
- * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+ * @throws IllegalArgumentException for non read-only properties if the {@code val} exceeds
+ * 91 characters
* @throws RuntimeException if the property cannot be set, for example, if it was blocked by
* SELinux. libc will log the underlying reason.
* @hide
*/
@UnsupportedAppUsage
public static void set(@NonNull String key, @Nullable String val) {
- if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
+ if (val != null && !key.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
throw new IllegalArgumentException("value of system property '" + key
+ "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9971cbcfa586..646a7095c1b3 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -301,28 +301,7 @@ public class StorageManager {
/** @hide The volume is not encrypted. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int ENCRYPTION_STATE_NONE =
- IVold.ENCRYPTION_STATE_NONE;
-
- /** @hide The volume has been encrypted succesfully. */
- public static final int ENCRYPTION_STATE_OK =
- IVold.ENCRYPTION_STATE_OK;
-
- /** @hide The volume is in a bad state. */
- public static final int ENCRYPTION_STATE_ERROR_UNKNOWN =
- IVold.ENCRYPTION_STATE_ERROR_UNKNOWN;
-
- /** @hide Encryption is incomplete */
- public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE =
- IVold.ENCRYPTION_STATE_ERROR_INCOMPLETE;
-
- /** @hide Encryption is incomplete and irrecoverable */
- public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT =
- IVold.ENCRYPTION_STATE_ERROR_INCONSISTENT;
-
- /** @hide Underlying data is corrupt */
- public static final int ENCRYPTION_STATE_ERROR_CORRUPT =
- IVold.ENCRYPTION_STATE_ERROR_CORRUPT;
+ public static final int ENCRYPTION_STATE_NONE = 1;
private static volatile IStorageManager sStorageManager = null;
@@ -3033,15 +3012,10 @@ public class StorageManager {
@GuardedBy("mFuseAppLoopLock")
private @Nullable FuseAppLoop mFuseAppLoop = null;
- /// Consts to match the password types in cryptfs.h
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int CRYPT_TYPE_PASSWORD = IVold.PASSWORD_TYPE_PASSWORD;
+ public static final int CRYPT_TYPE_PASSWORD = 0;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int CRYPT_TYPE_DEFAULT = IVold.PASSWORD_TYPE_DEFAULT;
- /** @hide */
- public static final int CRYPT_TYPE_PATTERN = IVold.PASSWORD_TYPE_PATTERN;
- /** @hide */
- public static final int CRYPT_TYPE_PIN = IVold.PASSWORD_TYPE_PIN;
+ public static final int CRYPT_TYPE_DEFAULT = 1;
}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index c73ebed3c368..431a88d324d8 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -97,7 +97,7 @@ public final class FillRequest implements Parcelable {
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
- // The flag value 0x20 has been defined in AutofillManager.
+ // The flag value 0x20 has been used.
/**
* Indicates the request supports fill dialog presentation for the fields, the
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index 38659e12b1ce..091bf797e24b 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -42,7 +42,19 @@ public interface QuickAccessWalletClient extends Closeable {
*/
@NonNull
static QuickAccessWalletClient create(@NonNull Context context) {
- return new QuickAccessWalletClientImpl(context);
+ return create(context, null /* bgExecutor */);
+ }
+
+ /**
+ * Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the
+ * service is unavailable, {@link #isWalletServiceAvailable()} will return false.
+ * @param context Context.
+ * @param bgExecutor A background {@link Executor} for service registration.
+ * @hide
+ */
+ @NonNull
+ static QuickAccessWalletClient create(@NonNull Context context, @Nullable Executor bgExecutor) {
+ return new QuickAccessWalletClientImpl(context, bgExecutor);
}
/**
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 95b51ea4dece..a3304a9ca386 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -67,6 +67,7 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
private final Context mContext;
private final Queue<ApiCaller> mRequestQueue;
private final Map<WalletServiceEventListener, String> mEventListeners;
+ private final Executor mLifecycleExecutor;
private boolean mIsConnected;
/** Timeout for active service connections (1 minute) */
private static final long SERVICE_CONNECTION_TIMEOUT_MS = 60 * 1000;
@@ -79,10 +80,11 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
private static final int MSG_TIMEOUT_SERVICE = 5;
- QuickAccessWalletClientImpl(@NonNull Context context) {
+ QuickAccessWalletClientImpl(@NonNull Context context, @Nullable Executor bgExecutor) {
mContext = context.getApplicationContext();
mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
mHandler = new Handler(Looper.getMainLooper());
+ mLifecycleExecutor = (bgExecutor == null) ? Runnable::run : bgExecutor;
mRequestQueue = new LinkedList<>();
mEventListeners = new HashMap<>(1);
}
@@ -369,7 +371,7 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
Intent intent = new Intent(SERVICE_INTERFACE);
intent.setComponent(mServiceInfo.getComponentName());
int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
- mContext.bindService(intent, this, flags);
+ mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
resetServiceConnectionTimeout();
}
@@ -411,7 +413,7 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
return;
}
mIsConnected = false;
- mContext.unbindService(/*conn=*/ this);
+ mLifecycleExecutor.execute(() -> mContext.unbindService(/*conn=*/ this));
mService = null;
mEventListeners.clear();
mRequestQueue.clear();
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 317408387526..c20fc75a51f3 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -24,6 +24,7 @@ import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.ViewRootImpl.LOCAL_LAYOUT;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.animation.Animator;
@@ -39,6 +40,7 @@ import android.app.Service;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
+import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -257,6 +259,8 @@ public abstract class WallpaperService extends Service {
private final Point mLastSurfaceSize = new Point();
private final Matrix mTmpMatrix = new Matrix();
private final float[] mTmpValues = new float[9];
+ private final WindowLayout mWindowLayout = new WindowLayout();
+ private final Rect mTempRect = new Rect();
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
@@ -1091,7 +1095,8 @@ public abstract class WallpaperService extends Service {
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final Configuration config = mMergedConfiguration.getMergedConfiguration();
- final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+ final WindowConfiguration winConfig = config.windowConfiguration;
+ final Rect maxBounds = winConfig.getMaxBounds();
if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
&& myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
mLayout.width = myWidth;
@@ -1149,10 +1154,28 @@ public abstract class WallpaperService extends Service {
mLayout.surfaceInsets.set(0, 0, 0, 0);
}
- final int relayoutResult = mSession.relayout(
- mWindow, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, mWinFrames, mMergedConfiguration, mSurfaceControl,
- mInsetsState, mTempControls, mSyncSeqIdBundle);
+ int relayoutResult = 0;
+ if (LOCAL_LAYOUT) {
+ if (!mSurfaceControl.isValid()) {
+ relayoutResult = mSession.updateVisibility(mWindow, mLayout,
+ View.VISIBLE, mMergedConfiguration, mSurfaceControl,
+ mInsetsState, mTempControls);
+ }
+
+ final Rect displayCutoutSafe = mTempRect;
+ mInsetsState.getDisplayCutoutSafe(displayCutoutSafe);
+ mWindowLayout.computeFrames(mLayout, mInsetsState, displayCutoutSafe,
+ winConfig.getBounds(), winConfig.getWindowingMode(), mWidth,
+ mHeight, mRequestedVisibilities, null /* attachedWindowFrame */,
+ 1f /* compatScale */, mWinFrames);
+
+ mSession.updateLayout(mWindow, mLayout, 0 /* flags */, mWinFrames, mWidth,
+ mHeight);
+ } else {
+ relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
+ View.VISIBLE, 0, mWinFrames, mMergedConfiguration,
+ mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+ }
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
@@ -1202,7 +1225,7 @@ public abstract class WallpaperService extends Service {
null /* ignoringVisibilityState */, config.isScreenRound(),
false /* alwaysConsumeSystemBars */, mLayout.softInputMode,
mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
- config.windowConfiguration.getWindowingMode(), null /* typeSideMap */);
+ winConfig.getWindowingMode(), null /* typeSideMap */);
if (!fixedSize) {
final Rect padding = mIWallpaperEngine.mDisplayPadding;
diff --git a/core/java/android/speech/RecognitionSupport.java b/core/java/android/speech/RecognitionSupport.java
index 43c8e1b956f0..60c3b63e80c0 100644
--- a/core/java/android/speech/RecognitionSupport.java
+++ b/core/java/android/speech/RecognitionSupport.java
@@ -17,6 +17,8 @@
package android.speech;
import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
@@ -34,19 +36,63 @@ import java.util.List;
)
public final class RecognitionSupport implements Parcelable {
- /** Support for this request is ready for use on this device for the returned languages. */
+ /**
+ * Support for this request is ready for use on this device for the returned languages.
+ *
+ * @deprecated See {@link #getInstalledOnDeviceLanguages()}.
+ */
@NonNull
+ @Deprecated
@DataClass.PluralOf("installedLanguage")
- private List<String> mInstalledLanguages = null;
+ private List<String> mInstalledLanguages = List.of();
- /** Support for this request is scheduled for download for the returned languages. */
+ /**
+ * Support for this request is scheduled for download for the returned languages.
+ *
+ * @deprecated See {@link #getPendingOnDeviceLanguages()}.
+ */
+ @NonNull
+ @Deprecated
@DataClass.PluralOf("pendingLanguage")
- @NonNull private List<String> mPendingLanguages = null;
+ private List<String> mPendingLanguages = List.of();
- /** These languages are supported but need to be downloaded before use. */
+ /**
+ * These languages are supported but need to be downloaded before use. See {@link
+ * SpeechRecognizer#triggerModelDownload(Intent)}.
+ *
+ * @deprecated See {@link #getSupportedOnDeviceLanguages()}.
+ */
@NonNull
+ @Deprecated
@DataClass.PluralOf("supportedLanguage")
- private List<String> mSupportedLanguages = null;
+ private List<String> mSupportedLanguages = List.of();
+
+ /** Support for this request is ready for use on this device for the returned languages. */
+ @NonNull
+ @DataClass.PluralOf("installedOnDeviceLanguage")
+ private List<String> mInstalledOnDeviceLanguages = List.of();
+
+ /** Support for this request is scheduled for download for the returned languages. */
+ @NonNull
+ @DataClass.PluralOf("pendingOnDeviceLanguage")
+ private List<String> mPendingOnDeviceLanguages = List.of();
+
+ /**
+ * These languages are supported but need to be downloaded before use. See {@link
+ * SpeechRecognizer#triggerModelDownload(Intent)}.
+ */
+ @NonNull
+ @DataClass.PluralOf("supportedOnDeviceLanguage")
+ private List<String> mSupportedOnDeviceLanguages = List.of();
+
+ /**
+ * Support for this request is available via a remote implementation. {@link SpeechRecognizer}
+ * created via {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)} are expected to
+ * return an empty list.
+ */
+ @NonNull
+ @DataClass.PluralOf("onlineLanguage")
+ private List<String> mOnlineLanguages = List.of();
@@ -65,46 +111,110 @@ public final class RecognitionSupport implements Parcelable {
@DataClass.Generated.Member
/* package-private */ RecognitionSupport(
- @NonNull List<String> installedLanguages,
- @NonNull List<String> pendingLanguages,
- @NonNull List<String> supportedLanguages) {
+ @NonNull @Deprecated List<String> installedLanguages,
+ @NonNull @Deprecated List<String> pendingLanguages,
+ @NonNull @Deprecated List<String> supportedLanguages,
+ @NonNull List<String> installedOnDeviceLanguages,
+ @NonNull List<String> pendingOnDeviceLanguages,
+ @NonNull List<String> supportedOnDeviceLanguages,
+ @NonNull List<String> onlineLanguages) {
this.mInstalledLanguages = installedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInstalledLanguages);
+ com.android.internal.util.AnnotationValidations.validate(
+ Deprecated.class, null, mInstalledLanguages);
this.mPendingLanguages = pendingLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPendingLanguages);
+ com.android.internal.util.AnnotationValidations.validate(
+ Deprecated.class, null, mPendingLanguages);
this.mSupportedLanguages = supportedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSupportedLanguages);
+ com.android.internal.util.AnnotationValidations.validate(
+ Deprecated.class, null, mSupportedLanguages);
+ this.mInstalledOnDeviceLanguages = installedOnDeviceLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInstalledOnDeviceLanguages);
+ this.mPendingOnDeviceLanguages = pendingOnDeviceLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPendingOnDeviceLanguages);
+ this.mSupportedOnDeviceLanguages = supportedOnDeviceLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSupportedOnDeviceLanguages);
+ this.mOnlineLanguages = onlineLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mOnlineLanguages);
// onConstructed(); // You can define this method to get a callback
}
/**
* Support for this request is ready for use on this device for the returned languages.
+ *
+ * @deprecated See {@link #getInstalledOnDeviceLanguages()}.
*/
@DataClass.Generated.Member
- public @NonNull List<String> getInstalledLanguages() {
+ @NonNull @Deprecated public List<String> getInstalledLanguages() {
return mInstalledLanguages;
}
/**
* Support for this request is scheduled for download for the returned languages.
+ *
+ * @deprecated See {@link #getPendingOnDeviceLanguages()}.
*/
@DataClass.Generated.Member
- public @NonNull List<String> getPendingLanguages() {
+ @NonNull @Deprecated public List<String> getPendingLanguages() {
return mPendingLanguages;
}
/**
- * These languages are supported but need to be downloaded before use.
+ * These languages are supported but need to be downloaded before use. See {@link
+ * SpeechRecognizer#triggerModelDownload(Intent)}.
+ *
+ * @deprecated See {@link #getSupportedOnDeviceLanguages()}.
*/
@DataClass.Generated.Member
- public @NonNull List<String> getSupportedLanguages() {
+ @NonNull @Deprecated public List<String> getSupportedLanguages() {
return mSupportedLanguages;
}
+ /**
+ * Support for this request is ready for use on this device for the returned languages.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getInstalledOnDeviceLanguages() {
+ return mInstalledOnDeviceLanguages;
+ }
+
+ /**
+ * Support for this request is scheduled for download for the returned languages.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getPendingOnDeviceLanguages() {
+ return mPendingOnDeviceLanguages;
+ }
+
+ /**
+ * These languages are supported but need to be downloaded before use. See {@link
+ * SpeechRecognizer#triggerModelDownload(Intent)}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getSupportedOnDeviceLanguages() {
+ return mSupportedOnDeviceLanguages;
+ }
+
+ /**
+ * Support for this request is available via a remote implementation. {@link SpeechRecognizer}
+ * created via {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)} are expected to
+ * return an empty list.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getOnlineLanguages() {
+ return mOnlineLanguages;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -114,7 +224,11 @@ public final class RecognitionSupport implements Parcelable {
return "RecognitionSupport { " +
"installedLanguages = " + mInstalledLanguages + ", " +
"pendingLanguages = " + mPendingLanguages + ", " +
- "supportedLanguages = " + mSupportedLanguages +
+ "supportedLanguages = " + mSupportedLanguages + ", " +
+ "installedOnDeviceLanguages = " + mInstalledOnDeviceLanguages + ", " +
+ "pendingOnDeviceLanguages = " + mPendingOnDeviceLanguages + ", " +
+ "supportedOnDeviceLanguages = " + mSupportedOnDeviceLanguages + ", " +
+ "onlineLanguages = " + mOnlineLanguages +
" }";
}
@@ -133,7 +247,11 @@ public final class RecognitionSupport implements Parcelable {
return true
&& java.util.Objects.equals(mInstalledLanguages, that.mInstalledLanguages)
&& java.util.Objects.equals(mPendingLanguages, that.mPendingLanguages)
- && java.util.Objects.equals(mSupportedLanguages, that.mSupportedLanguages);
+ && java.util.Objects.equals(mSupportedLanguages, that.mSupportedLanguages)
+ && java.util.Objects.equals(mInstalledOnDeviceLanguages, that.mInstalledOnDeviceLanguages)
+ && java.util.Objects.equals(mPendingOnDeviceLanguages, that.mPendingOnDeviceLanguages)
+ && java.util.Objects.equals(mSupportedOnDeviceLanguages, that.mSupportedOnDeviceLanguages)
+ && java.util.Objects.equals(mOnlineLanguages, that.mOnlineLanguages);
}
@Override
@@ -146,6 +264,10 @@ public final class RecognitionSupport implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mInstalledLanguages);
_hash = 31 * _hash + java.util.Objects.hashCode(mPendingLanguages);
_hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLanguages);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mInstalledOnDeviceLanguages);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPendingOnDeviceLanguages);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mSupportedOnDeviceLanguages);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mOnlineLanguages);
return _hash;
}
@@ -158,6 +280,10 @@ public final class RecognitionSupport implements Parcelable {
dest.writeStringList(mInstalledLanguages);
dest.writeStringList(mPendingLanguages);
dest.writeStringList(mSupportedLanguages);
+ dest.writeStringList(mInstalledOnDeviceLanguages);
+ dest.writeStringList(mPendingOnDeviceLanguages);
+ dest.writeStringList(mSupportedOnDeviceLanguages);
+ dest.writeStringList(mOnlineLanguages);
}
@Override
@@ -177,16 +303,42 @@ public final class RecognitionSupport implements Parcelable {
in.readStringList(pendingLanguages);
List<String> supportedLanguages = new java.util.ArrayList<>();
in.readStringList(supportedLanguages);
+ List<String> installedOnDeviceLanguages = new java.util.ArrayList<>();
+ in.readStringList(installedOnDeviceLanguages);
+ List<String> pendingOnDeviceLanguages = new java.util.ArrayList<>();
+ in.readStringList(pendingOnDeviceLanguages);
+ List<String> supportedOnDeviceLanguages = new java.util.ArrayList<>();
+ in.readStringList(supportedOnDeviceLanguages);
+ List<String> onlineLanguages = new java.util.ArrayList<>();
+ in.readStringList(onlineLanguages);
this.mInstalledLanguages = installedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInstalledLanguages);
+ com.android.internal.util.AnnotationValidations.validate(
+ Deprecated.class, null, mInstalledLanguages);
this.mPendingLanguages = pendingLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPendingLanguages);
+ com.android.internal.util.AnnotationValidations.validate(
+ Deprecated.class, null, mPendingLanguages);
this.mSupportedLanguages = supportedLanguages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSupportedLanguages);
+ com.android.internal.util.AnnotationValidations.validate(
+ Deprecated.class, null, mSupportedLanguages);
+ this.mInstalledOnDeviceLanguages = installedOnDeviceLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInstalledOnDeviceLanguages);
+ this.mPendingOnDeviceLanguages = pendingOnDeviceLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPendingOnDeviceLanguages);
+ this.mSupportedOnDeviceLanguages = supportedOnDeviceLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSupportedOnDeviceLanguages);
+ this.mOnlineLanguages = onlineLanguages;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mOnlineLanguages);
// onConstructed(); // You can define this method to get a callback
}
@@ -212,9 +364,13 @@ public final class RecognitionSupport implements Parcelable {
@DataClass.Generated.Member
public static final class Builder {
- private @NonNull List<String> mInstalledLanguages;
- private @NonNull List<String> mPendingLanguages;
- private @NonNull List<String> mSupportedLanguages;
+ private @NonNull @Deprecated List<String> mInstalledLanguages;
+ private @NonNull @Deprecated List<String> mPendingLanguages;
+ private @NonNull @Deprecated List<String> mSupportedLanguages;
+ private @NonNull List<String> mInstalledOnDeviceLanguages;
+ private @NonNull List<String> mPendingOnDeviceLanguages;
+ private @NonNull List<String> mSupportedOnDeviceLanguages;
+ private @NonNull List<String> mOnlineLanguages;
private long mBuilderFieldsSet = 0L;
@@ -223,9 +379,12 @@ public final class RecognitionSupport implements Parcelable {
/**
* Support for this request is ready for use on this device for the returned languages.
+ *
+ * @deprecated See {@link #getInstalledOnDeviceLanguages()}.
*/
@DataClass.Generated.Member
- public @NonNull Builder setInstalledLanguages(@NonNull List<String> value) {
+ @Deprecated @NonNull
+ public Builder setInstalledLanguages(@NonNull @Deprecated List<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
mInstalledLanguages = value;
@@ -234,7 +393,8 @@ public final class RecognitionSupport implements Parcelable {
/** @see #setInstalledLanguages */
@DataClass.Generated.Member
- public @NonNull Builder addInstalledLanguage(@NonNull String value) {
+ @Deprecated @NonNull
+ public Builder addInstalledLanguage(@NonNull String value) {
if (mInstalledLanguages == null) setInstalledLanguages(new java.util.ArrayList<>());
mInstalledLanguages.add(value);
return this;
@@ -242,9 +402,12 @@ public final class RecognitionSupport implements Parcelable {
/**
* Support for this request is scheduled for download for the returned languages.
+ *
+ * @deprecated See {@link #getPendingOnDeviceLanguages()}.
*/
@DataClass.Generated.Member
- public @NonNull Builder setPendingLanguages(@NonNull List<String> value) {
+ @Deprecated @NonNull
+ public Builder setPendingLanguages(@NonNull @Deprecated List<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
mPendingLanguages = value;
@@ -253,17 +416,22 @@ public final class RecognitionSupport implements Parcelable {
/** @see #setPendingLanguages */
@DataClass.Generated.Member
- public @NonNull Builder addPendingLanguage(@NonNull String value) {
+ @Deprecated @NonNull
+ public Builder addPendingLanguage(@NonNull String value) {
if (mPendingLanguages == null) setPendingLanguages(new java.util.ArrayList<>());
mPendingLanguages.add(value);
return this;
}
/**
- * These languages are supported but need to be downloaded before use.
+ * These languages are supported but need to be downloaded before use. See {@link
+ * SpeechRecognizer#triggerModelDownload(Intent)}.
+ *
+ * @deprecated See {@link #getSupportedOnDeviceLanguages()}.
*/
@DataClass.Generated.Member
- public @NonNull Builder setSupportedLanguages(@NonNull List<String> value) {
+ @Deprecated @NonNull
+ public Builder setSupportedLanguages(@NonNull @Deprecated List<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mSupportedLanguages = value;
@@ -272,35 +440,131 @@ public final class RecognitionSupport implements Parcelable {
/** @see #setSupportedLanguages */
@DataClass.Generated.Member
- public @NonNull Builder addSupportedLanguage(@NonNull String value) {
+ @Deprecated @NonNull
+ public Builder addSupportedLanguage(@NonNull String value) {
if (mSupportedLanguages == null) setSupportedLanguages(new java.util.ArrayList<>());
mSupportedLanguages.add(value);
return this;
}
+ /**
+ * Support for this request is ready for use on this device for the returned languages.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setInstalledOnDeviceLanguages(@NonNull List<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mInstalledOnDeviceLanguages = value;
+ return this;
+ }
+
+ /** @see #setInstalledOnDeviceLanguages */
+ @DataClass.Generated.Member
+ public @NonNull Builder addInstalledOnDeviceLanguage(@NonNull String value) {
+ if (mInstalledOnDeviceLanguages == null) setInstalledOnDeviceLanguages(new java.util.ArrayList<>());
+ mInstalledOnDeviceLanguages.add(value);
+ return this;
+ }
+
+ /**
+ * Support for this request is scheduled for download for the returned languages.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setPendingOnDeviceLanguages(@NonNull List<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mPendingOnDeviceLanguages = value;
+ return this;
+ }
+
+ /** @see #setPendingOnDeviceLanguages */
+ @DataClass.Generated.Member
+ public @NonNull Builder addPendingOnDeviceLanguage(@NonNull String value) {
+ if (mPendingOnDeviceLanguages == null) setPendingOnDeviceLanguages(new java.util.ArrayList<>());
+ mPendingOnDeviceLanguages.add(value);
+ return this;
+ }
+
+ /**
+ * These languages are supported but need to be downloaded before use. See {@link
+ * SpeechRecognizer#triggerModelDownload(Intent)}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setSupportedOnDeviceLanguages(@NonNull List<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mSupportedOnDeviceLanguages = value;
+ return this;
+ }
+
+ /** @see #setSupportedOnDeviceLanguages */
+ @DataClass.Generated.Member
+ public @NonNull Builder addSupportedOnDeviceLanguage(@NonNull String value) {
+ if (mSupportedOnDeviceLanguages == null) setSupportedOnDeviceLanguages(new java.util.ArrayList<>());
+ mSupportedOnDeviceLanguages.add(value);
+ return this;
+ }
+
+ /**
+ * Support for this request is available via a remote implementation. {@link SpeechRecognizer}
+ * created via {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)} are expected to
+ * return an empty list.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setOnlineLanguages(@NonNull List<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mOnlineLanguages = value;
+ return this;
+ }
+
+ /** @see #setOnlineLanguages */
+ @DataClass.Generated.Member
+ public @NonNull Builder addOnlineLanguage(@NonNull String value) {
+ if (mOnlineLanguages == null) setOnlineLanguages(new java.util.ArrayList<>());
+ mOnlineLanguages.add(value);
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull RecognitionSupport build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
+ mBuilderFieldsSet |= 0x80; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
- mInstalledLanguages = null;
+ mInstalledLanguages = List.of();
}
if ((mBuilderFieldsSet & 0x2) == 0) {
- mPendingLanguages = null;
+ mPendingLanguages = List.of();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
- mSupportedLanguages = null;
+ mSupportedLanguages = List.of();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mInstalledOnDeviceLanguages = List.of();
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mPendingOnDeviceLanguages = List.of();
+ }
+ if ((mBuilderFieldsSet & 0x20) == 0) {
+ mSupportedOnDeviceLanguages = List.of();
+ }
+ if ((mBuilderFieldsSet & 0x40) == 0) {
+ mOnlineLanguages = List.of();
}
RecognitionSupport o = new RecognitionSupport(
mInstalledLanguages,
mPendingLanguages,
- mSupportedLanguages);
+ mSupportedLanguages,
+ mInstalledOnDeviceLanguages,
+ mPendingOnDeviceLanguages,
+ mSupportedOnDeviceLanguages,
+ mOnlineLanguages);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
+ if ((mBuilderFieldsSet & 0x80) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -308,10 +572,10 @@ public final class RecognitionSupport implements Parcelable {
}
@DataClass.Generated(
- time = 1644582623366L,
+ time = 1647890081869L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/speech/RecognitionSupport.java",
- inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"installedLanguage\") java.util.List<java.lang.String> mInstalledLanguages\nprivate @com.android.internal.util.DataClass.PluralOf(\"pendingLanguage\") @android.annotation.NonNull java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"supportedLanguage\") java.util.List<java.lang.String> mSupportedLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "private @android.annotation.NonNull @java.lang.Deprecated @com.android.internal.util.DataClass.PluralOf(\"installedLanguage\") java.util.List<java.lang.String> mInstalledLanguages\nprivate @android.annotation.NonNull @java.lang.Deprecated @com.android.internal.util.DataClass.PluralOf(\"pendingLanguage\") java.util.List<java.lang.String> mPendingLanguages\nprivate @android.annotation.NonNull @java.lang.Deprecated @com.android.internal.util.DataClass.PluralOf(\"supportedLanguage\") java.util.List<java.lang.String> mSupportedLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"installedOnDeviceLanguage\") java.util.List<java.lang.String> mInstalledOnDeviceLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"pendingOnDeviceLanguage\") java.util.List<java.lang.String> mPendingOnDeviceLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"supportedOnDeviceLanguage\") java.util.List<java.lang.String> mSupportedOnDeviceLanguages\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"onlineLanguage\") java.util.List<java.lang.String> mOnlineLanguages\nclass RecognitionSupport extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index c1fa0797b42a..60593ca46bcb 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -779,7 +779,9 @@ public final class Choreographer {
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
- frameData.setFrameTimeNanos(frameTimeNanos);
+ DisplayEventReceiver.VsyncEventData latestVsyncEventData =
+ mDisplayEventReceiver.getLatestVsyncEventData();
+ frameData.updateFrameData(frameTimeNanos, latestVsyncEventData);
}
if (frameTimeNanos < mLastFrameTimeNanos) {
@@ -877,7 +879,9 @@ public final class Choreographer {
}
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
- frameData.setFrameTimeNanos(frameTimeNanos);
+ DisplayEventReceiver.VsyncEventData latestVsyncEventData =
+ mDisplayEventReceiver.getLatestVsyncEventData();
+ frameData.updateFrameData(frameTimeNanos, latestVsyncEventData);
}
}
}
@@ -1012,11 +1016,6 @@ public final class Choreographer {
return mVsyncId;
}
- /** Sets the vsync ID. */
- void resetVsyncId() {
- mVsyncId = FrameInfo.INVALID_VSYNC_ID;
- }
-
/**
* The time in {@link System#nanoTime()} timebase which this frame is expected to be
* presented.
@@ -1061,17 +1060,15 @@ public final class Choreographer {
}
private long mFrameTimeNanos;
- private final FrameTimeline[] mFrameTimelines;
- private final FrameTimeline mPreferredFrameTimeline;
+ private FrameTimeline[] mFrameTimelines;
+ private FrameTimeline mPreferredFrameTimeline;
- void setFrameTimeNanos(long frameTimeNanos) {
+ void updateFrameData(long frameTimeNanos,
+ DisplayEventReceiver.VsyncEventData latestVsyncEventData) {
mFrameTimeNanos = frameTimeNanos;
- for (FrameTimeline ft : mFrameTimelines) {
- // The ID is no longer valid because the frame time that was registered with the ID
- // no longer matches.
- // TODO(b/205721584): Ask SF for valid vsync information.
- ft.resetVsyncId();
- }
+ mFrameTimelines = convertFrameTimelines(latestVsyncEventData);
+ mPreferredFrameTimeline =
+ mFrameTimelines[latestVsyncEventData.preferredFrameTimelineIndex];
}
/** The time in nanoseconds when the frame started being rendered. */
@@ -1091,6 +1088,19 @@ public final class Choreographer {
public FrameTimeline getPreferredFrameTimeline() {
return mPreferredFrameTimeline;
}
+
+ private FrameTimeline[] convertFrameTimelines(
+ DisplayEventReceiver.VsyncEventData vsyncEventData) {
+ FrameTimeline[] frameTimelines =
+ new FrameTimeline[vsyncEventData.frameTimelines.length];
+ for (int i = 0; i < vsyncEventData.frameTimelines.length; i++) {
+ DisplayEventReceiver.VsyncEventData.FrameTimeline frameTimeline =
+ vsyncEventData.frameTimelines[i];
+ frameTimelines[i] = new FrameTimeline(frameTimeline.vsyncId,
+ frameTimeline.expectedPresentTime, frameTimeline.deadline);
+ }
+ return frameTimelines;
+ }
}
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 7d8e99840fb1..5f0098c25e55 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -762,35 +762,42 @@ public final class Display {
}
/**
- * Gets the size of the display, in pixels.
- * Value returned by this method does not necessarily represent the actual raw size
- * (native resolution) of the display.
- * <p>
- * 1. The returned size may be adjusted to exclude certain system decor elements
- * that are always visible.
- * </p><p>
- * 2. It may be scaled to provide compatibility with older applications that
- * were originally designed for smaller displays.
- * </p><p>
- * 3. It can be different depending on the WindowManager to which the display belongs.
- * </p><p>
- * - If requested from non-Activity context (e.g. Application context via
- * {@code (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE)})
- * it will report the size of the entire display based on current rotation and with subtracted
- * system decoration areas.
- * </p><p>
- * - If requested from activity (either using {@code getWindowManager()} or
- * {@code (WindowManager) getSystemService(Context.WINDOW_SERVICE)}) resulting size will
- * correspond to current app window size. In this case it can be smaller than physical size in
- * multi-window mode.
- * </p><p>
- * Typically for the purposes of layout apps should make a request from activity context
- * to obtain size available for the app content.
- * </p>
- *
- * @param outSize A {@link Point} object to receive the size information.
- * @deprecated Use {@link WindowManager#getCurrentWindowMetrics()} to obtain an instance of
- * {@link WindowMetrics} and use {@link WindowMetrics#getBounds()} instead.
+ * Gets the size of the display in pixels.
+ *
+ * <p>The return value does not necessarily represent the actual size (native resolution) of the
+ * display. The returned size might be adjusted to exclude certain system decor elements that
+ * are always visible, or the size might be scaled to provide compatibility with older
+ * applications that were originally designed for smaller displays.
+ *
+ * <p>The returned size can also be different depending on the WindowManager bound to the
+ * display:
+ * <ul>
+ * <li>If size is requested from an activity (either using a WindowManager accessed by
+ * {@code getWindowManager()} or {@code getSystemService(Context.WINDOW_SERVICE)}), the
+ * size of the current app window is returned. As a result, in multi-window mode, the
+ * returned size can be smaller than the size of the device screen.
+ * <li>If size is requested from a non-activity context (for example, the application
+ * context, where the WindowManager is accessed by
+ * {@code getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}), the
+ * returned size can vary depending on API level:
+ * <ul>
+ * <li>API level 29 and below &mdash; The size of the entire display (based on
+ * current rotation) minus system decoration areas is returned.
+ * <li>API level 30 and above &mdash; The size of the top running activity in the
+ * current process is returned. If the current process has no running
+ * activities, the size of the device default display, including system
+ * decoration areas, is returned.
+ * </ul>
+ * </ul>
+ *
+ * <p>For layout purposes, apps should make a request from an activity context to obtain the
+ * size of the display area available for app content.
+ *
+ * @param outSize A {@link Point} object which receives the display size information.
+ *
+ * @deprecated Use {@link WindowMetrics} instead. Obtain a {@code WindowMetrics} instance by
+ * calling {@link WindowManager#getCurrentWindowMetrics()}, then call
+ * {@link WindowMetrics#getBounds()} to get the dimensions of the application window.
*/
@Deprecated
public void getSize(Point outSize) {
@@ -806,8 +813,9 @@ public final class Display {
* Gets the size of the display as a rectangle, in pixels.
*
* @param outSize A {@link Rect} object to receive the size information.
+ *
* @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application
- * window area.
+ * window.
*/
@Deprecated
public void getRectSize(Rect outSize) {
@@ -1379,32 +1387,39 @@ public final class Display {
}
/**
- * Gets display metrics that describe the size and density of this display.
- * The size returned by this method does not necessarily represent the
- * actual raw size (native resolution) of the display.
- * <p>
- * 1. The returned size may be adjusted to exclude certain system decor elements
- * that are always visible.
- * </p><p>
- * 2. It may be scaled to provide compatibility with older applications that
- * were originally designed for smaller displays.
- * </p><p>
- * 3. It can be different depending on the WindowManager to which the display belongs.
- * </p><p>
- * - If requested from non-Activity context (e.g. Application context via
- * {@code (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE)})
- * metrics will report the size of the entire display based on current rotation and with
- * subtracted system decoration areas.
- * </p><p>
- * - If requested from activity (either using {@code getWindowManager()} or
- * {@code (WindowManager) getSystemService(Context.WINDOW_SERVICE)}) resulting metrics will
- * correspond to current app window metrics. In this case the size can be smaller than physical
- * size in multi-window mode.
- * </p>
+ * Gets the size and density of this display.
+ *
+ * <p>The size returned does not necessarily represent the actual size (native resolution) of
+ * the display. The returned size might be adjusted to exclude certain system decor elements
+ * that are always visible, or the size might be scaled to provide compatibility with older
+ * applications that were originally designed for smaller displays.
+ *
+ * <p>The returned size can also be different depending on the WindowManager associated with the
+ * display:
+ * <ul>
+ * <li>If metrics are requested from an activity (either using a WindowManager accessed by
+ * {@code getWindowManager()} or {@code getSystemService(Context.WINDOW_SERVICE)}), the
+ * returned metrics provide the size of the current app window. As a result, in
+ * multi-window mode, the returned size can be smaller than the size of the device
+ * screen.
+ * <li>If metrics are requested from a non-activity context (for example, the application
+ * context, where the WindowManager is accessed by
+ * {@code getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}), the
+ * returned size can vary depending on API level:
+ * <ul>
+ * <li>API level 29 and below &mdash; The returned metrics provide the size of the
+ * entire display (based on current rotation) minus system decoration areas.
+ * <li>API level 30 and above &mdash; The returned metrics provide the size of the
+ * top running activity in the current process. If the current process has no
+ * running activities, the metrics provide the size of the default display of
+ * the device, including system decoration areas.
+ * </ul>
+ * </ul>
+ *
+ * @param outMetrics A {@link DisplayMetrics} object which receives the display metrics.
*
- * @param outMetrics A {@link DisplayMetrics} object to receive the metrics.
* @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application
- * window area, and {@link Configuration#densityDpi} to get the current density.
+ * window. Use {@link Configuration#densityDpi} to get the display density.
*/
@Deprecated
public void getMetrics(DisplayMetrics outMetrics) {
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 774bab41fb9a..3a74b2ed63a7 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -84,6 +84,7 @@ public abstract class DisplayEventReceiver {
private static native void nativeDispose(long receiverPtr);
@FastNative
private static native void nativeScheduleVsync(long receiverPtr);
+ private static native VsyncEventData nativeGetLatestVsyncEventData(long receiverPtr);
/**
* Creates a display event receiver.
@@ -279,6 +280,13 @@ public abstract class DisplayEventReceiver {
}
}
+ /**
+ * Gets the latest vsync event data from surface flinger.
+ */
+ VsyncEventData getLatestVsyncEventData() {
+ return nativeGetLatestVsyncEventData(mReceiverPtr);
+ }
+
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index fd8690009a6d..70986024bc5b 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -107,6 +107,41 @@ interface IWindowSession {
out InsetsState insetsState, out InsetsSourceControl[] activeControls,
out Bundle bundle);
+ /**
+ * Changes the view visibility and the attributes of a window. This should only be called when
+ * the visibility of the root view is changed. This returns a valid surface if the root view is
+ * visible. This also returns the latest information for the caller to compute its window frame.
+ *
+ * @param window The window being updated.
+ * @param attrs If non-null, new attributes to apply to the window.
+ * @param viewVisibility Window root view's visibility.
+ * @param outMergedConfiguration New config container that holds global, override and merged
+ * config for window, if it is now becoming visible and the merged configuration has changed
+ * since it was last displayed.
+ * @param outSurfaceControl Object in which is placed the new display surface.
+ * @param outInsetsState The current insets state in the system.
+ * @param outActiveControls The insets source controls for the caller to override the insets
+ * state in the system.
+ *
+ * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
+ */
+ int updateVisibility(IWindow window, in WindowManager.LayoutParams attrs, int viewVisibility,
+ out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
+ out InsetsState outInsetsState, out InsetsSourceControl[] outActiveControls);
+
+ /**
+ * Reports the layout results and the attributes of a window to the server.
+ *
+ * @param window The window being reported.
+ * @param attrs If non-null, new attributes to apply to the window.
+ * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}.
+ * @param clientFrames the window frames computed by the client.
+ * @param requestedWidth The width the window wants to be.
+ * @param requestedHeight The height the window wants to be.
+ */
+ oneway void updateLayout(IWindow window, in WindowManager.LayoutParams attrs, int flags,
+ in ClientWindowFrames clientFrames, int requestedWidth, int requestedHeight);
+
/*
* Notify the window manager that an application is relaunching and
* windows should be prepared for replacement.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1b8dc706371e..febd2e2efdac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14649,6 +14649,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int selectionStart;
int selectionEnd;
if (extendSelection && isAccessibilitySelectionExtendable()) {
+ prepareForExtendedAccessibilitySelection();
selectionStart = getAccessibilitySelectionStart();
if (selectionStart == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
selectionStart = forward ? segmentStart : segmentEnd;
@@ -14688,6 +14689,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Prepare for extended selection.
+ * @hide
+ */
+ public void prepareForExtendedAccessibilitySelection() {
+ return;
+ }
+
+ /**
* @hide
*/
public int getAccessibilitySelectionStart() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 172cd03900e7..2613c1a2992f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -78,11 +78,13 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_E
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_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
@@ -279,6 +281,13 @@ public final class ViewRootImpl implements ViewParent,
public static final boolean CAPTION_ON_SHELL = false;
/**
+ * Whether the client should compute the window frame on its own.
+ * @hide
+ */
+ public static final boolean LOCAL_LAYOUT =
+ SystemProperties.getBoolean("persist.debug.local_layout", false);
+
+ /**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
@@ -8083,17 +8092,68 @@ public final class ViewRootImpl implements ViewParent,
final int requestedWidth = (int) (mView.getMeasuredWidth() * appScale + 0.5f);
final int requestedHeight = (int) (mView.getMeasuredHeight() * appScale + 0.5f);
- int relayoutResult = mWindowSession.relayout(mWindow, params,
- requestedWidth, requestedHeight, viewVisibility,
- insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
- mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
- mTempControls, mRelayoutBundle);
- mSyncSeqId = mRelayoutBundle.getInt("seqid");
+ mWillMove = false;
+ mWillResize = false;
+ int relayoutResult = 0;
+ WindowConfiguration winConfig = getConfiguration().windowConfiguration;
+ if (LOCAL_LAYOUT) {
+ if (mFirst || viewVisibility != mViewVisibility) {
+ relayoutResult = mWindowSession.updateVisibility(mWindow, params, viewVisibility,
+ mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls);
+ if (mTranslator != null) {
+ mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
+ mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
+ }
+ mInsetsController.onStateChanged(mTempInsets);
+ mInsetsController.onControlsChanged(mTempControls);
+
+ mPendingAlwaysConsumeSystemBars =
+ (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
+ }
+ final InsetsState state = mInsetsController.getState();
+ final Rect displayCutoutSafe = mTempRect;
+ state.getDisplayCutoutSafe(displayCutoutSafe);
+ if (mWindowAttributes.type == TYPE_APPLICATION_STARTING) {
+ // TODO(b/210378379): Remove the special logic.
+ // Letting starting window use the window bounds from the pending config is for the
+ // fixed rotation, because the config is not overridden before the starting window
+ // is created.
+ winConfig = mPendingMergedConfiguration.getMergedConfiguration()
+ .windowConfiguration;
+ }
+ mWindowLayout.computeFrames(mWindowAttributes, state, displayCutoutSafe,
+ winConfig.getBounds(), winConfig.getWindowingMode(), requestedWidth,
+ requestedHeight, mInsetsController.getRequestedVisibilities(),
+ getAttachedWindowFrame(), 1f /* compatScale */, mTmpFrames);
+
+ mWindowSession.updateLayout(mWindow, params,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mTmpFrames,
+ requestedWidth, requestedHeight);
+
+ } else {
+ relayoutResult = mWindowSession.relayout(mWindow, params,
+ requestedWidth, requestedHeight, viewVisibility,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+ mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
+ mTempControls, mRelayoutBundle);
+ mSyncSeqId = mRelayoutBundle.getInt("seqid");
+
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
+ mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
+ mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
+ }
+ mInsetsController.onStateChanged(mTempInsets);
+ mInsetsController.onControlsChanged(mTempControls);
+
+ mPendingAlwaysConsumeSystemBars =
+ (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
+ }
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
- final WindowConfiguration winConfig = getConfiguration().windowConfiguration;
WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth,
requestedHeight, mTmpFrames.frame, mPendingDragResizing, mSurfaceSize);
@@ -8142,24 +8202,10 @@ public final class ViewRootImpl implements ViewParent,
destroySurface();
}
- mPendingAlwaysConsumeSystemBars =
- (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
-
if (restore) {
params.restore();
}
-
- if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
- mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
- mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
- }
setFrame(mTmpFrames.frame);
- mWillMove = false;
- mWillResize = false;
- mInsetsController.onStateChanged(mTempInsets);
- mInsetsController.onControlsChanged(mTempControls);
return relayoutResult;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 06588b2cbb6d..385a80d7c32c 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -336,6 +336,21 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
+ public int updateVisibility(IWindow window, WindowManager.LayoutParams inAttrs,
+ int viewVisibility, MergedConfiguration outMergedConfiguration,
+ SurfaceControl outSurfaceControl, InsetsState outInsetsState,
+ InsetsSourceControl[] outActiveControls) {
+ // TODO(b/161810301): Finish the implementation.
+ return 0;
+ }
+
+ @Override
+ public void updateLayout(IWindow window, WindowManager.LayoutParams inAttrs, int flags,
+ ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
+ // TODO(b/161810301): Finish the implementation.
+ }
+
+ @Override
public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) {
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 2c81eb1ae306..07db91f31efc 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,7 +26,6 @@ import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,22 +46,17 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.service.autofill.AutofillService;
-import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
-import android.service.autofill.IFillCallback;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -82,7 +76,6 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@@ -107,7 +100,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.Executor;
import sun.misc.Cleaner;
@@ -176,12 +168,6 @@ import sun.misc.Cleaner;
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
- * <p>There is another choice for the application to provide it's datasets to the Autofill framework
- * by setting an {@link AutofillRequestCallback} through
- * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
- * its callback instead of the default {@link AutofillService}. See
- * {@link AutofillRequestCallback} for more details.
- *
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -307,7 +293,6 @@ public final class AutofillManager {
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
- /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -637,11 +622,6 @@ public final class AutofillManager {
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
- @GuardedBy("mLock")
- @Nullable private AutofillRequestCallback mAutofillRequestCallback;
- @GuardedBy("mLock")
- @Nullable private Executor mRequestCallbackExecutor;
-
/**
* Indicates whether there are any fields that need to do a fill request
* after the activity starts.
@@ -1955,32 +1935,6 @@ public final class AutofillManager {
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
- /**
- * Sets the client's suggestions callback for autofill.
- *
- * @see AutofillRequestCallback
- *
- * @param executor specifies the thread upon which the callbacks will be invoked.
- * @param callback which handles autofill request to provide client's suggestions.
- */
- public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull AutofillRequestCallback callback) {
- synchronized (mLock) {
- mRequestCallbackExecutor = executor;
- mAutofillRequestCallback = callback;
- }
- }
-
- /**
- * clears the client's suggestions callback for autofill.
- */
- public void clearAutofillRequestCallback() {
- synchronized (mLock) {
- mRequestCallbackExecutor = null;
- mAutofillRequestCallback = null;
- }
- }
-
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -2041,13 +1995,6 @@ public final class AutofillManager {
}
}
- if (mAutofillRequestCallback != null) {
- if (sDebug) {
- Log.d(TAG, "startSession with the client suggestions provider");
- }
- flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
- }
-
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, clientActivity,
@@ -2399,28 +2346,6 @@ public final class AutofillManager {
}
}
- private void onFillRequest(InlineSuggestionsRequest request,
- CancellationSignal cancellationSignal, FillCallback callback) {
- final AutofillRequestCallback autofillRequestCallback;
- final Executor executor;
- synchronized (mLock) {
- autofillRequestCallback = mAutofillRequestCallback;
- executor = mRequestCallbackExecutor;
- }
- if (autofillRequestCallback != null && executor != null) {
- final long ident = Binder.clearCallingIdentity();
- try {
- executor.execute(() ->
- autofillRequestCallback.onFillRequest(
- request, cancellationSignal, callback));
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- } else {
- callback.onSuccess(null);
- }
- }
-
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -3870,23 +3795,6 @@ public final class AutofillManager {
}
}
- @Override
- public void requestFillFromClient(int id, InlineSuggestionsRequest request,
- IFillCallback callback) {
- final AutofillManager afm = mAfm.get();
- if (afm != null) {
- ICancellationSignal transport = CancellationSignal.createTransport();
- try {
- callback.onCancellable(transport);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error requesting a cancellation", e);
- }
-
- afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
- new FillCallback(callback, id));
- }
- }
-
public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
deleted file mode 100644
index e632a5849471..000000000000
--- a/core/java/android/view/autofill/AutofillRequestCallback.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.view.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.CancellationSignal;
-import android.service.autofill.FillCallback;
-import android.view.inputmethod.InlineSuggestionsRequest;
-
-/**
- * <p>This class is used to provide some input suggestions to the Autofill framework.
- *
- * <P>When the user is requested to input something, Autofill will try to query input suggestions
- * for the user choosing. If the application want to provide some internal input suggestions,
- * implements this callback and register via
- * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
- * AutofillRequestCallback)}. Autofill will callback the
- * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
- * input suggestions.
- *
- * <P>To make sure the callback to take effect, must register before the autofill session starts.
- * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
- * session, and then the callback will be used at the next restarted session.
- *
- * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
- * {@link AutofillId}s from its view structure. Below is an example:
- * <pre class="prettyprint">
- * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
- * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
- * </pre>
- * To learn more about creating a {@link android.service.autofill.FillResponse}, read
- * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
- *
- * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
- * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
- * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
- * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
- * client would like to keep no suggestions for the field, respond with an empty
- * {@link android.service.autofill.FillResponse} which has no dataset.
- *
- * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
- * the keyboard may choose to block your app from the inline strip.
- */
-public interface AutofillRequestCallback {
- /**
- * Called by the Android system to decide if a screen can be autofilled by the callback.
- *
- * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
- * currently inline suggestions are supported and can be displayed.
- * @param cancellationSignal signal for observing cancellation requests. The system will use
- * this to notify you that the fill result is no longer needed and you should stop
- * handling this fill request in order to save resources.
- * @param callback object used to notify the result of the request.
- */
- void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
-}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 2e5967cc32d1..51afe4cf784d 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,11 +24,9 @@ import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
-import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -144,12 +142,6 @@ oneway interface IAutoFillManagerClient {
void requestShowSoftInput(in AutofillId id);
/**
- * Requests to determine if a screen can be autofilled by the client app.
- */
- void requestFillFromClient(int id, in InlineSuggestionsRequest request,
- in IFillCallback callback);
-
- /**
* Notifies autofill ids that require to show the fill dialog.
*/
void notifyFillDialogTriggerIds(in List<AutofillId> ids);
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 70279cc8e845..c78b810f0b1f 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,22 +111,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
/**
- * Whether the IME supports inline suggestions from the default Autofill service that
- * provides the input view.
- *
- * Note: The default value is {@code true}.
- */
- private boolean mServiceSupported;
-
- /**
- * Whether the IME supports inline suggestions from the application that provides the
- * input view.
- *
- * Note: The default value is {@code true}.
- */
- private boolean mClientSupported;
-
- /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -220,14 +204,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
return Bundle.EMPTY;
}
- private static boolean defaultServiceSupported() {
- return true;
- }
-
- private static boolean defaultClientSupported() {
- return true;
- }
-
/** @hide */
abstract static class BaseBuilder {
abstract Builder setInlinePresentationSpecs(
@@ -240,25 +216,15 @@ public final class InlineSuggestionsRequest implements Parcelable {
abstract Builder setHostDisplayId(int value);
}
- /** @hide */
- public boolean isServiceSupported() {
- return mServiceSupported;
- }
-
- /** @hide */
- public boolean isClientSupported() {
- return mClientSupported;
- }
-
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+ // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -274,9 +240,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
@NonNull Bundle extras,
@Nullable IBinder hostInputToken,
int hostDisplayId,
- @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
- boolean serviceSupported,
- boolean clientSupported) {
+ @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -293,8 +257,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
- this.mServiceSupported = serviceSupported;
- this.mClientSupported = clientSupported;
onConstructed();
}
@@ -378,9 +340,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
/**
- * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
- *
- * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
+ * Specifies the UI specification for the inline suggestion tooltip in the response.
*/
@DataClass.Generated.Member
public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -401,9 +361,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
"extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
"hostDisplayId = " + mHostDisplayId + ", " +
- "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
- "serviceSupported = " + mServiceSupported + ", " +
- "clientSupported = " + mClientSupported +
+ "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
" }";
}
@@ -427,9 +385,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
&& extrasEquals(that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
&& mHostDisplayId == that.mHostDisplayId
- && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
- && mServiceSupported == that.mServiceSupported
- && mClientSupported == that.mClientSupported;
+ && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
}
@Override
@@ -447,8 +403,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + mHostDisplayId;
_hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
- _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
- _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
return _hash;
}
@@ -459,8 +413,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
int flg = 0;
- if (mServiceSupported) flg |= 0x100;
- if (mClientSupported) flg |= 0x200;
if (mHostInputToken != null) flg |= 0x20;
if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
dest.writeInt(flg);
@@ -486,8 +438,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
int flg = in.readInt();
- boolean serviceSupported = (flg & 0x100) != 0;
- boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class);
@@ -514,8 +464,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
- this.mServiceSupported = serviceSupported;
- this.mClientSupported = clientSupported;
onConstructed();
}
@@ -549,8 +497,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @Nullable IBinder mHostInputToken;
private int mHostDisplayId;
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
- private boolean mServiceSupported;
- private boolean mClientSupported;
private long mBuilderFieldsSet = 0L;
@@ -683,9 +629,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
/**
- * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
- *
- * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
+ * Specifies the UI specification for the inline suggestion tooltip in the response.
*/
@DataClass.Generated.Member
public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -695,38 +639,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
return this;
}
- /**
- * Whether the IME supports inline suggestions from the default Autofill service that
- * provides the input view.
- *
- * Note: The default value is {@code true}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setServiceSupported(boolean value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x100;
- mServiceSupported = value;
- return this;
- }
-
- /**
- * Whether the IME supports inline suggestions from the application that provides the
- * input view.
- *
- * Note: The default value is {@code true}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setClientSupported(boolean value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x200;
- mClientSupported = value;
- return this;
- }
-
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x400; // Mark builder used
+ mBuilderFieldsSet |= 0x100; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -749,12 +665,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
if ((mBuilderFieldsSet & 0x80) == 0) {
mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
}
- if ((mBuilderFieldsSet & 0x100) == 0) {
- mServiceSupported = defaultServiceSupported();
- }
- if ((mBuilderFieldsSet & 0x200) == 0) {
- mClientSupported = defaultClientSupported();
- }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mInlinePresentationSpecs,
@@ -763,14 +673,12 @@ public final class InlineSuggestionsRequest implements Parcelable {
mExtras,
mHostInputToken,
mHostDisplayId,
- mInlineTooltipPresentationSpec,
- mServiceSupported,
- mClientSupported);
+ mInlineTooltipPresentationSpec);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x400) != 0) {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -778,10 +686,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1615798784918L,
- codegenVersion = "1.0.22",
+ time = 1621415989607L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3c8fcb978fbd..6f83a45ce7c6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12363,9 +12363,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
// A view should not be exposed as clickable/long-clickable to a service because of a
- // LinkMovementMethod.
+ // LinkMovementMethod or because it has selectable and non-editable text.
if ((info.isClickable() || info.isLongClickable())
- && mMovement instanceof LinkMovementMethod) {
+ && (mMovement instanceof LinkMovementMethod
+ || (isTextSelectable() && !isTextEditable()))) {
if (!hasOnClickListeners()) {
info.setClickable(false);
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
@@ -12597,11 +12598,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return true;
}
if (start >= 0 && start <= end && end <= text.length()) {
+ requestFocusOnNonEditableSelectableText();
Selection.setSelection((Spannable) text, start, end);
- // Make sure selection mode is engaged.
- if (mEditor != null) {
- mEditor.startSelectionActionModeAsync(false);
- }
+ hideAccessibilitySelectionControllers();
return true;
}
}
@@ -12695,6 +12694,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return handled;
}
+ private void requestFocusOnNonEditableSelectableText() {
+ if (!isTextEditable() && isTextSelectable()) {
+ if (!isEnabled()) {
+ return;
+ }
+
+ if (isFocusable() && !isFocused()) {
+ requestFocus();
+ }
+ }
+ }
+
private boolean hasSpannableText() {
return mText != null && mText instanceof Spannable;
}
@@ -12723,9 +12734,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns the text that should be exposed to accessibility services.
* <p>
- * This approximates what is displayed visually. If the user has specified
- * that accessibility services should speak passwords, this method will
- * bypass any password transformation method and return unobscured text.
+ * This approximates what is displayed visually.
*
* @return the text that should be exposed to accessibility services, may
* be {@code null} if no text is set
@@ -13706,6 +13715,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* @hide
*/
+ public void prepareForExtendedAccessibilitySelection() {
+ requestFocusOnNonEditableSelectableText();
+ }
+
+ /**
+ * @hide
+ */
@Override
public int getAccessibilitySelectionEnd() {
return getSelectionEnd();
@@ -13727,8 +13743,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Selection.removeSelection((Spannable) text);
}
// Hide all selection controllers used for adjusting selection
- // since we are doing so explicitlty by other means and these
+ // since we are doing so explicitly by other means and these
// controllers interact with how selection behaves.
+ hideAccessibilitySelectionControllers();
+ }
+
+ private void hideAccessibilitySelectionControllers() {
if (mEditor != null) {
mEditor.hideCursorAndSpanControllers();
mEditor.stopTextActionMode();
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 7718a3b7fc67..51da61ffb9c9 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -344,7 +344,7 @@ public final class TransitionInfo implements Parcelable {
if (parentChg.getMode() != TRANSIT_CHANGE) return false;
// If there are no more parents left, then all the parents, so far, have not been
- // visibility changes which means this change is indpendent.
+ // visibility changes which means this change is independent.
if (parentChg.getParent() == null) return true;
parentChg = info.getChange(parentChg.getParent());
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 377dfd0fb137..fd8534d45b2b 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -29,6 +29,8 @@ import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -60,6 +62,7 @@ import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import com.android.internal.R;
+import com.android.internal.protolog.common.ProtoLog;
import java.util.List;
@@ -295,9 +298,9 @@ public class TransitionAnimation {
@Nullable
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (mDebug) {
- Slog.v(mTag, "Loading animations: layout params pkg="
- + (lp != null ? lp.packageName : null)
- + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: layout params pkg=%s resId=0x%x",
+ lp != null ? lp.packageName : null,
+ lp != null ? lp.windowAnimations : 0);
}
if (lp != null && lp.windowAnimations != 0) {
// If this is a system resource, don't try to load it from the
@@ -309,7 +312,7 @@ public class TransitionAnimation {
packageName = DEFAULT_PACKAGE;
}
if (mDebug) {
- Slog.v(mTag, "Loading animations: picked package=" + packageName);
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: picked package=%s", packageName);
}
return AttributeCache.instance().get(packageName, resId,
com.android.internal.R.styleable.WindowAnimation);
@@ -320,16 +323,16 @@ public class TransitionAnimation {
@Nullable
private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
if (mDebug) {
- Slog.v(mTag, "Loading animations: package="
- + packageName + " resId=0x" + Integer.toHexString(resId));
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: package=%s resId=0x%x",
+ packageName, resId);
}
if (packageName != null) {
if ((resId & 0xFF000000) == 0x01000000) {
packageName = DEFAULT_PACKAGE;
}
if (mDebug) {
- Slog.v(mTag, "Loading animations: picked package="
- + packageName);
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: picked package=%s",
+ packageName);
}
return AttributeCache.instance().get(packageName, resId,
com.android.internal.R.styleable.WindowAnimation);
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 45c6d5f10a5a..7f36c79591b3 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -60,6 +60,7 @@ public enum ProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM),
WM_DEBUG_APP_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
+ WM_DEBUG_ANIM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
WM_DEBUG_APP_TRANSITIONS_ANIM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
WM_DEBUG_RECENTS_ANIMATIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 299cbe12b4d1..d1156979ee07 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
@@ -35,6 +36,8 @@ import android.view.RemotableViewMethod;
import android.widget.ImageView;
import android.widget.RemoteViews;
+import com.android.internal.R;
+
import java.util.Objects;
import java.util.function.Consumer;
@@ -55,9 +58,42 @@ public class CachingIconView extends ImageView {
private int mBackgroundColor;
private boolean mWillBeForceHidden;
+ private int mMaxDrawableWidth = -1;
+ private int mMaxDrawableHeight = -1;
+
+ public CachingIconView(Context context) {
+ this(context, null, 0, 0);
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public CachingIconView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0, 0);
+ }
+
+ public CachingIconView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public CachingIconView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ if (attrs == null) {
+ return;
+ }
+
+ TypedArray ta = context.obtainStyledAttributes(attrs,
+ R.styleable.CachingIconView, defStyleAttr, defStyleRes);
+ mMaxDrawableWidth = ta.getDimensionPixelSize(R.styleable
+ .CachingIconView_maxDrawableWidth, -1);
+ mMaxDrawableHeight = ta.getDimensionPixelSize(R.styleable
+ .CachingIconView_maxDrawableHeight, -1);
+ ta.recycle();
}
@Override
@@ -66,15 +102,27 @@ public class CachingIconView extends ImageView {
if (!testAndSetCache(icon)) {
mInternalSetDrawable = true;
// This calls back to setImageDrawable, make sure we don't clear the cache there.
- super.setImageIcon(icon);
+ Drawable drawable = loadSizeRestrictedIcon(icon);
+ if (drawable == null) {
+ super.setImageIcon(icon);
+ } else {
+ super.setImageDrawable(drawable);
+ }
mInternalSetDrawable = false;
}
}
+ @Nullable
+ private Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
+ return LocalImageResolver.resolveImage(icon, getContext(), mMaxDrawableWidth,
+ mMaxDrawableHeight);
+ }
+
@Override
- public Runnable setImageIconAsync(@Nullable Icon icon) {
+ public Runnable setImageIconAsync(@Nullable final Icon icon) {
resetCache();
- return super.setImageIconAsync(icon);
+ Drawable drawable = loadSizeRestrictedIcon(icon);
+ return () -> setImageDrawable(drawable);
}
@Override
@@ -83,14 +131,30 @@ public class CachingIconView extends ImageView {
if (!testAndSetCache(resId)) {
mInternalSetDrawable = true;
// This calls back to setImageDrawable, make sure we don't clear the cache there.
- super.setImageResource(resId);
+ Drawable drawable = loadSizeRestrictedDrawable(resId);
+ if (drawable == null) {
+ super.setImageResource(resId);
+ } else {
+ super.setImageDrawable(drawable);
+ }
mInternalSetDrawable = false;
}
}
+ @Nullable
+ private Drawable loadSizeRestrictedDrawable(@DrawableRes int resId) {
+ return LocalImageResolver.resolveImage(resId, getContext(), mMaxDrawableWidth,
+ mMaxDrawableHeight);
+ }
+
@Override
public Runnable setImageResourceAsync(@DrawableRes int resId) {
resetCache();
+ Drawable drawable = loadSizeRestrictedDrawable(resId);
+ if (drawable != null) {
+ return () -> setImageDrawable(drawable);
+ }
+
return super.setImageResourceAsync(resId);
}
@@ -98,13 +162,31 @@ public class CachingIconView extends ImageView {
@RemotableViewMethod(asyncImpl="setImageURIAsync")
public void setImageURI(@Nullable Uri uri) {
resetCache();
- super.setImageURI(uri);
+ Drawable drawable = loadSizeRestrictedUri(uri);
+ if (drawable == null) {
+ super.setImageURI(uri);
+ } else {
+ mInternalSetDrawable = true;
+ super.setImageDrawable(drawable);
+ mInternalSetDrawable = false;
+ }
+ }
+
+ @Nullable
+ private Drawable loadSizeRestrictedUri(@Nullable Uri uri) {
+ return LocalImageResolver.resolveImage(uri, getContext(), mMaxDrawableWidth,
+ mMaxDrawableHeight);
}
@Override
public Runnable setImageURIAsync(@Nullable Uri uri) {
resetCache();
- return super.setImageURIAsync(uri);
+ Drawable drawable = loadSizeRestrictedUri(uri);
+ if (drawable == null) {
+ return super.setImageURIAsync(uri);
+ } else {
+ return () -> setImageDrawable(drawable);
+ }
}
@Override
@@ -307,4 +389,18 @@ public class CachingIconView extends ImageView {
public void setWillBeForceHidden(boolean forceHidden) {
mWillBeForceHidden = forceHidden;
}
+
+ /**
+ * Returns the set maximum width of drawable in pixels. -1 if not set.
+ */
+ public int getMaxDrawableWidth() {
+ return mMaxDrawableWidth;
+ }
+
+ /**
+ * Returns the set maximum height of drawable in pixels. -1 if not set.
+ */
+ public int getMaxDrawableHeight() {
+ return mMaxDrawableHeight;
+ }
}
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index 616b69961b79..ce27b346567b 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -16,70 +16,195 @@
package com.android.internal.widget;
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.util.Log;
import android.util.Size;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */
public class LocalImageResolver {
- private static final String TAG = LocalImageResolver.class.getSimpleName();
- private static final int MAX_SAFE_ICON_SIZE_PX = 480;
+ private static final String TAG = "LocalImageResolver";
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480;
/**
- * Resolve an image from the given Uri using {@link ImageDecoder}
+ * Resolve an image from the given Uri using {@link ImageDecoder} if it contains a
+ * bitmap reference.
*/
+ @Nullable
public static Drawable resolveImage(Uri uri, Context context) throws IOException {
- final ImageDecoder.Source source =
- ImageDecoder.createSource(context.getContentResolver(), uri);
- final Drawable drawable =
- ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded);
- return drawable;
+ try {
+ final ImageDecoder.Source source =
+ ImageDecoder.createSource(context.getContentResolver(), uri);
+ return ImageDecoder.decodeDrawable(source,
+ (decoder, info, s) -> LocalImageResolver.onHeaderDecoded(decoder, info,
+ DEFAULT_MAX_SAFE_ICON_SIZE_PX, DEFAULT_MAX_SAFE_ICON_SIZE_PX));
+ } catch (Exception e) {
+ // Invalid drawable resource can actually throw either NullPointerException or
+ // ResourceNotFoundException. This sanitizes to expected output.
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Get the drawable from Icon using {@link ImageDecoder} if it contains a bitmap reference, or
+ * using {@link Icon#loadDrawable(Context)} otherwise. This will correctly apply the Icon's,
+ * tint, if present, to the drawable.
+ *
+ * @return drawable or null if loading failed.
+ */
+ @Nullable
+ public static Drawable resolveImage(@Nullable Icon icon, Context context) throws IOException {
+ return resolveImage(icon, context, DEFAULT_MAX_SAFE_ICON_SIZE_PX,
+ DEFAULT_MAX_SAFE_ICON_SIZE_PX);
}
/**
- * Get the drawable from Icon using {@link ImageDecoder} if it contains a Uri, or
+ * Get the drawable from Icon using {@link ImageDecoder} if it contains a bitmap reference, or
* using {@link Icon#loadDrawable(Context)} otherwise. This will correctly apply the Icon's,
* tint, if present, to the drawable.
+ *
+ * @throws IOException if the icon could not be loaded for whichever reason
*/
- public static Drawable resolveImage(Icon icon, Context context) throws IOException {
- Uri uri = getResolvableUri(icon);
- if (uri != null) {
- Drawable result = resolveImage(uri, context);
- if (icon.hasTint()) {
- result.mutate();
- result.setTintList(icon.getTintList());
- result.setTintBlendMode(icon.getTintBlendMode());
- }
- return result;
+ @Nullable
+ public static Drawable resolveImage(@Nullable Icon icon, Context context, int maxWidth,
+ int maxHeight) {
+ if (icon == null) {
+ return null;
+ }
+
+ switch (icon.getType()) {
+ case Icon.TYPE_URI:
+ case Icon.TYPE_URI_ADAPTIVE_BITMAP:
+ Uri uri = getResolvableUri(icon);
+ if (uri != null) {
+ Drawable result = resolveImage(uri, context, maxWidth, maxHeight);
+ if (result != null) {
+ return tintDrawable(icon, result);
+ }
+ }
+ break;
+ case Icon.TYPE_RESOURCE:
+ Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight);
+ if (result != null) {
+ return tintDrawable(icon, result);
+ }
+ break;
+ case Icon.TYPE_BITMAP:
+ case Icon.TYPE_ADAPTIVE_BITMAP:
+ return resolveBitmapImage(icon, context, maxWidth, maxHeight);
+ case Icon.TYPE_DATA: // We can't really improve on raw data images.
+ default:
+ break;
+ }
+
+ // Fallback to straight drawable load if we fail with more efficient approach.
+ try {
+ return icon.loadDrawable(context);
+ } catch (Resources.NotFoundException e) {
+ return null;
}
- return icon.loadDrawable(context);
}
- public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight)
- throws IOException {
+ /**
+ * Attempts to resolve the resource as a bitmap drawable constrained within max sizes.
+ */
+ @Nullable
+ public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight) {
final ImageDecoder.Source source =
ImageDecoder.createSource(context.getContentResolver(), uri);
- return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> {
- final Size size = info.getSize();
- if (size.getWidth() > size.getHeight()) {
- if (size.getWidth() > maxWidth) {
- final int targetHeight = size.getHeight() * maxWidth / size.getWidth();
- decoder.setTargetSize(maxWidth, targetHeight);
+ return resolveImage(source, maxWidth, maxHeight);
+ }
+
+ /**
+ * Attempts to resolve the resource as a bitmap drawable constrained within max sizes.
+ *
+ * @return decoded drawable or null if the passed resource is not a straight bitmap
+ */
+ @Nullable
+ public static Drawable resolveImage(@DrawableRes int resId, Context context, int maxWidth,
+ int maxHeight) {
+ final ImageDecoder.Source source = ImageDecoder.createSource(context.getResources(), resId);
+ return resolveImage(source, maxWidth, maxHeight);
+ }
+
+ @Nullable
+ private static Drawable resolveBitmapImage(Icon icon, Context context, int maxWidth,
+ int maxHeight) {
+ Bitmap bitmap = icon.getBitmap();
+ if (bitmap == null) {
+ return null;
+ }
+
+ if (bitmap.getWidth() > maxWidth || bitmap.getHeight() > maxHeight) {
+ Icon smallerIcon = icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP
+ ? Icon.createWithAdaptiveBitmap(bitmap) : Icon.createWithBitmap(bitmap);
+ // We don't want to modify the source icon, create a copy.
+ smallerIcon.setTintList(icon.getTintList())
+ .setTintBlendMode(icon.getTintBlendMode())
+ .scaleDownIfNecessary(maxWidth, maxHeight);
+ return smallerIcon.loadDrawable(context);
+ }
+
+ return icon.loadDrawable(context);
+ }
+
+ @Nullable
+ private static Drawable tintDrawable(Icon icon, @Nullable Drawable drawable) {
+ if (drawable == null) {
+ return null;
+ }
+
+ if (icon.hasTint()) {
+ drawable.mutate();
+ drawable.setTintList(icon.getTintList());
+ drawable.setTintBlendMode(icon.getTintBlendMode());
+ }
+
+ return drawable;
+ }
+
+ private static Drawable resolveImage(ImageDecoder.Source source, int maxWidth, int maxHeight) {
+ try {
+ return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> {
+ if (maxWidth <= 0 || maxHeight <= 0) {
+ return;
}
- } else {
- if (size.getHeight() > maxHeight) {
- final int targetWidth = size.getWidth() * maxHeight / size.getHeight();
- decoder.setTargetSize(targetWidth, maxHeight);
+
+ final Size size = info.getSize();
+ if (size.getWidth() > size.getHeight()) {
+ if (size.getWidth() > maxWidth) {
+ final int targetHeight = size.getHeight() * maxWidth / size.getWidth();
+ decoder.setTargetSize(maxWidth, targetHeight);
+ }
+ } else {
+ if (size.getHeight() > maxHeight) {
+ final int targetWidth = size.getWidth() * maxHeight / size.getHeight();
+ decoder.setTargetSize(targetWidth, maxHeight);
+ }
}
- }
- });
+ });
+
+ // ImageDecoder documentation is misleading a bit - it'll throw NotFoundException
+ // in some cases despite it not saying so. Rethrow it as an IOException to keep
+ // our API contract.
+ } catch (IOException | Resources.NotFoundException e) {
+ Log.e(TAG, "Failed to load image drawable", e);
+ return null;
+ }
}
private static int getPowerOfTwoForSampleRatio(double ratio) {
@@ -88,11 +213,12 @@ public class LocalImageResolver {
}
private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
- ImageDecoder.Source source) {
+ int maxWidth, int maxHeight) {
final Size size = info.getSize();
final int originalSize = Math.max(size.getHeight(), size.getWidth());
- final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
- ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX
+ final int maxSize = Math.max(maxWidth, maxHeight);
+ final double ratio = (originalSize > maxSize)
+ ? originalSize * 1f / maxSize
: 1.0;
decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
}
@@ -101,7 +227,7 @@ public class LocalImageResolver {
* Gets the Uri for this icon, assuming the icon can be treated as a pure Uri. Null otherwise.
*/
@Nullable
- public static Uri getResolvableUri(@Nullable Icon icon) {
+ private static Uri getResolvableUri(@Nullable Icon icon) {
if (icon == null || (icon.getType() != Icon.TYPE_URI
&& icon.getType() != Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
return null;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 01cec7727c4e..3cbf72034897 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -35,7 +35,6 @@ import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
import android.os.Bundle;
import android.os.Debug;
import android.os.Parcel;
@@ -769,8 +768,7 @@ public class LockPatternView extends View {
}
addCellToPattern(cell);
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
return cell;
}
return null;
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 361ba958f759..1074004b4c33 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -175,27 +175,6 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
return mCredential;
}
- /**
- * Returns the credential type recognized by {@link StorageManager}. Can be one of
- * {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
- * {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
- */
- public int getStorageCryptType() {
- if (isNone()) {
- return StorageManager.CRYPT_TYPE_DEFAULT;
- }
- if (isPattern()) {
- return StorageManager.CRYPT_TYPE_PATTERN;
- }
- if (isPin()) {
- return StorageManager.CRYPT_TYPE_PIN;
- }
- if (isPassword()) {
- return StorageManager.CRYPT_TYPE_PASSWORD;
- }
- throw new IllegalStateException("Unhandled credential type");
- }
-
/** Returns whether this is an empty credential */
public boolean isNone() {
ensureNotZeroized();
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 13ca13322cc7..f28e2f636dac 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -396,6 +396,9 @@ protected:
{
JNIEnv* env = javavm_to_jnienv(mVM);
+ LOG_ALWAYS_FATAL_IF(env == nullptr,
+ "Binder thread started or Java binder used, but env null. Attach JVM?");
+
ALOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
IPCThreadState* thread_state = IPCThreadState::self();
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 19402f7ed71a..05855867ed62 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -108,6 +108,32 @@ void NativeDisplayEventReceiver::dispose() {
DisplayEventDispatcher::dispose();
}
+static jobject createJavaVsyncEventData(JNIEnv* env, VsyncEventData vsyncEventData) {
+ ScopedLocalRef<jobjectArray>
+ frameTimelineObjs(env,
+ env->NewObjectArray(VsyncEventData::kFrameTimelinesLength,
+ gDisplayEventReceiverClassInfo
+ .frameTimelineClassInfo.clazz,
+ /*initial element*/ NULL));
+ for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
+ ScopedLocalRef<jobject>
+ frameTimelineObj(env,
+ env->NewObject(gDisplayEventReceiverClassInfo
+ .frameTimelineClassInfo.clazz,
+ gDisplayEventReceiverClassInfo
+ .frameTimelineClassInfo.init,
+ frameTimeline.vsyncId,
+ frameTimeline.expectedPresentationTime,
+ frameTimeline.deadlineTimestamp));
+ env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get());
+ }
+ return env->NewObject(gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.init,
+ frameTimelineObjs.get(), vsyncEventData.preferredFrameTimelineIndex,
+ vsyncEventData.frameInterval);
+}
+
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
uint32_t count, VsyncEventData vsyncEventData) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -116,37 +142,9 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
- ScopedLocalRef<jobjectArray>
- frameTimelineObjs(env,
- env->NewObjectArray(VsyncEventData::kFrameTimelinesLength,
- gDisplayEventReceiverClassInfo
- .frameTimelineClassInfo.clazz,
- /*initial element*/ NULL));
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
- VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
- ScopedLocalRef<jobject>
- frameTimelineObj(env,
- env->NewObject(gDisplayEventReceiverClassInfo
- .frameTimelineClassInfo.clazz,
- gDisplayEventReceiverClassInfo
- .frameTimelineClassInfo.init,
- frameTimeline.vsyncId,
- frameTimeline.expectedPresentationTime,
- frameTimeline.deadlineTimestamp));
- env->SetObjectArrayElement(frameTimelineObjs.get(), i, frameTimelineObj.get());
- }
- ScopedLocalRef<jobject>
- vsyncEventDataJava(env,
- env->NewObject(gDisplayEventReceiverClassInfo
- .vsyncEventDataClassInfo.clazz,
- gDisplayEventReceiverClassInfo
- .vsyncEventDataClassInfo.init,
- frameTimelineObjs.get(),
- vsyncEventData.preferredFrameTimelineIndex,
- vsyncEventData.frameInterval));
-
+ jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData);
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
- timestamp, displayId.value, count, vsyncEventDataJava.get());
+ timestamp, displayId.value, count, javaVsyncEventData);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -255,19 +253,27 @@ static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
}
}
+static jobject nativeGetLatestVsyncEventData(JNIEnv* env, jclass clazz, jlong receiverPtr) {
+ sp<NativeDisplayEventReceiver> receiver =
+ reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
+ gui::ParcelableVsyncEventData parcelableVsyncEventData;
+ status_t status = receiver->getLatestVsyncEventData(&parcelableVsyncEventData);
+ if (status) {
+ ALOGW("Failed to get latest vsync event data from surface flinger");
+ return NULL;
+ }
+ return createJavaVsyncEventData(env, parcelableVsyncEventData.vsync);
+}
static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "nativeInit",
- "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;II)J",
- (void*)nativeInit },
- { "nativeDispose",
- "(J)V",
- (void*)nativeDispose },
- // @FastNative
- { "nativeScheduleVsync", "(J)V",
- (void*)nativeScheduleVsync }
-};
+ /* name, signature, funcPtr */
+ {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;II)J",
+ (void*)nativeInit},
+ {"nativeDispose", "(J)V", (void*)nativeDispose},
+ // @FastNative
+ {"nativeScheduleVsync", "(J)V", (void*)nativeScheduleVsync},
+ {"nativeGetLatestVsyncEventData", "(J)Landroid/view/DisplayEventReceiver$VsyncEventData;",
+ (void*)nativeGetLatestVsyncEventData}};
int register_android_view_DisplayEventReceiver(JNIEnv* env) {
int res = RegisterMethodsOrDie(env, "android/view/DisplayEventReceiver", gMethods,
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0ade0934f2a6..152d729da3b6 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -384,6 +384,7 @@ message ActivityRecordProto {
optional float min_aspect_ratio = 33;
optional bool provides_max_bounds = 34;
optional bool enable_recents_screenshot = 35;
+ optional int32 last_drop_input_mode = 36;
}
/* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 45b6c786670d..6d8f820142d7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -471,11 +471,9 @@
android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
<!-- Defined in RestrictionsManager -->
- <protected-broadcast
- android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
- <!-- Defined in RestrictionsManager -->
+ <protected-broadcast android:name="android.content.action.PERMISSION_RESPONSE_RECEIVED" />
+ <protected-broadcast android:name="android.content.action.REQUEST_PERMISSION" />
- <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
<protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_STARTED" />
<protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
<protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_PROGRESS" />
@@ -656,6 +654,8 @@
<protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
+ <protected-broadcast android:name="com.android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"/>
+
<!-- Added in O -->
<protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" />
<protected-broadcast android:name="com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION" />
@@ -6015,7 +6015,7 @@
<!-- @SystemApi Allows an application to turn on / off quiet mode.
@hide -->
<permission android:name="android.permission.MODIFY_QUIET_MODE"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- Allows internal management of the camera framework
@hide -->
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 81a79c50c3ef..a7f2aa7cba69 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -49,6 +49,8 @@
android:layout_marginStart="@dimen/notification_icon_circle_start"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
/>
<!-- extends ViewGroup -->
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index c6983ae5e045..0756d68063f5 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -45,6 +45,8 @@
android:layout_marginStart="@dimen/notification_icon_circle_start"
android:background="@drawable/notification_icon_circle"
android:padding="@dimen/notification_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
/>
<FrameLayout
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2295090b6616..155e9034b96f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9809,4 +9809,12 @@
of the supported locale. {@link android.app.LocaleConfig} -->
<attr name="name" />
</declare-styleable>
+
+ <!-- @hide -->
+ <declare-styleable name="CachingIconView">
+ <!-- Maximum width of displayed drawable. Drawables exceeding this size will be downsampled. -->
+ <attr name="maxDrawableWidth" format="dimension"/>
+ <!-- Maximum width of height drawable. Drawables exceeding this size will be downsampled. -->
+ <attr name="maxDrawableHeight" format="dimension"/>
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 2997fef00f2f..ffcadd06899d 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -147,6 +147,10 @@
<public name="supportsInlineSuggestionsWithTouchExploration" />
<public name="lineBreakStyle" />
<public name="lineBreakWordStyle" />
+ <!-- @hide -->
+ <public name="maxDrawableWidth" />
+ <!-- @hide -->
+ <public name="maxDrawableHeight" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/tests/coretests/res/drawable/big_a.png b/core/tests/coretests/res/drawable/big_a.png
new file mode 100644
index 000000000000..dc059a3557a8
--- /dev/null
+++ b/core/tests/coretests/res/drawable/big_a.png
Binary files differ
diff --git a/core/tests/coretests/res/layout/caching_icon_view_test_max_size.xml b/core/tests/coretests/res/layout/caching_icon_view_test_max_size.xml
new file mode 100644
index 000000000000..9a034466b0fd
--- /dev/null
+++ b/core/tests/coretests/res/layout/caching_icon_view_test_max_size.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.
+ -->
+
+<com.android.internal.widget.CachingIconView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/caching_icon_view"
+ android:layout_width="120dp"
+ android:layout_height="120dp"
+ android:maxDrawableWidth="80dp"
+ android:maxDrawableHeight="80dp" />
diff --git a/core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml b/core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml
new file mode 100644
index 000000000000..a213a977761d
--- /dev/null
+++ b/core/tests/coretests/res/layout/caching_icon_view_test_no_max_size.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<com.android.internal.widget.CachingIconView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/caching_icon_view"
+ android:layout_width="120dp"
+ android:layout_height="120dp" />
diff --git a/core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java b/core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java
new file mode 100644
index 000000000000..aa9620312a91
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/CachingIconViewTest.java
@@ -0,0 +1,259 @@
+/*
+ * 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.internal.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.InsetDrawable;
+import android.net.Uri;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CachingIconViewTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Test
+ public void invalidIcon_skipsLoadSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageIcon(Icon.createWithResource(mContext, 0x85743222));
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isNull();
+ }
+
+ @Test
+ public void customDrawable_setImageIcon_skipsResizeSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageIcon(Icon.createWithResource(mContext, R.drawable.custom_drawable));
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(InsetDrawable.class);
+ }
+
+ @Test
+ public void customDrawable_setImageIconAsync_skipsResizeSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.custom_drawable)).run();
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(InsetDrawable.class);
+ }
+
+ @Test
+ public void customDrawable_setImageResource_skipsResizeSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageResource(R.drawable.custom_drawable);
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(InsetDrawable.class);
+ }
+
+ @Test
+ public void customDrawable_setImageResourceAsync_skipsResizeSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageResourceAsync(R.drawable.custom_drawable).run();
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(InsetDrawable.class);
+ }
+
+ @Test
+ public void customDrawable_setImageUri_skipsResizeSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageURI(Uri.parse(
+ "android.resource://com.android.frameworks.coretests/"
+ + R.drawable.custom_drawable));
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(InsetDrawable.class);
+ }
+
+ @Test
+ public void customDrawable_setImageUriAsync_skipsResizeSuccessfully() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageURIAsync(Uri.parse(
+ "android.resource://com.android.frameworks.coretests/"
+ + R.drawable.custom_drawable)).run();
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(InsetDrawable.class);
+ }
+
+ @Test
+ public void maxDrawableDimensionsSet_setImageIcon_resizesImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageIcon(Icon.createWithResource(mContext, R.drawable.big_a));
+
+ assertDrawableResized(view);
+ }
+
+ @Test
+ public void maxDrawableWithNoDimensionsSet_setImageIcon_doesNotResizeImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_no_max_size, null);
+ view.setImageIcon(Icon.createWithResource(mContext, R.drawable.big_a));
+
+ assertDrawableNotResized(view);
+ }
+
+ @Test
+ public void maxDrawableDimensionsSet_setImageIconAsync_resizesImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.big_a)).run();
+
+ assertDrawableResized(view);
+ }
+
+ @Test
+ public void maxDrawableWithNoDimensionsSet_setImageIconAsync_doesNotResizeImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_no_max_size, null);
+ view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.big_a)).run();
+
+ assertDrawableNotResized(view);
+ }
+
+ @Test
+ public void maxDrawableDimensionsSet_setImageResource_resizesImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageResource(R.drawable.big_a);
+
+ assertDrawableResized(view);
+ }
+
+ @Test
+ public void maxDrawableWithNoDimensionsSet_setImageResource_doesNotResizeImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_no_max_size, null);
+ view.setImageResource(R.drawable.big_a);
+
+ assertDrawableNotResized(view);
+ }
+
+ @Test
+ public void maxDrawableDimensionsSet_setImageResourceAsync_resizesImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageResourceAsync(R.drawable.big_a).run();
+
+ assertDrawableResized(view);
+ }
+
+ @Test
+ public void maxDrawableWithNoDimensionsSet_setImageResourceAsync_doesNotResizeImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_no_max_size, null);
+ view.setImageResourceAsync(R.drawable.big_a).run();
+
+ assertDrawableNotResized(view);
+ }
+
+ @Test
+ public void maxDrawableDimensionsSet_setImageUri_resizesImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageURI(Uri.parse(
+ "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a));
+
+ assertDrawableResized(view);
+ }
+
+ @Test
+ public void maxDrawableWithNoDimensionsSet_setImageUri_doesNotResizeImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_no_max_size, null);
+ view.setImageURI(Uri.parse(
+ "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a));
+
+ assertDrawableNotResized(view);
+ }
+
+ @Test
+ public void maxDrawableDimensionsSet_setImageUriAsync_resizesImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_max_size, null);
+ view.setImageURIAsync(Uri.parse(
+ "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a)).run();
+
+ assertDrawableResized(view);
+ }
+
+ @Test
+ public void maxDrawableWithNoDimensionsSet_setImageUriAsync_doesNotResizeImageIcon() {
+ CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
+ R.layout.caching_icon_view_test_no_max_size, null);
+ view.setImageURIAsync(Uri.parse(
+ "android.resource://com.android.frameworks.coretests/" + R.drawable.big_a)).run();
+
+ assertDrawableNotResized(view);
+ }
+
+
+ private void assertDrawableResized(@Nullable CachingIconView view) {
+ assertThat(view).isNotNull();
+ int maxSize =
+ (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,
+ mContext.getResources().getDisplayMetrics());
+ assertThat(view.getMaxDrawableHeight()).isEqualTo(maxSize);
+ assertThat(view.getMaxDrawableWidth()).isEqualTo(maxSize);
+
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ assertThat(bitmapDrawable.getBitmap().getWidth()).isLessThan(maxSize + 1);
+ assertThat(bitmapDrawable.getBitmap().getHeight()).isLessThan(maxSize + 1);
+ }
+
+ private void assertDrawableNotResized(@Nullable CachingIconView view) {
+ assertThat(view).isNotNull();
+ int maxSize =
+ (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,
+ mContext.getResources().getDisplayMetrics());
+ assertThat(view.getMaxDrawableHeight()).isEqualTo(-1);
+ assertThat(view.getMaxDrawableWidth()).isEqualTo(-1);
+
+ Drawable drawable = view.getDrawable();
+ assertThat(drawable).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ assertThat(bitmapDrawable.getBitmap().getWidth()).isGreaterThan(maxSize);
+ assertThat(bitmapDrawable.getBitmap().getHeight()).isGreaterThan(maxSize);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
new file mode 100644
index 000000000000..d8b37803f568
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.internal.widget;
+
+import android.content.Context;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.frameworks.coretests.R;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class LocalImageResolverTest {
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ @Test
+ public void resolveImage_invalidResource_returnsNull() throws IOException {
+ // We promise IOException in case of errors - but ImageDecode will throw NotFoundException
+ // in case of wrong resource. This test verifies that we throw IOException for API users.
+ Icon icon = Icon.createWithResource(mContext, 0x85849454);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+ assertThat(d).isNull();
+ }
+
+ @Test
+ public void resolveImage_invalidIconUri_returnsNull() throws IOException {
+ // We promise IOException in case of errors - but ImageDecode will throw NotFoundException
+ // in case of wrong resource. This test verifies that we throw IOException for API users.
+ Icon icon = Icon.createWithContentUri(Uri.parse("bogus://uri"));
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+ assertThat(d).isNull();
+ }
+
+ @Test(expected = IOException.class)
+ public void resolveImage_invalidUri_throwsException() throws IOException {
+ Drawable d = LocalImageResolver.resolveImage(Uri.parse("bogus://uri"), mContext);
+ assertThat(d).isNull();
+ }
+
+ @Test
+ public void resolveImage_nonBitmapResourceIcon_fallsBackToNonResizingLoad() throws IOException {
+ Icon icon = Icon.createWithResource(mContext, R.drawable.blue);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+ assertThat(d).isInstanceOf(ColorDrawable.class);
+ }
+
+ @Test(expected = IOException.class)
+ public void resolveImage_nonBitmapResourceUri_throwsIoException() throws IOException {
+ LocalImageResolver.resolveImage(
+ Uri.parse("android.resource://com.android.frameworks.coretests/" + R.drawable.blue),
+ mContext);
+ }
+
+ @Test
+ public void resolveImageWithResId_nonBitmapResourceIcon_returnsNull() {
+ Drawable d = LocalImageResolver.resolveImage(R.drawable.blue, mContext, 480, 480);
+ assertThat(d).isNull();
+ }
+
+ @Test
+ public void resolveImage_largeBitmapIcon_defaultSize_resizeToDefaultSize() throws
+ IOException {
+ Icon icon = Icon.createWithBitmap(
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ // No isLessOrEqualThan sadly.
+ assertThat(bd.getBitmap().getWidth()).isLessThan(
+ LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
+ assertThat(bd.getBitmap().getHeight()).isLessThan(
+ LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
+ }
+
+ @Test
+ public void resolveImage_largeAdaptiveBitmapIcon_defaultSize_resizeToDefaultSize() throws
+ IOException {
+ Icon icon = Icon.createWithAdaptiveBitmap(
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+
+ assertThat(d).isInstanceOf(AdaptiveIconDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground();
+ // No isLessOrEqualThan sadly.
+ assertThat(bd.getBitmap().getWidth()).isLessThan(
+ LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
+ assertThat(bd.getBitmap().getHeight()).isLessThan(
+ LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
+ }
+
+ @Test
+ public void resolveImage_largeResourceIcon_defaultSize_resizeToDefaultSize() throws
+ IOException {
+ Icon icon = Icon.createWithResource(mContext, R.drawable.big_a);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ // No isLessOrEqualThan sadly.
+ assertThat(bd.getBitmap().getWidth()).isLessThan(
+ LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
+ assertThat(bd.getBitmap().getHeight()).isLessThan(
+ LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1);
+ }
+
+ @Test
+ public void resolveImage_largeResourceIcon_passedSize_resizeToDefinedSize() {
+ Icon icon = Icon.createWithResource(mContext, R.drawable.big_a);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ assertThat(bd.getBitmap().getWidth()).isLessThan(101);
+ assertThat(bd.getBitmap().getHeight()).isLessThan(51);
+ }
+
+ @Test
+ public void resolveImage_largeBitmapIcon_passedSize_resizeToDefinedSize() {
+ Icon icon = Icon.createWithBitmap(
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ assertThat(bd.getBitmap().getWidth()).isLessThan(101);
+ assertThat(bd.getBitmap().getHeight()).isLessThan(51);
+ }
+
+ @Test
+ public void resolveImage_largeAdaptiveBitmapIcon_passedSize_resizeToDefinedSize() {
+ Icon icon = Icon.createWithAdaptiveBitmap(
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a));
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50);
+
+ assertThat(d).isInstanceOf(AdaptiveIconDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground();
+ assertThat(bd.getBitmap().getWidth()).isLessThan(101);
+ assertThat(bd.getBitmap().getHeight()).isLessThan(51);
+ }
+
+
+ @Test
+ public void resolveImage_smallResourceIcon_defaultSize_untouched() throws IOException {
+ Icon icon = Icon.createWithResource(mContext, R.drawable.test32x24);
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ assertThat(bd.getBitmap().getWidth()).isEqualTo(32);
+ assertThat(bd.getBitmap().getHeight()).isEqualTo(24);
+ }
+
+ @Test
+ public void resolveImage_smallBitmapIcon_defaultSize_untouched() throws IOException {
+ Icon icon = Icon.createWithBitmap(
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test32x24));
+ final int originalWidth = icon.getBitmap().getWidth();
+ final int originalHeight = icon.getBitmap().getHeight();
+
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+
+ assertThat(d).isInstanceOf(BitmapDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) d;
+ assertThat(bd.getBitmap().getWidth()).isEqualTo(originalWidth);
+ assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight);
+ }
+
+ @Test
+ public void resolveImage_smallAdaptiveBitmapIcon_defaultSize_untouched() throws IOException {
+ Icon icon = Icon.createWithAdaptiveBitmap(
+ BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test32x24));
+ final int originalWidth = icon.getBitmap().getWidth();
+ final int originalHeight = icon.getBitmap().getHeight();
+
+ Drawable d = LocalImageResolver.resolveImage(icon, mContext);
+ assertThat(d).isInstanceOf(AdaptiveIconDrawable.class);
+ BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground();
+ assertThat(bd.getBitmap().getWidth()).isEqualTo(originalWidth);
+ assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight);
+
+ }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 9ff6d931cb88..bde18f36684c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -67,6 +67,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-2052051397": {
+ "message": "Clear animatingExit: reason=destroySurface win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-2049725903": {
"message": "Task back pressed on root taskId=%d",
"level": "VERBOSE",
@@ -133,6 +139,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "-1969928125": {
+ "message": "Animation start for %s, anim=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"-1963461591": {
"message": "Removing %s from %s",
"level": "VERBOSE",
@@ -169,6 +181,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1933723759": {
+ "message": "Clear animatingExit: reason=relayoutVisibleWindow win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1924376693": {
"message": " Setting Ready-group to %b. group=%s from %s",
"level": "VERBOSE",
@@ -595,6 +613,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1488852351": {
+ "message": "Task=%d contains embedded TaskFragment in untrusted mode. Disabled all input during TaskFragment remote animation.",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"-1483435730": {
"message": "InsetsSource setWin %s for type %s",
"level": "DEBUG",
@@ -631,6 +655,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1471518109": {
+ "message": "Set animatingExit: reason=onAppVisibilityChanged win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1468740466": {
"message": "Moving to PAUSED: %s (starting in paused state)",
"level": "VERBOSE",
@@ -769,6 +799,12 @@
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
+ "-1318478129": {
+ "message": "applyAnimation: win=%s anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"-1311436264": {
"message": "Unregister task fragment organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
@@ -799,12 +835,24 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1303628829": {
+ "message": "**** STARTING EXIT",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/DisplayPolicy.java"
+ },
"-1292329638": {
"message": "Added starting %s: startingWindow=%s startingView=%s",
"level": "VERBOSE",
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1288007399": {
+ "message": "performShowLocked: mDrawState=HAS_DRAWN in %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1270731689": {
"message": "Attempted to set replacing window on app token with no content %s",
"level": "WARN",
@@ -853,6 +901,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
+ "-1209252064": {
+ "message": "Clear animatingExit: reason=clearAnimatingFlags win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1207757583": {
"message": "startAnimation(): Notify animation start: %s",
"level": "DEBUG",
@@ -1237,6 +1291,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-799003045": {
+ "message": "Set animatingExit: reason=remove\/replaceWindow win=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-784959154": {
"message": "Attempted to add private presentation window to a non-private display. Aborting.",
"level": "WARN",
@@ -1699,6 +1759,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-347866078": {
+ "message": "Setting move animation on %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-344488673": {
"message": "Finishing drawing window %s: mDrawState=%s",
"level": "VERBOSE",
@@ -1723,6 +1789,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-319689203": {
+ "message": "Reparenting to original parent: %s for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"-317761482": {
"message": "Create sleep token: tag=%s, displayId=%d",
"level": "DEBUG",
@@ -1831,6 +1903,18 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-208664771": {
+ "message": "Reparenting to leash for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
+ "-203358733": {
+ "message": "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"-198463978": {
"message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
"level": "VERBOSE",
@@ -1927,6 +2011,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-91393839": {
+ "message": "Set animatingExit: reason=remove\/applyAnimation win=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-90559682": {
"message": "Config is skipping already pausing %s",
"level": "VERBOSE",
@@ -1993,6 +2083,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
+ "-32102932": {
+ "message": "Error sending initial configuration change to WindowContainer overlay",
+ "level": "ERROR",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"-23020844": {
"message": "Back: Reset surfaces",
"level": "DEBUG",
@@ -2227,6 +2323,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "215077284": {
+ "message": "Animation start delayed for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"221540118": {
"message": "mUserActivityTimeout set to %d",
"level": "DEBUG",
@@ -2317,6 +2419,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
+ "283489582": {
+ "message": "Clear animatingExit: reason=exitAnimationDone win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"288485303": {
"message": "Attempted to set remove mode to a display that does not exist: %d",
"level": "WARN",
@@ -2389,6 +2497,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "341360111": {
+ "message": "selectAnimation in %s: transit=%d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/DisplayPolicy.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2455,6 +2569,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "385595355": {
+ "message": "Starting animation on %s: type=%d, anim=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"397105698": {
"message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
"level": "VERBOSE",
@@ -2467,6 +2587,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "397862437": {
+ "message": "Cancelling animation restarting=%b for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"399841913": {
"message": "SURFACE RECOVER DESTROY: %s",
"level": "INFO",
@@ -2833,6 +2959,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "769218938": {
+ "message": "Loaded animation %s for %s, duration: %d, stack=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"778774915": {
"message": "Unable to record task since feature is disabled %d",
"level": "VERBOSE",
@@ -2989,6 +3121,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "975275467": {
+ "message": "Set animatingExit: reason=remove\/isAnimating win=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"979347997": {
"message": "Launch on display check: disallow activity embedding without permission.",
"level": "DEBUG",
@@ -3157,6 +3295,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
},
+ "1164325516": {
+ "message": "onExitAnimationDone in %s: exiting=%b remove=%b selfAnimating=%b anim=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1166381079": {
"message": "Execute app transition: %s, displayId: %d Callers=%s",
"level": "WARN",
@@ -3301,6 +3445,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
+ "1335791109": {
+ "message": "createSurface %s: mDrawState=DRAW_PENDING",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1337596507": {
"message": "Sending to proc %s new compat %s",
"level": "VERBOSE",
@@ -3715,6 +3865,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1810209625": {
+ "message": "Animation done in %s: exiting=%b, reportedVisible=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1822314934": {
"message": "Expected target rootTask=%s to restored behind rootTask=%s but it is behind rootTask=%s",
"level": "WARN",
@@ -3793,6 +3949,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java"
},
+ "1878927091": {
+ "message": "prepareSurface: No changes in animation for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
@@ -3895,6 +4057,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/InputMonitor.java"
},
+ "2010476671": {
+ "message": "Animation done in %s: reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"2018454757": {
"message": "WS.removeImmediately: %s Already removed...",
"level": "VERBOSE",
@@ -3907,6 +4075,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "2019765997": {
+ "message": "selectRotationAnimation topFullscreen=%s rotationAnimation=%d forceJumpcut=%b",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
"2022422429": {
"message": "createAnimationAdapter(): container=%s",
"level": "DEBUG",
@@ -3961,6 +4135,12 @@
"group": "WM_DEBUG_WINDOW_INSETS",
"at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
},
+ "2075693141": {
+ "message": "Set animatingExit: reason=startExitingAnimation\/%s win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"2083556954": {
"message": "Set mOrientationChanging of %s",
"level": "VERBOSE",
@@ -4020,6 +4200,9 @@
"WM_DEBUG_ADD_REMOVE": {
"tag": "WindowManager"
},
+ "WM_DEBUG_ANIM": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_APP_TRANSITIONS": {
"tag": "WindowManager"
},
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 05fb4c3cf76f..919a93b8f107 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.security.keystore.KeyProperties;
import android.security.maintenance.IKeystoreMaintenance;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
@@ -158,11 +157,6 @@ public class AndroidKeyStoreMaintenance {
* Migrates a key given by the source descriptor to the location designated by the destination
* descriptor.
*
- * If Domain::APP is selected in either source or destination, nspace must be set to
- * {@link KeyProperties#NAMESPACE_APPLICATION}, implying the caller's UID.
- * If the caller has the MIGRATE_ANY_KEY permission, Domain::APP may be used with
- * other nspace values which then indicates the UID of a different application.
- *
* @param source - The key to migrate may be specified by Domain.APP, Domain.SELINUX, or
* Domain.KEY_ID. The caller needs the permissions use, delete, and grant for the
* source namespace.
@@ -189,20 +183,4 @@ public class AndroidKeyStoreMaintenance {
return SYSTEM_ERROR;
}
}
-
- /**
- * @see IKeystoreMaintenance#listEntries(int, long)
- */
- @Nullable
- public static KeyDescriptor[] listEntries(int domain, long nspace) {
- try {
- return getService().listEntries(domain, nspace);
- } catch (ServiceSpecificException e) {
- Log.e(TAG, "listEntries failed", e);
- return null;
- } catch (Exception e) {
- Log.e(TAG, "Can not connect to keystore", e);
- return null;
- }
- }
}
diff --git a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
index 87deb8b5a1fd..5c8c84cbb85b 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
@@ -21,8 +21,8 @@
android:layout_width="wrap_content"
android:paddingTop="48dp"
android:paddingBottom="48dp"
- android:paddingEnd="16dp"
- android:layout_marginEnd="24dp"
+ android:paddingEnd="@dimen/bubble_user_education_padding_end"
+ android:layout_marginEnd="@dimen/bubble_user_education_margin_end"
android:orientation="vertical"
android:background="@drawable/bubble_stack_user_education_bg"
>
diff --git a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
index fafe40e924f5..b28f58f8356d 100644
--- a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
@@ -23,8 +23,8 @@
android:clickable="true"
android:paddingTop="28dp"
android:paddingBottom="16dp"
- android:paddingEnd="48dp"
- android:layout_marginEnd="24dp"
+ android:paddingEnd="@dimen/bubble_user_education_padding_end"
+ android:layout_marginEnd="@dimen/bubble_user_education_margin_end"
android:orientation="vertical"
android:background="@drawable/bubble_stack_user_education_bg"
>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index ad38975f1f3a..59d03c738723 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -205,8 +205,15 @@
<dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
<dimen name="bubble_manage_menu_elevation">4dp</dimen>
- <!-- Size of user education views on large screens (phone is just match parent). -->
- <dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
+ <!-- Size of manage user education views on large screens or in landscape. -->
+ <dimen name="bubbles_user_education_width">480dp</dimen>
+ <!-- Margin applied to the end of the user education views (really only matters for phone
+ since the width is match parent). -->
+ <dimen name="bubble_user_education_margin_end">24dp</dimen>
+ <!-- Padding applied to the end of the user education view. -->
+ <dimen name="bubble_user_education_padding_end">58dp</dimen>
+ <!-- Padding between the bubble and the user education text. -->
+ <dimen name="bubble_user_education_stack_padding">16dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">16dp</dimen>
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 677c1c7d7759..e4e5e3d411d9 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
@@ -899,7 +899,7 @@ public class BubbleStackView extends FrameLayout
mStackAnimationController.updateResources();
mBubbleOverflow.updateResources();
- if (mRelativeStackPositionBeforeRotation != null) {
+ if (!isStackEduShowing() && mRelativeStackPositionBeforeRotation != null) {
mStackAnimationController.setStackPosition(
mRelativeStackPositionBeforeRotation);
mRelativeStackPositionBeforeRotation = null;
@@ -1200,6 +1200,8 @@ public class BubbleStackView extends FrameLayout
addView(mStackEduView);
}
mBubbleContainer.bringToFront();
+ // Ensure the stack is in the correct spot
+ mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
return mStackEduView.show(mPositioner.getDefaultStartPosition());
}
@@ -1214,6 +1216,8 @@ public class BubbleStackView extends FrameLayout
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
mBubbleContainer.bringToFront(); // Stack appears on top of the stack education
+ // Ensure the stack is in the correct spot
+ mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
mStackEduView.show(mPositioner.getDefaultStartPosition());
}
if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
@@ -1331,10 +1335,12 @@ public class BubbleStackView extends FrameLayout
mStackAnimationController.updateResources();
mDismissView.updateResources();
mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2);
- mStackAnimationController.setStackPosition(
- new RelativeStackPosition(
- mPositioner.getRestingPosition(),
- mStackAnimationController.getAllowableStackPositionRegion()));
+ if (!isStackEduShowing()) {
+ mStackAnimationController.setStackPosition(
+ new RelativeStackPosition(
+ mPositioner.getRestingPosition(),
+ mStackAnimationController.getAllowableStackPositionRegion()));
+ }
if (mIsExpanded) {
updateExpandedView();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index eb4737ac6c63..c09d1e0d189c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -101,9 +101,8 @@ class ManageEducationView constructor(context: Context, positioner: BubblePositi
bubbleExpandedView = expandedView
expandedView.taskView?.setObscuredTouchRect(Rect(positioner.screenRect))
- layoutParams.width = if (positioner.isLargeScreen)
- context.resources.getDimensionPixelSize(
- R.dimen.bubbles_user_education_width_large_screen)
+ layoutParams.width = if (positioner.isLargeScreen || positioner.isLandscape)
+ context.resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
else ViewGroup.LayoutParams.MATCH_PARENT
alpha = 0f
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 3846de73842d..1ff4be887fb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -122,28 +122,29 @@ class StackEducationView constructor(
* If necessary, shows the user education view for the bubble stack. This appears the first
* time a user taps on a bubble.
*
- * @return true if user education was shown, false otherwise.
+ * @return true if user education was shown and wasn't showing before, false otherwise.
*/
fun show(stackPosition: PointF): Boolean {
isHiding = false
if (visibility == VISIBLE) return false
controller.updateWindowFlagsForBackpress(true /* interceptBack */)
- layoutParams.width = if (positioner.isLargeScreen)
- context.resources.getDimensionPixelSize(
- R.dimen.bubbles_user_education_width_large_screen)
+ layoutParams.width = if (positioner.isLargeScreen || positioner.isLandscape)
+ context.resources.getDimensionPixelSize(R.dimen.bubbles_user_education_width)
else ViewGroup.LayoutParams.MATCH_PARENT
+ val stackPadding = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_user_education_stack_padding)
setAlpha(0f)
setVisibility(View.VISIBLE)
post {
requestFocus()
with(view) {
if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
- setPadding(positioner.bubbleSize + paddingRight, paddingTop, paddingRight,
+ setPadding(positioner.bubbleSize + stackPadding, paddingTop, paddingRight,
paddingBottom)
} else {
- setPadding(paddingLeft, paddingTop, positioner.bubbleSize + paddingLeft,
+ setPadding(paddingLeft, paddingTop, positioner.bubbleSize + stackPadding,
paddingBottom)
}
translationY = stackPosition.y + positioner.bubbleSize / 2 - getHeight() / 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 36e55bae18c3..a10ee6af4213 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -23,6 +23,9 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERL
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -42,6 +45,8 @@ import android.view.WindowlessWindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.SurfaceUtils;
@@ -52,6 +57,7 @@ import com.android.wm.shell.common.SurfaceUtils;
public class SplitDecorManager extends WindowlessWindowManager {
private static final String TAG = SplitDecorManager.class.getSimpleName();
private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+ private static final long FADE_DURATION = 133;
private final IconProvider mIconProvider;
private final SurfaceSession mSurfaceSession;
@@ -63,6 +69,11 @@ public class SplitDecorManager extends WindowlessWindowManager {
private SurfaceControl mIconLeash;
private SurfaceControl mBackgroundLeash;
+ private boolean mShown;
+ private boolean mIsResizing;
+ private Rect mBounds = new Rect();
+ private ValueAnimator mFadeAnimator;
+
public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
SurfaceSession surfaceSession) {
super(configuration, null /* rootSurface */, null /* hostInputToken */);
@@ -137,16 +148,19 @@ public class SplitDecorManager extends WindowlessWindowManager {
return;
}
+ if (!mIsResizing) {
+ mIsResizing = true;
+ mBounds.set(newBounds);
+ }
+
if (mBackgroundLeash == null) {
mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
- .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
- .show(mBackgroundLeash);
+ .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1);
}
if (mIcon == null && resizingTask.topActivityInfo != null) {
- // TODO: add fade-in animation.
mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
mResizingIconView.setImageDrawable(mIcon);
mResizingIconView.setVisibility(View.VISIBLE);
@@ -156,12 +170,20 @@ public class SplitDecorManager extends WindowlessWindowManager {
lp.width = mIcon.getIntrinsicWidth();
lp.height = mIcon.getIntrinsicHeight();
mViewHost.relayout(lp);
- t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
+ t.setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
}
-
t.setPosition(mIconLeash,
newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
+
+ boolean show = newBounds.width() > mBounds.width() || newBounds.height() > mBounds.height();
+ if (show != mShown) {
+ if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
+ mFadeAnimator.cancel();
+ }
+ startFadeAnimation(show, false /* releaseLeash */);
+ mShown = show;
+ }
}
/** Stops showing resizing hint. */
@@ -170,6 +192,68 @@ public class SplitDecorManager extends WindowlessWindowManager {
return;
}
+ mIsResizing = false;
+ if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
+ if (!mShown) {
+ // If fade-out animation is running, just add release callback to it.
+ SurfaceControl.Transaction finishT = new SurfaceControl.Transaction();
+ mFadeAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ releaseLeash(finishT);
+ finishT.apply();
+ finishT.close();
+ }
+ });
+ return;
+ }
+
+ // If fade-in animation is running, cancel it and re-run fade-out one.
+ mFadeAnimator.cancel();
+ }
+ if (mShown) {
+ startFadeAnimation(false /* show */, true /* releaseLeash */);
+ mShown = false;
+ } else {
+ // Surface is hidden so release it directly.
+ releaseLeash(t);
+ }
+ }
+
+ private void startFadeAnimation(boolean show, boolean releaseLeash) {
+ final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
+ mFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
+ mFadeAnimator.setDuration(FADE_DURATION);
+ mFadeAnimator.addUpdateListener(valueAnimator-> {
+ final float progress = (float) valueAnimator.getAnimatedValue();
+ animT.setAlpha(mBackgroundLeash, show ? progress : 1 - progress);
+ animT.setAlpha(mIconLeash, show ? progress : 1 - progress);
+ animT.apply();
+ });
+ mFadeAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {
+ if (show) {
+ animT.show(mBackgroundLeash).show(mIconLeash).apply();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ if (!show) {
+ animT.hide(mBackgroundLeash).hide(mIconLeash).apply();
+ }
+ if (releaseLeash) {
+ releaseLeash(animT);
+ animT.apply();
+ }
+ animT.close();
+ }
+ });
+ mFadeAnimator.start();
+ }
+
+ private void releaseLeash(SurfaceControl.Transaction t) {
if (mBackgroundLeash != null) {
t.remove(mBackgroundLeash);
mBackgroundLeash = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index daba7742661c..d05a4df2a4cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -513,10 +513,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
public void applyTaskChanges(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
- if (mImePositionProcessor.applyTaskLayoutForIme(wct, task1.token, task2.token)) {
- return;
- }
-
if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) {
wct.setBounds(task1.token, mBounds1);
wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1));
@@ -758,6 +754,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final int mDisplayId;
+ private boolean mHasImeFocus;
private boolean mImeShown;
private int mYOffsetForIme;
private float mDimValue1;
@@ -780,25 +777,32 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
- if (displayId != mDisplayId) return 0;
+ if (displayId != mDisplayId || !mInitialized) {
+ return 0;
+ }
+
final int imeTargetPosition = getImeTargetPosition();
- if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0;
+ mHasImeFocus = imeTargetPosition != SPLIT_POSITION_UNDEFINED;
+ if (!mHasImeFocus) {
+ return 0;
+ }
+
mStartImeTop = showing ? hiddenTop : shownTop;
mEndImeTop = showing ? shownTop : hiddenTop;
mImeShown = showing;
// Update target dim values
mLastDim1 = mDimValue1;
- mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && showing
+ mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && mImeShown
? ADJUSTED_NONFOCUS_DIM : 0.0f;
mLastDim2 = mDimValue2;
- mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing
+ mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && mImeShown
? ADJUSTED_NONFOCUS_DIM : 0.0f;
// Calculate target bounds offset for IME
mLastYOffset = mYOffsetForIme;
final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
- && !isFloating && !isLandscape(mRootBounds) && showing;
+ && !isFloating && !isLandscape(mRootBounds) && mImeShown;
mTargetYOffset = needOffset ? getTargetYOffset() : 0;
if (mTargetYOffset != mLastYOffset) {
@@ -817,15 +821,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
// because DividerView won't receive onImeVisibilityChanged callback after it being
// re-inflated.
- mSplitWindowManager.setInteractive(
- !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED);
+ mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus);
return needOffset ? IME_ANIMATION_NO_ALPHA : 0;
}
@Override
public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
- if (displayId != mDisplayId) return;
+ if (displayId != mDisplayId || !mHasImeFocus) return;
onProgress(getProgress(imeTop));
mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
}
@@ -833,7 +836,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {
- if (displayId != mDisplayId || cancel) return;
+ if (displayId != mDisplayId || !mHasImeFocus || cancel) return;
onProgress(1.0f);
mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
}
@@ -845,6 +848,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
if (!controlling && mImeShown) {
reset();
mSplitWindowManager.setInteractive(true);
+ mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
}
}
@@ -878,6 +882,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
void reset() {
+ mHasImeFocus = false;
mImeShown = false;
mYOffsetForIme = mLastYOffset = mTargetYOffset = 0;
mDimValue1 = mLastDim1 = mTargetDim1 = 0.0f;
@@ -885,26 +890,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
/**
- * Applies adjusted task layout for showing IME.
- *
- * @return {@code false} if there's no need to adjust, otherwise {@code true}
- */
- boolean applyTaskLayoutForIme(WindowContainerTransaction wct,
- WindowContainerToken token1, WindowContainerToken token2) {
- if (mYOffsetForIme == 0) return false;
-
- mTempRect.set(mBounds1);
- mTempRect.offset(0, mYOffsetForIme);
- wct.setBounds(token1, mTempRect);
-
- mTempRect.set(mBounds2);
- mTempRect.offset(0, mYOffsetForIme);
- wct.setBounds(token2, mTempRect);
-
- return true;
- }
-
- /**
* Adjusts surface layout while showing IME.
*
* @return {@code false} if there's no need to adjust, otherwise {@code true}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index e5a755c35add..48df28ee4cde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -89,6 +89,8 @@ public class PipTransition extends PipTransitionController {
private final Rect mExitDestinationBounds = new Rect();
@Nullable
private IBinder mExitTransition;
+ private IBinder mRequestedEnterTransition;
+ private WindowContainerToken mRequestedEnterTask;
/** The Task window that is currently in PIP windowing mode. */
@Nullable
private WindowContainerToken mCurrentPipTaskToken;
@@ -201,6 +203,9 @@ public class PipTransition extends PipTransitionController {
}
mCurrentPipTaskToken = null;
return true;
+ } else if (transition == mRequestedEnterTransition) {
+ mRequestedEnterTransition = null;
+ mRequestedEnterTask = null;
}
// The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
@@ -243,6 +248,8 @@ public class PipTransition extends PipTransitionController {
if (request.getType() == TRANSIT_PIP) {
WindowContainerTransaction wct = new WindowContainerTransaction();
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ mRequestedEnterTransition = transition;
+ mRequestedEnterTask = request.getTriggerTask().token;
wct.setActivityWindowingMode(request.getTriggerTask().token,
WINDOWING_MODE_UNDEFINED);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
@@ -255,6 +262,23 @@ public class PipTransition extends PipTransitionController {
}
@Override
+ public boolean handleRotateDisplay(int startRotation, int endRotation,
+ WindowContainerTransaction wct) {
+ if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ // A fade-in was requested but not-yet started. In this case, just recalculate the
+ // initial state under the new rotation.
+ int rotationDelta = deltaRotation(startRotation, endRotation);
+ if (rotationDelta != Surface.ROTATION_0) {
+ mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ wct.setBounds(mRequestedEnterTask, destinationBounds);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
public void onTransitionMerged(@NonNull IBinder transition) {
if (transition != mExitTransition) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 02e713d2eaa3..24993c621e3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -196,6 +196,17 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/**
+ * Called when the display is going to rotate.
+ *
+ * @return {@code true} if it was handled, otherwise the existing pip logic
+ * will deal with rotation.
+ */
+ public boolean handleRotateDisplay(int startRotation, int endRotation,
+ WindowContainerTransaction wct) {
+ return false;
+ }
+
+ /**
* Callback interface for PiP transitions (both from and to PiP mode)
*/
public interface PipTransitionCallback {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index e86ebaaabbfe..9b0476411370 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -89,13 +89,14 @@ import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Consumer;
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
public class PipController implements PipTransitionController.PipTransitionCallback,
- RemoteCallable<PipController> {
+ RemoteCallable<PipController>, DisplayController.OnDisplaysChangedListener {
private static final String TAG = "PipController";
private Context mContext;
@@ -143,6 +144,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
*/
private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
+ if (mPipTransitionController.handleRotateDisplay(fromRotation, toRotation, t)) {
+ return;
+ }
if (mPipBoundsState.getDisplayLayout().rotation() == toRotation) {
// The same rotation may have been set by auto PiP-able or fixed rotation. So notify
// the change with fromRotation=false to apply the rotated destination bounds from
@@ -454,6 +458,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return mMainExecutor;
}
+ @Override
+ public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
+ Set<Rect> unrestricted) {
+ if (mPipBoundsState.getDisplayId() == displayId) {
+ mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+ }
+ }
+
private void onConfigurationChanged(Configuration newConfig) {
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index f40aa66932cd..bd98585b67ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -123,6 +123,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 935f6695538d..af6e5d4c6f1a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -59,6 +59,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
+import java.util.Set;
/**
* Unit tests for {@link PipController}
@@ -209,4 +210,15 @@ public class PipControllerTest extends ShellTestCase {
verify(mMockPipMotionHelper, never()).movePip(any(Rect.class));
}
+
+ @Test
+ public void onKeepClearAreasChanged_updatesPipBoundsState() {
+ final int displayId = 1;
+ final Rect keepClearArea = new Rect(0, 0, 10, 10);
+ when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
+
+ mPipController.onKeepClearAreasChanged(displayId, Set.of(keepClearArea), Set.of());
+
+ verify(mMockPipBoundsState).setKeepClearAreas(Set.of(keepClearArea), Set.of());
+ }
}
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 0e9c99bf90d9..71042412e546 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -181,6 +181,80 @@ public class AudioDeviceVolumeManager {
}
}
+ /**
+ * Manages the OnDeviceVolumeBehaviorChangedListener listeners and
+ * DeviceVolumeBehaviorDispatcherStub
+ */
+ private final CallbackUtil.LazyListenerManager<OnDeviceVolumeBehaviorChangedListener>
+ mDeviceVolumeBehaviorChangedListenerMgr = new CallbackUtil.LazyListenerManager();
+
+ /**
+ * @hide
+ * Interface definition of a callback to be invoked when the volume behavior of an audio device
+ * is updated.
+ */
+ public interface OnDeviceVolumeBehaviorChangedListener {
+ /**
+ * Called on the listener to indicate that the volume behavior of a device has changed.
+ * @param device the audio device whose volume behavior changed
+ * @param volumeBehavior the new volume behavior of the audio device
+ */
+ void onDeviceVolumeBehaviorChanged(
+ @NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of changes to any device's volume behavior.
+ * @throws SecurityException if the caller doesn't hold the required permission
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public void addOnDeviceVolumeBehaviorChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnDeviceVolumeBehaviorChangedListener listener)
+ throws SecurityException {
+ mDeviceVolumeBehaviorChangedListenerMgr.addListener(executor, listener,
+ "addOnDeviceVolumeBehaviorChangedListener",
+ () -> new DeviceVolumeBehaviorDispatcherStub());
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener of changes to device volume behavior.
+ */
+
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE
+ })
+ public void removeOnDeviceVolumeBehaviorChangedListener(
+ @NonNull OnDeviceVolumeBehaviorChangedListener listener) {
+ mDeviceVolumeBehaviorChangedListenerMgr.removeListener(listener,
+ "removeOnDeviceVolumeBehaviorChangedListener");
+ }
+
+ private final class DeviceVolumeBehaviorDispatcherStub
+ extends IDeviceVolumeBehaviorDispatcher.Stub implements CallbackUtil.DispatcherStub {
+ public void register(boolean register) {
+ try {
+ getService().registerDeviceVolumeBehaviorDispatcher(register, this);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ mDeviceVolumeBehaviorChangedListenerMgr.callListeners((listener) ->
+ listener.onDeviceVolumeBehaviorChanged(device, volumeBehavior));
+ }
+ }
+
private static IAudioService getService() {
if (sService != null) {
return sService;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c186700a4326..babafaf26036 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -33,6 +33,7 @@ import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
+import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
@@ -44,7 +45,6 @@ import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.ISpatializerOutputCallback;
import android.media.IVolumeController;
-import android.media.IVolumeController;
import android.media.PlayerBase;
import android.media.VolumeInfo;
import android.media.VolumePolicy;
@@ -482,6 +482,10 @@ interface IAudioService {
void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING,android.Manifest.permission.QUERY_AUDIO_STATE})")
+ void registerDeviceVolumeBehaviorDispatcher(boolean register,
+ in IDeviceVolumeBehaviorDispatcher dispatcher);
+
List<AudioFocusInfo> getFocusStack();
boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
diff --git a/media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl b/media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl
new file mode 100644
index 000000000000..2b19bffa97f7
--- /dev/null
+++ b/media/java/android/media/IDeviceVolumeBehaviorDispatcher.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioDeviceAttributes;
+
+/**
+ * AIDL for AudioService to signal changes to an audio device's volume behavior
+ *
+ * {@hide}
+ */
+oneway interface IDeviceVolumeBehaviorDispatcher {
+
+ void dispatchDeviceVolumeBehaviorChanged(in AudioDeviceAttributes device,
+ int deviceVolumeBehavior);
+
+}
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
index 28dff374d539..05dc39aaf81c 100644
--- a/media/java/android/media/tv/StreamEventResponse.java
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -63,8 +63,12 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements
mEventId = source.readInt();
mNptMillis = source.readLong();
int dataLength = source.readInt();
- mData = new byte[dataLength];
- source.readByteArray(mData);
+ if (dataLength > 0) {
+ mData = new byte[dataLength];
+ source.readByteArray(mData);
+ } else {
+ mData = null;
+ }
}
/**
@@ -100,7 +104,11 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements
super.writeToParcel(dest, flags);
dest.writeInt(mEventId);
dest.writeLong(mNptMillis);
- dest.writeInt(mData.length);
- dest.writeByteArray(mData);
+ if (mData != null && mData.length > 0) {
+ dest.writeInt(mData.length);
+ dest.writeByteArray(mData);
+ } else {
+ dest.writeInt(0);
+ }
}
}
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 4c3b68958c0f..0e6249047980 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -150,6 +150,7 @@ public final class BluetoothMidiDevice {
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
+ byte[] value,
int status) {
Log.d(TAG, "onCharacteristicRead " + status);
@@ -164,8 +165,8 @@ public final class BluetoothMidiDevice {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
CLIENT_CHARACTERISTIC_CONFIG);
if (descriptor != null) {
- descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- boolean result = mBluetoothGatt.writeDescriptor(descriptor);
+ int result = mBluetoothGatt.writeDescriptor(descriptor,
+ BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
Log.d(TAG, "writeDescriptor returned " + result);
} else {
Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice);
@@ -184,12 +185,13 @@ public final class BluetoothMidiDevice {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
+ BluetoothGattCharacteristic characteristic,
+ byte[] value) {
if (DEBUG) {
- logByteArray("Received ", characteristic.getValue(), 0,
- characteristic.getValue().length);
+ logByteArray("Received BLE packet", value, 0,
+ value.length);
}
- mPacketDecoder.decodePacket(characteristic.getValue(), mOutputReceiver);
+ mPacketDecoder.decodePacket(value, mOutputReceiver);
}
@Override
@@ -227,17 +229,14 @@ public final class BluetoothMidiDevice {
mCachedBuffer = new byte[count];
}
System.arraycopy(buffer, 0, mCachedBuffer, 0, count);
- if (!mCharacteristic.setValue(mCachedBuffer)) {
- Log.w(TAG, "could not set characteristic value");
- return false;
- }
if (DEBUG) {
logByteArray("Sent ", mCharacteristic.getValue(), 0,
mCharacteristic.getValue().length);
}
- if (!mBluetoothGatt.writeCharacteristic(mCharacteristic)) {
+ if (mBluetoothGatt.writeCharacteristic(mCharacteristic, mCachedBuffer,
+ mCharacteristic.getWriteType()) != BluetoothGatt.GATT_SUCCESS) {
Log.w(TAG, "could not write characteristic to Bluetooth GATT");
return false;
}
diff --git a/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml b/packages/CompanionDeviceManager/res/drawable/helper_back_button.xml
index 8e92051faf6c..8e92051faf6c 100644
--- a/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml
+++ b/packages/CompanionDeviceManager/res/drawable/helper_back_button.xml
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_info.xml b/packages/CompanionDeviceManager/res/drawable/ic_info.xml
new file mode 100644
index 000000000000..d9f6a2720ab6
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/ic_info.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/system_accent1_600">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M11,17H13V11H11ZM12,9Q12.425,9 12.713,8.712Q13,8.425 13,8Q13,7.575 12.713,7.287Q12.425,7 12,7Q11.575,7 11.288,7.287Q11,7.575 11,8Q11,8.425 11.288,8.712Q11.575,9 12,9ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12ZM12,20Q15.325,20 17.663,17.663Q20,15.325 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,15.325 6.338,17.663Q8.675,20 12,20Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
index a22ca941c5eb..a9ace44d49b8 100644
--- a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
@@ -59,9 +59,9 @@
android:gravity="end">
<Button
- android:id="@+id/btn_ok"
- style="@style/VendorHelperOkButton"
- android:text="@string/consent_ok" />
+ android:id="@+id/btn_back"
+ style="@style/VendorHelperBackButton"
+ android:text="@string/consent_back" />
</LinearLayout>
diff --git a/packages/CompanionDeviceManager/res/layout/vendor_header.xml b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
index 5f6aa9419c6e..c35f59e7e8a0 100644
--- a/packages/CompanionDeviceManager/res/layout/vendor_header.xml
+++ b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
@@ -43,7 +43,7 @@
<ImageButton
android:id="@+id/vendor_header_button"
- style="?android:attr/actionOverflowButtonStyle"
+ android:background="@drawable/ic_info"
android:layout_width="31dp"
android:layout_height="32dp"
android:layout_alignParentRight="true" />
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 67fc6c2dc864..586a0223b460 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -47,20 +47,11 @@
<!-- Confirmation for associating an application with a companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
<string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone</string>
- <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
- <string name="summary_app_streaming" product="default">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected.</string>
-
- <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
- <string name="summary_app_streaming" product="tablet">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected.</string>
-
- <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
- <string name="summary_app_streaming" product="device">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected.</string>
-
<!-- Title of the helper dialog for APP_STREAMING profile [CHAR LIMIT=30]. -->
<string name="helper_title_app_streaming">Cross-device services</string>
<!-- Description of the helper dialog for APP_STREAMING profile. [CHAR LIMIT=NONE] -->
- <string name="helper_summary_app_streaming">This service is used to stream apps between your devices</string>
+ <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string>
<!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= -->
@@ -94,10 +85,7 @@
<string name="helper_title_computer">Google Play services</string>
<!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] -->
- <string name="helper_summary_computer" product="default">This service shares photos, media, and notifications form your phone to other devices</string>
-
- <!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] -->
- <string name="helper_summary_computer" product="tablet">This service shares photos, media, and notifications form your phone to other devices</string>
+ <string name="helper_summary_computer"> <xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string>
<!-- ================= null profile ================= -->
@@ -115,8 +103,8 @@
<!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] -->
<string name="consent_no">Don\u2019t allow</string>
- <!-- Ok button for the helper consent dialog [CHAR LIMIT=30] -->
- <string name="consent_ok">OK</string>
+ <!-- Back button for the helper consent dialog [CHAR LIMIT=30] -->
+ <string name="consent_back">Back</string>
<!-- ================== System data transfer ==================== -->
<!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=60] -->
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index a55f30c9be6a..faa303219cf0 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -57,13 +57,14 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
- <style name="VendorHelperOkButton"
+ <style name="VendorHelperBackButton"
parent="@android:style/Widget.Material.Button.Borderless.Colored">
- <item name="android:layout_width">50dp</item>
+ <item name="android:layout_width">60dp</item>
<item name="android:layout_height">36dp</item>
<item name="android:layout_marginTop">20dp</item>
+ <item name="android:textAllCaps">false</item>
<item name="android:textColor">@android:color/system_neutral1_900</item>
- <item name="android:background">@drawable/helper_ok_button</item>
+ <item name="android:background">@drawable/helper_back_button</item>
</style>
<style name="PositiveButton"
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 5eeb167ee5e0..b5968162214d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -38,6 +38,7 @@ import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import static com.android.companiondevicemanager.Utils.getIcon;
import static com.android.companiondevicemanager.Utils.getVendorHeaderIcon;
import static com.android.companiondevicemanager.Utils.getVendorHeaderName;
+import static com.android.companiondevicemanager.Utils.hasVendorIcon;
import static com.android.companiondevicemanager.Utils.prepareResultReceiverForIpc;
import static java.util.Objects.requireNonNull;
@@ -285,7 +286,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mVendorHeaderButton.setOnClickListener(this::onShowHelperDialog);
if (mRequest.isSelfManaged()) {
- initUiForSelfManagedAssociation(appLabel);
+ initUiForSelfManagedAssociation();
} else if (mRequest.isSingleDevice()) {
initUiForSingleDevice(appLabel);
} else {
@@ -396,7 +397,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
finish();
}
- private void initUiForSelfManagedAssociation(CharSequence appLabel) {
+ private void initUiForSelfManagedAssociation() {
if (DEBUG) Log.i(TAG, "initUiFor_SelfManaged_Association()");
final CharSequence deviceName = mRequest.getDisplayName();
@@ -412,6 +413,12 @@ public class CompanionDeviceActivity extends FragmentActivity implements
try {
vendorIcon = getVendorHeaderIcon(this, packageName, userId);
vendorName = getVendorHeaderName(this, packageName, userId);
+
+ mVendorHeaderImage.setImageDrawable(vendorIcon);
+ if (hasVendorIcon(this, packageName, userId)) {
+ mVendorHeaderImage.setColorFilter(getResources().getColor(
+ android.R.color.system_accent1_600, /* Theme= */null));
+ }
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Package u" + userId + "/" + packageName + " not found.");
setResultAndFinish(null, RESULT_INTERNAL_ERROR);
@@ -446,9 +453,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
mPermissionListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mTitle.setText(title);
- mVendorHeaderImage.setImageDrawable(vendorIcon);
mVendorHeaderName.setText(vendorName);
-
mDeviceListRecyclerView.setVisibility(View.GONE);
mProfileIcon.setVisibility(View.GONE);
mVendorHeader.setVisibility(View.VISIBLE);
@@ -586,8 +591,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
private void onShowHelperDialog(View view) {
FragmentManager fragmentManager = getSupportFragmentManager();
CompanionVendorHelperDialogFragment fragmentDialog =
- CompanionVendorHelperDialogFragment.newInstance(mRequest.getPackageName(),
- mRequest.getUserId(), mRequest.getDeviceProfile());
+ CompanionVendorHelperDialogFragment.newInstance(mRequest);
mAssociationConfirmationDialog.setVisibility(View.INVISIBLE);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
index 728e5e5d3b9e..f2f6cb0cda7d 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
@@ -23,6 +23,7 @@ import static com.android.companiondevicemanager.Utils.getApplicationIcon;
import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
import android.annotation.Nullable;
+import android.companion.AssociationRequest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
@@ -44,10 +45,7 @@ import androidx.fragment.app.DialogFragment;
*/
public class CompanionVendorHelperDialogFragment extends DialogFragment {
private static final String TAG = CompanionVendorHelperDialogFragment.class.getSimpleName();
-
- private static final String PACKAGE_NAME_EXTRA = "packageName";
- private static final String DEVICE_PROFILE_EXTRA = "deviceProfile";
- private static final String USER_ID_EXTRA = "userId";
+ private static final String ASSOCIATION_REQUEST_EXTRA = "association_request";
private CompanionVendorHelperDialogListener mListener;
// Only present for selfManaged devices.
@@ -63,15 +61,12 @@ public class CompanionVendorHelperDialogFragment extends DialogFragment {
private CompanionVendorHelperDialogFragment() {}
- static CompanionVendorHelperDialogFragment newInstance(String packageName,
- int userId, String deviceProfile) {
+ static CompanionVendorHelperDialogFragment newInstance(AssociationRequest request) {
CompanionVendorHelperDialogFragment fragmentDialog =
new CompanionVendorHelperDialogFragment();
Bundle bundle = new Bundle();
- bundle.putString(PACKAGE_NAME_EXTRA, packageName);
- bundle.putInt(USER_ID_EXTRA, userId);
- bundle.putString(DEVICE_PROFILE_EXTRA, deviceProfile);
+ bundle.putParcelable(ASSOCIATION_REQUEST_EXTRA, request);
fragmentDialog.setArguments(bundle);
return fragmentDialog;
@@ -102,9 +97,13 @@ public class CompanionVendorHelperDialogFragment extends DialogFragment {
super.onViewCreated(view, savedInstanceState);
Drawable applicationIcon;
- String packageName = getArguments().getString(PACKAGE_NAME_EXTRA);
- String deviceProfile = getArguments().getString(DEVICE_PROFILE_EXTRA);
- int userId = getArguments().getInt(USER_ID_EXTRA);
+ AssociationRequest request = getArguments().getParcelable(
+ ASSOCIATION_REQUEST_EXTRA, AssociationRequest.class);
+
+ final String deviceProfile = request.getDeviceProfile();
+ final String packageName = request.getPackageName();
+ final CharSequence displayName = request.getDisplayName();
+ final int userId = request.getUserId();
try {
applicationIcon = getApplicationIcon(getContext(), packageName);
@@ -117,7 +116,7 @@ public class CompanionVendorHelperDialogFragment extends DialogFragment {
mTitle = view.findViewById(R.id.helper_title);
mSummary = view.findViewById(R.id.helper_summary);
mAppIcon = view.findViewById(R.id.app_icon);
- mButton = view.findViewById(R.id.btn_ok);
+ mButton = view.findViewById(R.id.btn_back);
final Spanned title;
final Spanned summary;
@@ -125,12 +124,14 @@ public class CompanionVendorHelperDialogFragment extends DialogFragment {
switch (deviceProfile) {
case DEVICE_PROFILE_APP_STREAMING:
title = getHtmlFromResources(getContext(), R.string.helper_title_app_streaming);
- summary = getHtmlFromResources(getContext(), R.string.helper_summary_app_streaming);
+ summary = getHtmlFromResources(
+ getContext(), R.string.helper_summary_app_streaming, title, displayName);
break;
case DEVICE_PROFILE_COMPUTER:
title = getHtmlFromResources(getContext(), R.string.helper_title_computer);
- summary = getHtmlFromResources(getContext(), R.string.helper_summary_computer);
+ summary = getHtmlFromResources(
+ getContext(), R.string.helper_summary_computer, title, displayName);
break;
default:
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index 1852e82cb9f9..fceca91ce617 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -108,6 +108,18 @@ class Utils {
return appInfo.metaData.getCharSequence(COMPANION_DEVICE_ACTIVITY_VENDOR_NAME, "");
}
+ static boolean hasVendorIcon(@NonNull Context context,
+ @NonNull String packageName, int userId) throws PackageManager.NameNotFoundException {
+ final ApplicationInfo appInfo = getApplicationInfo(context, packageName, userId);
+ final Bundle bundle = appInfo.metaData;
+
+ if (bundle == null) {
+ return false;
+ } else {
+ return bundle.getInt(COMPANION_DEVICE_ACTIVITY_VENDOR_ICON) != 0;
+ }
+ }
+
/**
* Getting ApplicationInfo from meta-data.
*/
diff --git a/packages/ConnectivityT/OWNERS b/packages/ConnectivityT/OWNERS
index e267d19ad7e2..adbcd4b5a36b 100644
--- a/packages/ConnectivityT/OWNERS
+++ b/packages/ConnectivityT/OWNERS
@@ -1,2 +1,5 @@
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
-per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
+# OWNERS block for code move: b/222234190
+reminv@google.com
+
+# file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+# per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp
index c5b2fe66c315..d3cc4d1a1e70 100644
--- a/packages/SettingsLib/SettingsSpinner/Android.bp
+++ b/packages/SettingsLib/SettingsSpinner/Android.bp
@@ -15,6 +15,7 @@ android_library {
static_libs: [
"androidx.preference_preference",
+ "SettingsLibSettingsTheme",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
deleted file mode 100644
index 8fda876355d6..000000000000
--- a/packages/SettingsLib/SettingsSpinner/res/values-v31/colors.xml
+++ /dev/null
@@ -1,20 +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.
--->
-
-<resources>
- <color name="settingslib_spinner_title_color">@android:color/system_neutral1_900</color>
- <color name="settingslib_spinner_dropdown_color">@android:color/system_neutral2_700</color>
-</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index c4dbc5edbe79..fe47e858b747 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -79,4 +79,7 @@
<color name="settingslib_colorAccentSecondary">@color/settingslib_accent_secondary_device_default</color>
<color name="settingslib_colorSurface">@color/settingslib_surface_light</color>
+
+ <color name="settingslib_spinner_title_color">@android:color/system_neutral1_900</color>
+ <color name="settingslib_spinner_dropdown_color">@android:color/system_neutral2_700</color>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 87cb41863528..b12c6d2850c4 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -54,4 +54,15 @@
<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 e9bbcc785a2f..4f426a3bf10c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -27,6 +27,8 @@
<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>
<!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b0508b1aed36..1c1e1ba32f00 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -398,9 +398,6 @@
<!-- Title for a work profile. [CHAR LIMIT=25] -->
<string name="managed_user_title">All work apps</string>
- <!-- Title for Guest user [CHAR LIMIT=35] -->
- <string name="user_guest">Guest</string>
-
<!-- Manage apps, individual app screen, substituted for the application's label when the app's label CAN NOT be determined.-->
<string name="unknown">Unknown</string>
@@ -1421,16 +1418,18 @@
<!-- Title for the preference to enter the nickname of the user to display in the user switcher [CHAR LIMIT=25]-->
<string name="user_nickname">Nickname</string>
+ <!-- Label for adding a new user in the user switcher [CHAR LIMIT=35] -->
+ <string name="user_add_user">Add user</string>
<!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
<string name="guest_new_guest">Add guest</string>
<!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] -->
<string name="guest_exit_guest">Remove guest</string>
<!-- Label for resetting guest session in the user switcher, which will remove all data from the current guest session [CHAR LIMIT=35] -->
<string name="guest_reset_guest">Reset guest</string>
- <!-- Name for the guest user [CHAR LIMIT=35] -->
- <string name="guest_nickname">Guest</string>
<!-- Title of the confirmation dialog to confirm resetting guest. [CHAR LIMIT=NONE] -->
<string name="guest_reset_guest_dialog_title">Reset guest?</string>
+ <!-- Title of the confirmation dialog to confirm removing guest. [CHAR LIMIT=NONE] -->
+ <string name="guest_remove_guest_dialog_title">Remove guest?</string>
<!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] -->
<string name="guest_reset_guest_confirm_button">Reset</string>
<!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] -->
@@ -1442,6 +1441,15 @@
<!-- Accessibility message for the photo selector which is a button/popup with the current photo [CHAR LIMIT=50] -->
<string name="user_image_photo_selector">Select photo</string>
+ <!-- Content of the dialog shown when the user has failed to provide the device lock too many times and the device is wiped. [CHAR LIMIT=NONE] -->
+ <string name="failed_attempts_now_wiping_device">Too many incorrect attempts. This device\'s data will be deleted.</string>
+ <!-- Content of the dialog shown when the user has failed to provide the user lock too many times and the user is removed. [CHAR LIMIT=NONE] -->
+ <string name="failed_attempts_now_wiping_user">Too many incorrect attempts. This user will be deleted.</string>
+ <!-- Content of the dialog shown when the user has failed to provide the work lock too many times and the work profile is removed. [CHAR LIMIT=NONE] -->
+ <string name="failed_attempts_now_wiping_profile">Too many incorrect attempts. This work profile and its data will be deleted.</string>
+ <!-- Button label to dismiss the dialog telling the user the work profile has been wiped. [CHAR LIMIT=40] -->
+ <string name="failed_attempts_now_wiping_dialog_dismiss">Dismiss</string>
+
<!-- List entry in developer settings to choose default device/system behavior for the app freezer [CHAR LIMIT=30]-->
<string name="cached_apps_freezer_device_default">Device default</string>
<!-- List entry in developer settings to disable the app freezer in developer settings [CHAR LIMIT=30]-->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f639022f222d..aaa011458258 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -134,7 +134,7 @@ public class Utils {
? getUpdatableManagedUserTitle(context)
: context.getString(R.string.managed_user_title);
} else if (info.isGuest()) {
- name = context.getString(R.string.user_guest);
+ name = context.getString(com.android.internal.R.string.guest_name);
}
if (name == null && info != null) {
name = Integer.toString(info.id);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 9ca431d81211..a72f311e55ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -32,6 +32,8 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
+import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
+
import android.annotation.TargetApi;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
@@ -465,6 +467,7 @@ public class InfoMediaManager extends MediaManager {
RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
if (routingSessionInfo != null) {
infos.addAll(mRouterManager.getSelectedRoutes(routingSessionInfo));
+ infos.addAll(mRouterManager.getSelectableRoutes(routingSessionInfo));
}
final List<MediaRoute2Info> transferableRoutes =
mRouterManager.getTransferableRoutes(packageName);
@@ -496,9 +499,11 @@ public class InfoMediaManager extends MediaManager {
mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
mPackageName);
if (!TextUtils.isEmpty(mPackageName)
- && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())
- && mCurrentConnectedDevice == null) {
- mCurrentConnectedDevice = mediaDevice;
+ && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
+ mediaDevice.setState(STATE_SELECTED);
+ if (mCurrentConnectedDevice == null) {
+ mCurrentConnectedDevice = mediaDevice;
+ }
}
break;
case TYPE_BUILTIN_SPEAKER:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 5520ea41aef9..c2e36b79b753 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -63,12 +63,14 @@ public class LocalMediaManager implements BluetoothCallback {
@IntDef({MediaDeviceState.STATE_CONNECTED,
MediaDeviceState.STATE_CONNECTING,
MediaDeviceState.STATE_DISCONNECTED,
- MediaDeviceState.STATE_CONNECTING_FAILED})
+ MediaDeviceState.STATE_CONNECTING_FAILED,
+ MediaDeviceState.STATE_SELECTED})
public @interface MediaDeviceState {
int STATE_CONNECTED = 0;
int STATE_CONNECTING = 1;
int STATE_DISCONNECTED = 2;
int STATE_CONNECTING_FAILED = 3;
+ int STATE_SELECTED = 4;
}
private final Collection<DeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 3984ee9aa007..2fb534ddfe96 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -31,6 +31,8 @@ import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
@@ -55,22 +57,22 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
@Retention(RetentionPolicy.SOURCE)
@IntDef({MediaDeviceType.TYPE_UNKNOWN,
+ MediaDeviceType.TYPE_PHONE_DEVICE,
MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE,
MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE,
MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE,
MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
MediaDeviceType.TYPE_CAST_DEVICE,
- MediaDeviceType.TYPE_CAST_GROUP_DEVICE,
- MediaDeviceType.TYPE_PHONE_DEVICE})
+ MediaDeviceType.TYPE_CAST_GROUP_DEVICE})
public @interface MediaDeviceType {
int TYPE_UNKNOWN = 0;
- int TYPE_USB_C_AUDIO_DEVICE = 1;
- int TYPE_3POINT5_MM_AUDIO_DEVICE = 2;
- int TYPE_FAST_PAIR_BLUETOOTH_DEVICE = 3;
- int TYPE_BLUETOOTH_DEVICE = 4;
- int TYPE_CAST_DEVICE = 5;
- int TYPE_CAST_GROUP_DEVICE = 6;
- int TYPE_PHONE_DEVICE = 7;
+ int TYPE_PHONE_DEVICE = 1;
+ int TYPE_USB_C_AUDIO_DEVICE = 2;
+ int TYPE_3POINT5_MM_AUDIO_DEVICE = 3;
+ int TYPE_FAST_PAIR_BLUETOOTH_DEVICE = 4;
+ int TYPE_BLUETOOTH_DEVICE = 5;
+ int TYPE_CAST_DEVICE = 6;
+ int TYPE_CAST_GROUP_DEVICE = 7;
}
@VisibleForTesting
@@ -305,12 +307,12 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
* The most recent used one + device group with usage info sorted by how many times the
* device has been used.
* 4. The order is followed below rule:
- * 1. USB-C audio device
- * 2. 3.5 mm audio device
- * 3. Bluetooth device
- * 4. Cast device
- * 5. Cast group device
- * 6. Phone
+ * 1. Phone
+ * 2. USB-C audio device
+ * 3. 3.5 mm audio device
+ * 4. Bluetooth device
+ * 5. Cast device
+ * 6. Cast group device
*
* So the device list will look like 5 slots ranked as below.
* Rule 4 + Rule 1 + the most recently used device + Rule 3 + Rule 2
@@ -330,6 +332,12 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
}
}
+ if (getState() == STATE_SELECTED) {
+ return -1;
+ } else if (another.getState() == STATE_SELECTED) {
+ return 1;
+ }
+
// Both devices have same connection status, compare the range zone
if (NearbyDevice.compareRangeZones(getRangeZone(), another.getRangeZone()) != 0) {
return NearbyDevice.compareRangeZones(getRangeZone(), another.getRangeZone());
@@ -378,12 +386,12 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
return s1.compareToIgnoreCase(s2);
} else {
// Both devices have never been used, the priority is:
- // 1. USB-C audio device
- // 2. 3.5 mm audio device
- // 3. Bluetooth device
- // 4. Cast device
- // 5. Cast group device
- // 6. Phone
+ // 1. Phone
+ // 2. USB-C audio device
+ // 3. 3.5 mm audio device
+ // 4. Bluetooth device
+ // 5. Cast device
+ // 6. Cast group device
return mType < another.mType ? -1 : 1;
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index a50d4ae16c99..dc166b4c2349 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -82,5 +82,6 @@ public class GlobalSettings {
Settings.Global.USER_PREFERRED_REFRESH_RATE,
Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT,
Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH,
+ Settings.Global.POWER_BUTTON_LONG_PRESS
};
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 6cfcb51239a3..440bb67dc788 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -53,6 +53,13 @@ public class SettingsHelper {
private static final String SETTING_ORIGINAL_KEY_SUFFIX = "_original";
private static final float FLOAT_TOLERANCE = 0.01f;
+ /** See frameworks/base/core/res/res/values/config.xml#config_longPressOnPowerBehavior **/
+ private static final int LONG_PRESS_POWER_NOTHING = 0;
+ private static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
+ private static final int LONG_PRESS_POWER_FOR_ASSISTANT = 5;
+ /** See frameworks/base/core/res/res/values/config.xml#config_keyChordPowerVolumeUp **/
+ private static final int KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS = 2;
+
private Context mContext;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager;
@@ -190,6 +197,9 @@ public class SettingsHelper {
&& !displayColorModeVendorModeHintsMatch) {
return;
}
+ } else if (Settings.Global.POWER_BUTTON_LONG_PRESS.equals(name)) {
+ setLongPressPowerBehavior(cr, value);
+ return;
}
// Default case: write the restored value to settings
@@ -374,6 +384,67 @@ public class SettingsHelper {
}
}
+ /**
+ * Correctly sets long press power button Behavior.
+ *
+ * The issue is that setting for LongPressPower button Behavior is not available on all devices
+ * and actually changes default Behavior of two properties - the long press power button
+ * and volume up + power button combo. OEM can also reconfigure these Behaviors in config.xml,
+ * so we need to be careful that we don't irreversibly change power button Behavior when
+ * restoring. Or switch to a non-default button Behavior.
+ */
+ private void setLongPressPowerBehavior(ContentResolver cr, String value) {
+ // We will not restore the value if the long press power setting option is unavailable.
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable)) {
+ return;
+ }
+
+ int longPressOnPowerBehavior;
+ try {
+ longPressOnPowerBehavior = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return;
+ }
+
+ if (longPressOnPowerBehavior < LONG_PRESS_POWER_NOTHING
+ || longPressOnPowerBehavior > LONG_PRESS_POWER_FOR_ASSISTANT) {
+ return;
+ }
+
+ // When user enables long press power for Assistant, we also switch the meaning
+ // of Volume Up + Power key chord to the "Show power menu" option.
+ // If the user disables long press power for Assistant, we switch back to default OEM
+ // Behavior configured in config.xml. If the default Behavior IS "LPP for Assistant",
+ // then we fall back to "Long press for Power Menu" Behavior.
+ if (longPressOnPowerBehavior == LONG_PRESS_POWER_FOR_ASSISTANT) {
+ Settings.Global.putInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ LONG_PRESS_POWER_FOR_ASSISTANT);
+ Settings.Global.putInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ KEY_CHORD_POWER_VOLUME_UP_GLOBAL_ACTIONS);
+ } else {
+ // We're restoring "LPP for Assistant Disabled" state, prefer OEM config.xml Behavior
+ // if possible.
+ int longPressOnPowerDeviceBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerBehavior);
+ if (longPressOnPowerDeviceBehavior == LONG_PRESS_POWER_FOR_ASSISTANT) {
+ // The default on device IS "LPP for Assistant Enabled" so fall back to power menu.
+ Settings.Global.putInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ LONG_PRESS_POWER_GLOBAL_ACTIONS);
+ } else {
+ // The default is non-Assistant Behavior, so restore that default.
+ Settings.Global.putInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ longPressOnPowerDeviceBehavior);
+ }
+
+ // Clear and restore default power + volume up Behavior as well.
+ int powerVolumeUpDefaultBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_keyChordPowerVolumeUp);
+ Settings.Global.putInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ powerVolumeUpDefaultBehavior);
+ }
+ }
+
/* package */ byte[] getLocaleData() {
Configuration conf = mContext.getResources().getConfiguration();
return conf.getLocales().toLanguageTags().getBytes();
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 7baa2266756f..4f7b494c0e71 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -16,6 +16,8 @@
package com.android.providers.settings;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,13 +29,19 @@ import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.res.Resources;
import android.media.AudioManager;
import android.net.Uri;
import android.os.LocaleList;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,20 +60,28 @@ public class SettingsHelperTest {
private SettingsHelper mSettingsHelper;
@Mock private Context mContext;
+ @Mock private Resources mResources;
@Mock private ContentResolver mContentResolver;
@Mock private AudioManager mAudioManager;
@Mock private TelephonyManager mTelephonyManager;
@Before
public void setUp() {
+ clearLongPressPowerValues();
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManager);
when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn(
mTelephonyManager);
+ when(mContext.getResources()).thenReturn(mResources);
mSettingsHelper = spy(new SettingsHelper(mContext));
}
+ @After
+ public void tearDown() {
+ clearLongPressPowerValues();
+ }
+
@Test
public void testOnBackupValue_settingReplaced_returnsRealValue() {
when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(true);
@@ -92,6 +108,110 @@ public class SettingsHelperTest {
}
@Test
+ public void testRestoreValue_lppForAssistantEnabled_updatesValue() {
+ ContentResolver cr =
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ when(mResources.getBoolean(
+ R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
+ true);
+
+ mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
+
+ assertThat(
+ Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
+ .isEqualTo(5);
+ assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ -1)).isEqualTo(2);
+ }
+
+ @Test
+ public void testRestoreValue_lppForAssistantNotEnabled_updatesValueToDefaultConfig() {
+ ContentResolver cr =
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ when(mResources.getBoolean(
+ R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
+ true);
+
+ when(mResources.getInteger(
+ R.integer.config_longPressOnPowerBehavior)).thenReturn(
+ 1);
+ when(mResources.getInteger(
+ R.integer.config_keyChordPowerVolumeUp)).thenReturn(
+ 1);
+
+ mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
+
+ assertThat(
+ Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
+ .isEqualTo(1);
+ assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ -1)).isEqualTo(1);
+ }
+
+ @Test
+ public void testRestoreValue_lppForAssistantNotEnabledDefaultConfig_updatesValue() {
+ ContentResolver cr =
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ when(mResources.getBoolean(
+ R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
+ true);
+
+ when(mResources.getInteger(
+ R.integer.config_longPressOnPowerBehavior)).thenReturn(
+ 5);
+ when(mResources.getInteger(
+ R.integer.config_keyChordPowerVolumeUp)).thenReturn(
+ 1);
+
+ mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
+
+ assertThat(
+ Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
+ .isEqualTo(1);
+ assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ -1)).isEqualTo(1);
+ }
+
+ @Test
+ public void testRestoreValue_lppForAssistantNotAvailable_doesNotRestore() {
+ ContentResolver cr =
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ when(mResources.getBoolean(
+ R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
+ false);
+
+ mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
+
+ assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ -1))).isEqualTo(-1);
+ }
+
+
+ @Test
+ public void testRestoreValue_lppForAssistantInvalid_doesNotRestore() {
+ ContentResolver cr =
+ InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ when(mResources.getBoolean(
+ R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
+ false);
+
+ mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "trees", 0);
+
+ assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ -1))).isEqualTo(-1);
+ }
+
+ @Test
public void testResolveLocales() throws Exception {
// Empty string from backup server
assertEquals(LocaleList.forLanguageTags("en-US"),
@@ -184,4 +304,11 @@ public class SettingsHelperTest {
LocaleList.forLanguageTags("en-US"), // current
new String[] { "he-IL", "id-ID", "yi" })); // supported
}
+
+ private void clearLongPressPowerValues() {
+ ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ Settings.Global.putString(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, null);
+ Settings.Global.putString(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, null);
+ }
}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 0b8bd9784b7d..1ce4c64fd8b8 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -161,6 +161,7 @@ public class BugreportProgressService extends Service {
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+ static final String EXTRA_BUGREPORT_NONCE = "android.intent.extra.BUGREPORT_NONCE";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
static final String EXTRA_ID = "android.intent.extra.ID";
static final String EXTRA_NAME = "android.intent.extra.NAME";
@@ -428,7 +429,7 @@ public class BugreportProgressService extends Service {
final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath();
if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
- mInfo.bugreportFile);
+ mInfo.bugreportFile, mInfo.nonce);
} else {
cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
@@ -441,7 +442,7 @@ public class BugreportProgressService extends Service {
}
private static void sendRemoteBugreportFinishedBroadcast(Context context,
- String bugreportFileName, File bugreportFile) {
+ String bugreportFileName, File bugreportFile, long nonce) {
cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
bugreportFile.getParentFile());
final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
@@ -452,6 +453,7 @@ public class BugreportProgressService extends Service {
}
intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
+ intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_NONCE, nonce);
intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
android.Manifest.permission.DUMP);
@@ -628,11 +630,12 @@ public class BugreportProgressService extends Service {
String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ long nonce = intent.getLongExtra(EXTRA_BUGREPORT_NONCE, 0);
String baseName = getBugreportBaseName(bugreportType);
String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
BugreportInfo info = new BugreportInfo(mContext, baseName, name,
- shareTitle, shareDescription, bugreportType, mBugreportsDir);
+ shareTitle, shareDescription, bugreportType, mBugreportsDir, nonce);
synchronized (mLock) {
if (info.bugreportFile.exists()) {
Log.e(TAG, "Failed to start bugreport generation, the requested bugreport file "
@@ -2065,6 +2068,11 @@ public class BugreportProgressService extends Service {
*/
final int type;
+ /**
+ * Nonce of the bugreport
+ */
+ final long nonce;
+
private final Object mLock = new Object();
/**
@@ -2072,12 +2080,13 @@ public class BugreportProgressService extends Service {
*/
BugreportInfo(Context context, String baseName, String name,
@Nullable String shareTitle, @Nullable String shareDescription,
- @BugreportParams.BugreportMode int type, File bugreportsDir) {
+ @BugreportParams.BugreportMode int type, File bugreportsDir, long nonce) {
this.context = context;
this.name = this.initialName = name;
this.shareTitle = shareTitle == null ? "" : shareTitle;
this.shareDescription = shareDescription == null ? "" : shareDescription;
this.type = type;
+ this.nonce = nonce;
this.baseName = baseName;
this.bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
}
@@ -2317,6 +2326,7 @@ public class BugreportProgressService extends Service {
screenshotCounter = in.readInt();
shareDescription = in.readString();
type = in.readInt();
+ nonce = in.readLong();
}
@Override
@@ -2345,6 +2355,7 @@ public class BugreportProgressService extends Service {
dest.writeInt(screenshotCounter);
dest.writeString(shareDescription);
dest.writeInt(type);
+ dest.writeLong(nonce);
}
@Override
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b45cc338a6a..dfe683dbee9a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -385,6 +385,7 @@
android:theme="@style/LongScreenshotActivity"
android:process=":screenshot"
android:exported="false"
+ android:label="@string/screenshot_scroll_label"
android:finishOnTaskLaunch="true" />
<service android:name=".screenrecord.RecordingService" />
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewBoundAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index 5593fdfe5f51..c480197d23dc 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewBoundAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -29,7 +29,7 @@ import android.view.animation.Interpolator
* A class that allows changes in bounds within a view hierarchy to animate seamlessly between the
* start and end state.
*/
-class ViewBoundAnimator {
+class ViewHierarchyAnimator {
// TODO(b/221418522): make this private once it can't be passed as an arg anymore.
enum class Bound(val label: String, val overrideTag: Int) {
LEFT("left", R.id.tag_override_left) {
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
index 17765b51c325..ca8554cbbbfb 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
@@ -22,7 +22,6 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">88dp</dimen>
- <dimen name="qs_header_system_icons_area_height">0dp</dimen>
<dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 405863dc9d92..ec82ccf2022e 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -20,7 +20,7 @@
android:id="@+id/split_shade_status_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/split_shade_header_min_height"
+ android:minHeight="@dimen/large_screen_shade_header_min_height"
android:clickable="false"
android:focusable="true"
android:paddingLeft="@dimen/qs_panel_padding"
@@ -61,8 +61,8 @@
<include
android:id="@+id/carrier_group"
layout="@layout/qs_carrier_group"
- app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
- android:minHeight="@dimen/split_shade_header_min_height"
+ app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
+ android:minHeight="@dimen/large_screen_shade_header_min_height"
app:layout_constraintWidth_min="48dp"
android:layout_width="0dp"
android:layout_height="0dp"
@@ -78,7 +78,7 @@
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
- app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:layout_width="wrap_content"
android:layout_height="48dp"
@@ -93,7 +93,7 @@
android:id="@+id/batteryRemainingIcon"
android:layout_width="wrap_content"
android:layout_height="48dp"
- app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
app:textAppearance="@style/TextAppearance.QS.Status"
app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/large_screen_shade_header.xml
index b6e96ceeaf20..250eabd42d50 100644
--- a/packages/SystemUI/res/layout/split_shade_header.xml
+++ b/packages/SystemUI/res/layout/large_screen_shade_header.xml
@@ -18,8 +18,8 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/split_shade_status_bar"
android:layout_width="match_parent"
- android:layout_height="@dimen/split_shade_header_height"
- android:minHeight="@dimen/split_shade_header_min_height"
+ android:layout_height="@dimen/large_screen_shade_header_height"
+ android:minHeight="@dimen/large_screen_shade_header_min_height"
android:clickable="false"
android:focusable="true"
android:paddingLeft="@dimen/qs_panel_padding"
@@ -32,7 +32,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="48dp"
- android:minHeight="@dimen/split_shade_header_min_height"
+ android:minHeight="@dimen/large_screen_shade_header_min_height"
android:gravity="start|center_vertical"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
@@ -69,7 +69,7 @@
android:layout_gravity="end|center_vertical"
android:layout_marginStart="8dp"
android:focusable="false"
- android:minHeight="@dimen/split_shade_header_min_height"
+ android:minHeight="@dimen/large_screen_shade_header_min_height"
android:minWidth="48dp" />
<com.android.systemui.statusbar.phone.StatusIconContainer
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 740697ba98af..9f14d2f236b5 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -36,7 +36,6 @@
<item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">0.45</item>
<dimen name="controls_task_view_right_margin">8dp</dimen>
- <dimen name="split_shade_header_height">42dp</dimen>
<dimen name="status_bar_header_height_keyguard">42dp</dimen>
<!-- Distance that the full shade transition takes in order to complete by tapping on a button
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index abc69b080440..36cc0ad70315 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -38,4 +38,6 @@
<!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
<bool name="allow_force_nav_bar_handle_opaque">false</bool>
+ <bool name="config_use_large_screen_shade_header">true</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 8f6bde56d521..2264671e5067 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -67,4 +67,6 @@
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">472dp</dimen>
+
+ <dimen name="large_screen_shade_header_height">42dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index bdd704936594..72a71b035998 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -23,7 +23,6 @@
<dimen name="keyguard_split_shade_top_margin">72dp</dimen>
- <dimen name="split_shade_header_height">56dp</dimen>
<dimen name="status_bar_header_height_keyguard">56dp</dimen>
<dimen name="qs_media_session_height_expanded">184dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 95df59442978..07050171470a 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -20,5 +20,7 @@
<dimen name="status_bar_icon_padding">1dp</dimen>
<dimen name="controls_padding_horizontal">75dp</dimen>
+
+ <dimen name="large_screen_shade_header_height">56dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9ea361892b32..d5331e8eae7e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -556,6 +556,9 @@
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">false</bool>
+ <!-- Whether we use large screen shade header which takes only one row compared to QS header -->
+ <bool name="config_use_large_screen_shade_header">false</bool>
+
<!-- Whether notification header should never show section headers. -->
<bool name="config_notification_never_show_section_headers">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 73457262fd17..a309cc4ff1f5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -368,10 +368,12 @@
<dimen name="match_parent">-1px</dimen>
<!-- Height of status bar in split shade mode - visible only on large screens -->
- <dimen name="split_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
- <dimen name="split_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
+ <dimen name="large_screen_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
+ <dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
- <!-- The top margin of the panel that holds the list of notifications. -->
+ <!-- The top margin of the panel that holds the list of notifications.
+ On phones it's always 0dp but it's overridden in Car UI
+ -->
<dimen name="notification_panel_margin_top">0dp</dimen>
<!-- The minimum content height for the split shade NSSL.
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index aad0b5704a97..d3b76d90d09f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -368,15 +368,6 @@
<!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the work profile is removed. [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_last_password_attempt_before_wipe_profile">If you enter an incorrect password on the next attempt, your work profile and its data will be deleted.</string>
- <!-- Content of a dialog shown when the user has failed to provide the device lock too many times and the device is wiped. [CHAR LIMIT=NONE] -->
- <string name="biometric_dialog_failed_attempts_now_wiping_device">Too many incorrect attempts. This device\u2019s data will be deleted.</string>
- <!-- Content of a dialog shown when the user has failed to provide the user lock too many times and the user is removed. [CHAR LIMIT=NONE] -->
- <string name="biometric_dialog_failed_attempts_now_wiping_user">Too many incorrect attempts. This user will be deleted.</string>
- <!-- Content of a dialog shown when the user has failed to provide the work lock too many times and the work profile is removed. [CHAR LIMIT=NONE] -->
- <string name="biometric_dialog_failed_attempts_now_wiping_profile">Too many incorrect attempts. This work profile and its data will be deleted.</string>
- <!-- Button label to dismiss the dialog telling the user the device, user, or work profile has been wiped. [CHAR LIMIT=40] -->
- <string name="biometric_dialog_now_wiping_dialog_dismiss">Dismiss</string>
-
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -831,15 +822,6 @@
<!-- Accessibility label for the button that opens the quick contact of the user. -->
- <!-- Label for the adding a new user in the user switcher [CHAR LIMIT=35] -->
- <string name="user_add_user">Add user</string>
-
- <!-- Name for a freshly added user [CHAR LIMIT=30] -->
- <string name="user_new_user_name">New user</string>
-
- <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
- <string name="guest_exit_guest_dialog_title">Remove guest?</string>
-
<!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
<string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string>
@@ -858,12 +840,6 @@
<!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
<string name="guest_wipe_session_dontwipe">Yes, continue</string>
- <!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
- <string name="user_add_user_title" msgid="2108112641783146007">Add new user?</string>
-
- <!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] -->
- <string name="user_add_user_message_short" msgid="1511354412249044381">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string>
-
<!-- Title for the dialog that lets users know that the maximum allowed number of users on the device has been reached. [CHAR LIMIT=35]-->
<string name="user_limit_reached_title">User limit reached</string>
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
index 91607d2f9bea..0e833265c15f 100644
--- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -43,9 +43,9 @@
</Transition>
<Transition
- android:id="@+id/split_header_transition"
- app:constraintSetStart="@id/split_header_constraint"
- app:constraintSetEnd="@id/split_header_constraint"/>
+ android:id="@+id/large_screen_header_transition"
+ app:constraintSetStart="@id/large_screen_header_constraint"
+ app:constraintSetEnd="@id/large_screen_header_constraint"/>
<!--
Placeholder ConstraintSet. They are populated in the controller for this class.
@@ -56,6 +56,6 @@
<ConstraintSet android:id="@id/qs_header_constraint"/>
- <ConstraintSet android:id="@id/split_header_constraint" />
+ <ConstraintSet android:id="@id/large_screen_header_constraint" />
</MotionScene>
diff --git a/packages/SystemUI/res/xml/split_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index 03401b3d51d1..89090513ea37 100644
--- a/packages/SystemUI/res/xml/split_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -18,7 +18,7 @@
<ConstraintSet
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/split_header_constraint">
+ android:id="@+id/large_screen_header_constraint">
<Constraint
android:id="@+id/clock">
@@ -58,7 +58,7 @@
<Layout
android:layout_width="wrap_content"
android:layout_height="0dp"
- app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toStartOf="@id/privacy_container"
app:layout_constraintTop_toTopOf="@id/clock"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index c9a659a649d5..9cf482fdf790 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -32,6 +32,7 @@ import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -269,6 +270,7 @@ public class RemoteAnimationTargetCompat {
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
final SparseArray<RemoteAnimationTargetCompat> childTaskTargets = new SparseArray<>();
+ final IntArray excludedParentTaskIds = new IntArray();
for (int i = 0; i < info.getChanges().size(); i++) {
final TransitionInfo.Change change = info.getChanges().get(i);
final boolean changeIsWallpaper =
@@ -282,22 +284,40 @@ public class RemoteAnimationTargetCompat {
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo != null) {
- if (taskInfo.parentTaskId != -1) {
- // Cache child task targets to override its parent target later and exclude
- // child task while wrapping up animate targets. Otherwise the child task might
- // get transformed twice with the flow like RecentsView#redrawLiveTile.
- childTaskTargets.put(taskInfo.parentTaskId, targetCompat);
+ // Skip wrapping excluded parent task animate target since it will animate its child
+ // tasks instead.
+ if (excludedParentTaskIds.binarySearch(taskInfo.taskId) != -1) {
continue;
}
- final RemoteAnimationTargetCompat childTaskTarget =
- childTaskTargets.get(taskInfo.taskId);
+ // Check if there's a matching child task target in cache.
+ RemoteAnimationTargetCompat childTaskTarget = childTaskTargets.get(taskInfo.taskId);
if (childTaskTarget != null) {
- // Launcher monitors leaf tasks to perform animation, hence override the parent
- // task target with child task info so Launcher can locate and animate root
- // surface directly with leaf task information.
+ // Launcher monitors leaf task ids to perform animation, override the target
+ // with its child task information so Launcher can animate this parent surface
+ // directly with leaf task information.
targetCompat.taskInfo = childTaskTarget.taskInfo;
targetCompat.taskId = childTaskTarget.taskId;
+ childTaskTargets.remove(taskInfo.taskId);
+ }
+
+ // Check if it has a parent task, cache its information for later use.
+ if (taskInfo.parentTaskId != -1
+ && excludedParentTaskIds.binarySearch(taskInfo.parentTaskId) == -1) {
+ if (!childTaskTargets.contains(taskInfo.parentTaskId)) {
+ // Cache the target amd skip wrapping it info the final animation targets.
+ // Otherwise, the child task might get transformed multiple-times with the
+ // flow like RecentsView#redrawLiveTile.
+ childTaskTargets.put(taskInfo.parentTaskId, targetCompat);
+ continue;
+ }
+
+ // There is another child task target cached with the same parent task id.
+ // Which means the parent having multiple child tasks in transition. Stop
+ // propagate child task info.
+ childTaskTarget = childTaskTargets.removeReturnOld(taskInfo.parentTaskId);
+ out.add(childTaskTarget);
+ excludedParentTaskIds.add(taskInfo.parentTaskId);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index d02b8752469a..b18abf3fd02c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -76,8 +76,7 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
public void setKeyDownListener(KeyDownListener keyDownListener) {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 5cab547ee435..f771c974fe60 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -219,7 +219,6 @@ public class NumPadKey extends ViewGroup {
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 76d4aa839ef3..55da8da6cc33 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -47,6 +47,7 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
+import com.android.systemui.util.LargeScreenUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -814,7 +815,7 @@ public class AuthBiometricView extends LinearLayout {
}
private boolean isLargeDisplay() {
- return com.android.systemui.util.Utils.shouldUseSplitNotificationShade(getResources());
+ return LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 2f2ca5b51fbf..ed84a37682e4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -390,7 +390,9 @@ public abstract class AuthCredentialView extends LinearLayout {
private void showNowWipingDialog() {
final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
.setMessage(getNowWipingMessage(getUserTypeForWipe()))
- .setPositiveButton(R.string.biometric_dialog_now_wiping_dialog_dismiss, null)
+ .setPositiveButton(
+ com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss,
+ null /* OnClickListener */)
.setOnDismissListener(
dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR))
.create();
@@ -513,13 +515,13 @@ public abstract class AuthCredentialView extends LinearLayout {
int resId;
switch (userType) {
case USER_TYPE_PRIMARY:
- resId = R.string.biometric_dialog_failed_attempts_now_wiping_device;
+ resId = com.android.settingslib.R.string.failed_attempts_now_wiping_device;
break;
case USER_TYPE_MANAGED_PROFILE:
- resId = R.string.biometric_dialog_failed_attempts_now_wiping_profile;
+ resId = com.android.settingslib.R.string.failed_attempts_now_wiping_profile;
break;
case USER_TYPE_SECONDARY:
- resId = R.string.biometric_dialog_failed_attempts_now_wiping_user;
+ resId = com.android.settingslib.R.string.failed_attempts_now_wiping_user;
break;
default:
throw new IllegalArgumentException("Unrecognized user type:" + userType);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 995df19f64c2..330bd6cb8281 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -27,6 +27,9 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -38,6 +41,8 @@ import javax.inject.Named;
@DreamOverlayComponent.DreamOverlayScope
public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
private final DreamOverlayStatusBarViewController mStatusBarViewController;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final BlurUtils mBlurUtils;
private final ComplicationHostViewController mComplicationHostViewController;
@@ -60,12 +65,57 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
private long mJitterStartTimeMillis;
+ private boolean mBouncerAnimating;
+
+ private final KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback =
+ new KeyguardBouncer.BouncerExpansionCallback() {
+
+ @Override
+ public void onStartingToShow() {
+ mBouncerAnimating = true;
+ }
+
+ @Override
+ public void onStartingToHide() {
+ mBouncerAnimating = true;
+ }
+
+ @Override
+ public void onFullyHidden() {
+ mBouncerAnimating = false;
+ }
+
+ @Override
+ public void onFullyShown() {
+ mBouncerAnimating = false;
+ }
+
+ @Override
+ public void onExpansionChanged(float bouncerHideAmount) {
+ if (!mBouncerAnimating) return;
+ final int blurRadius =
+ (int) mBlurUtils.blurRadiusOfRatio(1 - bouncerHideAmount);
+ updateTransitionState(blurRadius, bouncerHideAmount);
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ // The bouncer may be hidden abruptly without triggering onExpansionChanged.
+ // In this case, we should reset the transition state.
+ if (!isVisible) {
+ updateTransitionState(0, 1f);
+ }
+ }
+ };
+
@Inject
public DreamOverlayContainerViewController(
DreamOverlayContainerView containerView,
ComplicationHostViewController complicationHostViewController,
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
DreamOverlayStatusBarViewController statusBarViewController,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ BlurUtils blurUtils,
@Main Handler handler,
@Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
@@ -74,6 +124,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mBlurUtils = blurUtils;
mComplicationHostViewController = complicationHostViewController;
final View view = mComplicationHostViewController.getView();
@@ -98,11 +150,19 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
protected void onViewAttached() {
mJitterStartTimeMillis = System.currentTimeMillis();
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
+ final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
+ if (bouncer != null) {
+ bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
+ }
}
@Override
protected void onViewDetached() {
mHandler.removeCallbacks(this::updateBurnInOffsets);
+ final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
+ if (bouncer != null) {
+ bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
+ }
}
View getContainerView() {
@@ -131,4 +191,9 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
}
+
+ private void updateTransitionState(int blurRadiusPixels, float alpha) {
+ mBlurUtils.applyBlur(mView.getViewRootImpl(), blurRadiusPixels, false);
+ mView.setAlpha(alpha);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index b96cee650663..1b62ec4772f3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -82,6 +82,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
private Boolean mCapture;
+ private boolean mBouncerInitiallyShowing;
+
private TouchSession mTouchSession;
private ValueAnimatorCreator mValueAnimatorCreator;
@@ -97,6 +99,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
// If the user scrolling favors a vertical direction, begin capturing
// scrolls.
mCapture = Math.abs(distanceY) > Math.abs(distanceX);
+ mBouncerInitiallyShowing = mCentralSurfaces.isBouncerShowing();
if (mCapture) {
// Since the user is dragging the bouncer up, set scrimmed to false.
@@ -115,7 +118,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
// (0).
final float screenTravelPercentage =
Math.abs((e1.getY() - e2.getY()) / mCentralSurfaces.getDisplayHeight());
- setPanelExpansion(mCentralSurfaces.isBouncerShowing()
+ setPanelExpansion(mBouncerInitiallyShowing
? screenTravelPercentage : 1 - screenTravelPercentage);
return true;
}
@@ -174,18 +177,18 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
mTouchSession = session;
mVelocityTracker.clear();
mNotificationShadeWindowController.setForcePluginOpen(true, this);
+
+ session.registerCallback(() -> {
+ mVelocityTracker.recycle();
+ mCapture = null;
+ mNotificationShadeWindowController.setForcePluginOpen(false, this);
+ });
+
session.registerGestureListener(mOnGestureListener);
session.registerInputListener(ev -> onMotionEvent(ev));
}
- @Override
- public void onSessionEnd(TouchSession session) {
- mVelocityTracker.recycle();
- mCapture = null;
- mNotificationShadeWindowController.setForcePluginOpen(false, this);
- }
-
private void onMotionEvent(InputEvent event) {
if (!(event instanceof MotionEvent)) {
Log.e(TAG, "non MotionEvent received:" + event);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
index 20008d5b02c8..8288fcfb5481 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
@@ -96,11 +96,4 @@ public interface DreamTouchHandler {
* @param session
*/
void onSessionStart(TouchSession session);
-
- /**
- * Invoked when a session has ended. This will be invoked for every session completion, even
- * those that are removed through {@link TouchSession#pop()}.
- * @param session
- */
- default void onSessionEnd(TouchSession session) { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java
index d4ba2d82f38a..4965c9dfd00b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java
@@ -127,11 +127,4 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
}
});
}
-
- @Override
- public void onSessionEnd(TouchSession session) {
- if (DEBUG) {
- Log.d(TAG, "onSessionEnd");
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 5a8b7e3d3d64..237b5053ea2c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -30,7 +30,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.MediaContainerView
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.Utils
+import com.android.systemui.util.LargeScreenUtils
import javax.inject.Inject
import javax.inject.Named
@@ -71,7 +71,7 @@ class KeyguardMediaController @Inject constructor(
}
private fun updateResources() {
- useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
+ useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 56d8c6486631..f457ae74fe83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -105,6 +105,17 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean {
}
/**
+ * Allow recommendations from smartspace to show in media controls.
+ * Requires [Utils.useQsMediaPlayer] to be enabled.
+ * On by default, but can be disabled by setting to 0
+ */
+private fun allowMediaRecommendations(context: Context): Boolean {
+ val flag = Settings.Secure.getInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1)
+ return Utils.useQsMediaPlayer(context) && flag > 0
+}
+
+/**
* A class that facilitates management and loading of Media Data, ready for binding.
*/
@SysUISingleton
@@ -164,7 +175,7 @@ class MediaDataManager(
// There should ONLY be at most one Smartspace media recommendation.
var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
private var smartspaceSession: SmartspaceSession? = null
- private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ private var allowMediaRecommendations = allowMediaRecommendations(context)
/**
* Check whether this notification is an RCN
@@ -272,7 +283,7 @@ class MediaDataManager(
smartspaceSession?.let { it.requestSmartspaceUpdate() }
tunerService.addTunable(object : TunerService.Tunable {
override fun onTuningChanged(key: String?, newValue: String?) {
- allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ allowMediaRecommendations = allowMediaRecommendations(context)
if (!allowMediaRecommendations) {
dismissSmartspaceRecommendation(key = smartspaceMediaData.targetId, delay = 0L)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index d978d02d63f1..b0159ede51a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -43,7 +43,7 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.Utils
+import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -514,7 +514,7 @@ class MediaHierarchyManager @Inject constructor(
private fun updateConfiguration() {
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_media_transition_distance)
- inSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
+ inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index e4b8874ff601..87579040372f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -66,11 +66,14 @@ class MediaTttCommandLineHelper @Inject constructor(
as StatusBarManager
val routeInfo = MediaRoute2Info.Builder("id", args[0])
.addFeature("feature")
- .setPackageName(TEST_PACKAGE_NAME)
- .build()
+ val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
+ if (useAppIcon) {
+ routeInfo.setPackageName(TEST_PACKAGE_NAME)
+ }
+
statusBarManager.updateMediaTapToTransferSenderDisplay(
- displayState,
- routeInfo,
+ displayState,
+ routeInfo.build(),
getUndoExecutor(displayState),
getUndoCallback(displayState)
)
@@ -106,7 +109,8 @@ class MediaTttCommandLineHelper @Inject constructor(
}
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipState>")
+ pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND " +
+ "<deviceName> <chipState> useAppIcon=[true|false]")
}
}
@@ -128,7 +132,8 @@ class MediaTttCommandLineHelper @Inject constructor(
as StatusBarManager
val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
.addFeature("feature")
- if (args.size >= 2 && args[1] == "useAppIcon=true") {
+ val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false")
+ if (useAppIcon) {
routeInfo.setPackageName(TEST_PACKAGE_NAME)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index a1a319814269..df820a722e28 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -33,6 +33,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.view.View;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
@@ -72,6 +73,7 @@ import dagger.Lazy;
*/
@SysUISingleton
public final class NavBarHelper implements
+ AccessibilityManager.AccessibilityServicesStateChangeListener,
AccessibilityButtonModeObserver.ModeChangedListener,
AccessibilityButtonTargetsObserver.TargetsChangedListener,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
@@ -123,8 +125,7 @@ public final class NavBarHelper implements
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mUserTracker = userTracker;
mSystemActions = systemActions;
- accessibilityManager.addAccessibilityServicesStateChangeListener(
- accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
+ accessibilityManager.addAccessibilityServicesStateChangeListener(this);
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
@@ -146,6 +147,7 @@ public final class NavBarHelper implements
Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
updateAssistantAvailability();
+ updateA11yState();
}
public void destroy() {
@@ -178,6 +180,12 @@ public final class NavBarHelper implements
}
@Override
+ public void onAccessibilityServicesStateChanged(AccessibilityManager manager) {
+ dispatchA11yEventUpdate();
+ updateA11yState();
+ }
+
+ @Override
public void onAccessibilityButtonModeChanged(int mode) {
updateA11yState();
dispatchA11yEventUpdate();
@@ -190,7 +198,9 @@ public final class NavBarHelper implements
}
/**
- * Updates the current accessibility button state.
+ * Updates the current accessibility button state. The accessibility button state is only
+ * used for {@link Secure#ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR} and
+ * {@link Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}, otherwise it is reset to 0.
*/
private void updateA11yState() {
final int prevState = mA11yButtonState;
@@ -213,6 +223,9 @@ public final class NavBarHelper implements
final int requestingServices = a11yButtonTargets.size();
clickable = requestingServices >= 1;
+
+ // `longClickable` is used to determine whether to pop up the accessibility chooser
+ // dialog or not, and it’s also only for multiple services.
longClickable = requestingServices >= 2;
mA11yButtonState = (clickable ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
| (longClickable ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
@@ -237,11 +250,13 @@ public final class NavBarHelper implements
}
/**
- * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
- * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ * Gets the accessibility button state based on the {@link Secure#ACCESSIBILITY_BUTTON_MODE}.
*
- * @return the a11y button clickable and long_clickable states, or 0 if there is no
- * a11y button in the navbar
+ * @return the accessibility button state:
+ * 0 = disable state
+ * 16 = {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE}
+ * 48 = the combination of {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
*/
public int getA11yButtonState() {
return mA11yButtonState;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index ceb895f74d90..519ed5ceeab4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -29,7 +29,6 @@ import android.widget.FrameLayout;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.util.Utils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -147,7 +146,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
}
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
- Utils.getQsHeaderSystemIconsAreaHeight(mContext),
+ QSUtils.getQsHeaderSystemIconsAreaHeight(mContext),
mQSPanelContainer.getPaddingEnd(),
bottomPadding);
@@ -175,7 +174,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
public void updateExpansion() {
int height = calculateContainerHeight();
- int scrollBottom = calculateContainerBottom();
setBottom(getTop() + height);
}
@@ -187,15 +185,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
+ mHeader.getHeight();
}
- int calculateContainerBottom() {
- int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
- return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
- : Math.round(mQsExpansion
- * (heightOverride + mQSPanelContainer.getScrollRange()
- - mQSPanelContainer.getScrollY() - mHeader.getHeight()))
- + mHeader.getHeight();
- }
-
public void setExpansion(float expansion) {
mQsExpansion = expansion;
mQSPanelContainer.setScrollingEnabled(expansion > 0f);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 6572daa91269..58007c0d370a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -37,7 +37,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.util.Utils;
+import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -88,7 +88,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
@Override
public void onConfigurationChange(Configuration newConfig) {
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(getResources());
+ LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
onConfigurationChanged();
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
@@ -133,7 +133,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mQSLogger = qsLogger;
mDumpManager = dumpManager;
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(getResources());
+ LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
new file mode 100644
index 000000000000..e42264f24e92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
@@ -0,0 +1,24 @@
+package com.android.systemui.qs
+
+import android.content.Context
+import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.util.LargeScreenUtils.shouldUseLargeScreenShadeHeader
+
+object QSUtils {
+
+ /**
+ * Gets the [R.dimen.qs_header_system_icons_area_height] unless we use large screen header.
+ *
+ * It's the same as [com.android.internal.R.dimen.quick_qs_offset_height] except for
+ * sw600dp-land.
+ */
+ @JvmStatic
+ fun getQsHeaderSystemIconsAreaHeight(context: Context): Int {
+ return if (shouldUseLargeScreenShadeHeader(context.resources)) {
+ // value should be 0 when using large screen shade header because it's not expandable
+ 0
+ } else {
+ SystemBarUtils.getQuickQsOffsetHeight(context)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index f2dd7700e65e..c5ca285aa312 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconMa
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.VariableDateView;
+import com.android.systemui.util.LargeScreenUtils;
import java.util.List;
@@ -242,11 +243,10 @@ public class QuickStatusBarHeader extends FrameLayout {
void updateResources() {
Resources resources = mContext.getResources();
- // status bar is already displayed out of QS in split shade
- boolean shouldUseSplitShade =
- resources.getBoolean(R.bool.config_use_split_notification_shade);
+ boolean largeScreenHeaderActive =
+ LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources);
- boolean gone = shouldUseSplitShade || mUseCombinedQSHeader || mQsDisabled;
+ boolean gone = largeScreenHeaderActive || mUseCombinedQSHeader || mQsDisabled;
mStatusIconsView.setVisibility(gone ? View.GONE : View.VISIBLE);
mDatePrivacyView.setVisibility(gone ? View.GONE : View.VISIBLE);
@@ -287,7 +287,7 @@ public class QuickStatusBarHeader extends FrameLayout {
}
MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
- qqsLP.topMargin = shouldUseSplitShade || !mUseCombinedQSHeader ? mContext.getResources()
+ qqsLP.topMargin = largeScreenHeaderActive || !mUseCombinedQSHeader ? mContext.getResources()
.getDimensionPixelSize(R.dimen.qqs_layout_margin_top) : qsOffsetHeight;
mHeaderQsPanel.setLayoutParams(qqsLP);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index b59c0ccc510a..71f0a33fd374 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -36,8 +36,8 @@ import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
+import com.android.systemui.qs.QSUtils;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.util.Utils;
/**
* Allows full-screen customization of QS, through show() and hide().
@@ -86,7 +86,7 @@ public class QSCustomizer extends LinearLayout {
void updateResources() {
LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
- lp.height = Utils.getQsHeaderSystemIconsAreaHeight(mContext);
+ lp.height = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
mTransparentView.setLayoutParams(lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 7fb9ef34cfd1..be6982ab2470 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -16,13 +16,14 @@ package com.android.systemui.qs.tileimpl;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Animatable2.AnimationCallback;
import android.graphics.drawable.Drawable;
@@ -53,6 +54,8 @@ public class QSIconViewImpl extends QSIconView {
@Nullable
private QSTile.Icon mLastIcon;
+ private ValueAnimator mColorAnimator = new ValueAnimator();
+
public QSIconViewImpl(Context context) {
super(context);
@@ -61,6 +64,7 @@ public class QSIconViewImpl extends QSIconView {
mIcon = createIcon();
addView(mIcon);
+ mColorAnimator.setDuration(QS_ANIM_LENGTH);
}
@Override
@@ -165,7 +169,6 @@ public class QSIconViewImpl extends QSIconView {
mState = state.state;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
- mTint = color;
} else {
if (iv instanceof AlphaControlledSlashImageView) {
((AlphaControlledSlashImageView)iv)
@@ -173,7 +176,6 @@ public class QSIconViewImpl extends QSIconView {
} else {
setTint(iv, color);
}
- mTint = color;
updateIcon(iv, state, allowAnimations);
}
} else {
@@ -191,39 +193,30 @@ public class QSIconViewImpl extends QSIconView {
((AlphaControlledSlashImageView)iv)
.setFinalImageTintList(ColorStateList.valueOf(toColor));
}
+ mColorAnimator.cancel();
if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
- final float fromAlpha = Color.alpha(fromColor);
- final float toAlpha = Color.alpha(toColor);
- final float fromChannel = Color.red(fromColor);
- final float toChannel = Color.red(toColor);
-
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- anim.setDuration(QS_ANIM_LENGTH);
- anim.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
- int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
-
- setTint(iv, Color.argb(alpha, channel, channel, channel));
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- endRunnable.run();
- }
+ PropertyValuesHolder values = PropertyValuesHolder.ofInt("color", fromColor, toColor);
+ values.setEvaluator(ArgbEvaluator.getInstance());
+ mColorAnimator.setValues(values);
+ mColorAnimator.removeAllListeners();
+ mColorAnimator.addUpdateListener(animation -> {
+ setTint(iv, (int) animation.getAnimatedValue());
});
- anim.start();
+ mColorAnimator.addListener(new EndRunnableAnimatorListener(endRunnable));
+
+ mColorAnimator.start();
} else {
+
setTint(iv, toColor);
endRunnable.run();
}
}
- public static void setTint(ImageView iv, int color) {
+ public void setTint(ImageView iv, int color) {
iv.setImageTintList(ColorStateList.valueOf(color));
+ mTint = color;
}
-
protected int getIconMeasureMode() {
return MeasureSpec.EXACTLY;
}
@@ -261,4 +254,25 @@ public class QSIconViewImpl extends QSIconView {
return 0;
}
}
+
+ private static class EndRunnableAnimatorListener extends AnimatorListenerAdapter {
+ private Runnable mRunnable;
+
+ EndRunnableAnimatorListener(Runnable endRunnable) {
+ super();
+ mRunnable = endRunnable;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ mRunnable.run();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mRunnable.run();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 4732a8c09500..310eb4f24652 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -40,7 +40,7 @@ import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.Utils
+import com.android.systemui.util.LargeScreenUtils
import java.io.FileDescriptor
import java.io.PrintWriter
import javax.inject.Inject
@@ -251,7 +251,7 @@ class LockscreenShadeTransitionController @Inject constructor(
R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
statusBarTransitionDistance = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_status_bar_transition_distance)
- useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
+ useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
}
fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
@@ -441,7 +441,9 @@ class LockscreenShadeTransitionController @Inject constructor(
}
private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
- if (depthControllerTransitionDistance > 0) {
+ if (depthControllerTransitionDistance == 0) { // split shade
+ depthController.transitionToFullShadeProgress = 0f
+ } else {
val depthProgress =
MathUtils.saturate(dragDownAmount / depthControllerTransitionDistance)
depthController.transitionToFullShadeProgress = depthProgress
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 08b248352016..31a13e6faa8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -19,8 +19,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
-import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
@@ -35,7 +35,7 @@ import javax.inject.Inject
@CoordinatorScope
class ViewConfigCoordinator @Inject internal constructor(
private val mConfigurationController: ConfigurationController,
- private val mLockscreenUserManager: NotificationLockscreenUserManagerImpl,
+ private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index 4b0e2ffd5d7f..41eeada0fcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -21,7 +21,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
-import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -82,16 +81,8 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso
@Override
protected Drawable doInBackground(Uri... uris) {
- Drawable drawable = null;
Uri target = uris[0];
-
- try {
- drawable = mResolver.resolveImage(target);
- } catch (IOException | SecurityException ex) {
- Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex);
- }
-
- return drawable;
+ return mResolver.resolveImage(target);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 44ccb68cce4a..b05e64ab1991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -31,7 +31,6 @@ import com.android.internal.widget.ImageResolver;
import com.android.internal.widget.LocalImageResolver;
import com.android.internal.widget.MessagingMessage;
-import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -111,30 +110,30 @@ public class NotificationInlineImageResolver implements ImageResolver {
* To resolve image from specified uri directly. If the resulting image is larger than the
* maximum allowed size, scale it down.
* @param uri Uri of the image.
- * @return Drawable of the image.
- * @throws IOException Throws if failed at resolving the image.
+ * @return Drawable of the image, or null if unable to load.
*/
- Drawable resolveImage(Uri uri) throws IOException {
- return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight);
+ Drawable resolveImage(Uri uri) {
+ try {
+ return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight);
+ } catch (Exception ex) {
+ // Catch general Exception because ContentResolver can re-throw arbitrary Exception
+ // from remote process as a RuntimeException. See: Parcel#readException
+ Log.d(TAG, "resolveImage: Can't load image from " + uri, ex);
+ }
+ return null;
}
@Override
public Drawable loadImage(Uri uri) {
- Drawable result = null;
- try {
- if (hasCache()) {
- // if the uri isn't currently cached, try caching it first
- if (!mImageCache.hasEntry(uri)) {
- mImageCache.preload((uri));
- }
- result = mImageCache.get(uri);
- } else {
- result = resolveImage(uri);
- }
- } catch (IOException | SecurityException ex) {
- Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
+ return hasCache() ? loadImageFromCache(uri) : resolveImage(uri);
+ }
+
+ private Drawable loadImageFromCache(Uri uri) {
+ // if the uri isn't currently cached, try caching it first
+ if (!mImageCache.hasEntry(uri)) {
+ mImageCache.preload((uri));
}
- return result;
+ return mImageCache.get(uri);
}
/**
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 5c68559ffb1e..3e6f94c67e21 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
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
-import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -110,6 +109,7 @@ import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
import com.android.systemui.util.DumpUtilsKt;
+import com.android.systemui.util.LargeScreenUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -5540,7 +5540,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void updateSplitNotificationShade() {
- boolean split = shouldUseSplitNotificationShade(getResources());
+ boolean split = LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
if (split != mShouldUseSplitNotificationShade) {
mShouldUseSplitNotificationShade = split;
updateDismissBehavior();
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 3f97155242b4..6c6ed850f45a 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
@@ -22,10 +22,8 @@ import android.view.View.GONE
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.children
@@ -41,8 +39,6 @@ private const val DEBUG = false
class NotificationStackSizeCalculator
@Inject
constructor(
- private val groupManager: NotificationGroupManagerLegacy,
- private val lockscreenUserManager: NotificationLockscreenUserManager,
private val statusBarStateController: SysuiStatusBarStateController,
@Main private val resources: Resources
) {
@@ -191,7 +187,7 @@ constructor(
if (onLockscreen) {
when (this) {
is ExpandableNotificationRow -> {
- if (isSummaryOfSuppressedGroup() || !canShowViewOnLockscreen() || isRemoved) {
+ if (!canShowViewOnLockscreen() || isRemoved) {
return false
}
}
@@ -208,9 +204,6 @@ constructor(
visibleIndex: Int
) = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex)
- private fun ExpandableNotificationRow.isSummaryOfSuppressedGroup() =
- groupManager.isSummaryOfSuppressedGroup(entry.sbn)
-
/**
* Can a view be shown on the lockscreen when calculating the number of allowed notifications to
* show?
@@ -220,31 +213,12 @@ constructor(
private fun ExpandableView.canShowViewOnLockscreen(): Boolean {
if (hasNoContentHeight()) {
return false
- }
- if (this is ExpandableNotificationRow && !canShowRowOnLockscreen()) {
- return false
} else if (visibility == GONE) {
return false
}
return true
}
- /**
- * Can a row be shown on the lockscreen when calculating the number of allowed notifications to
- * show?
- *
- * @return true if it can be shown
- */
- private fun ExpandableNotificationRow.canShowRowOnLockscreen(): Boolean {
- if (isSummaryOfSuppressedGroup()) {
- return false
- }
- if (!lockscreenUserManager.shouldShowOnKeyguard(entry)) {
- return false
- }
- return !isRemoved
- }
-
private fun log(s: () -> String) {
if (DEBUG) {
Log.d(TAG, s())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index b46ed57fb866..aab5ff8d92e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -537,27 +537,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mPendingShowBouncer = false;
}
- @Override
- public void onStartedGoingToSleep(int why) {
- resetMode();
- mFadedAwayAfterWakeAndUnlock = false;
- mPendingAuthenticated = null;
- }
-
- @Override
- public void onFinishedGoingToSleep(int why) {
- Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
- if (mPendingAuthenticated != null) {
- PendingAuthenticated pendingAuthenticated = mPendingAuthenticated;
- // Post this to make sure it's executed after the device is fully locked.
- mHandler.post(() -> onBiometricAuthenticated(pendingAuthenticated.userId,
- pendingAuthenticated.biometricSourceType,
- pendingAuthenticated.isStrongBiometric));
- mPendingAuthenticated = null;
- }
- Trace.endSection();
- }
-
public boolean hasPendingAuthentication() {
return mPendingAuthenticated != null
&& mUpdateMonitor
@@ -752,13 +731,34 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
@VisibleForTesting
final WakefulnessLifecycle.Observer mWakefulnessObserver =
new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedWakingUp() {
- if (mPendingShowBouncer) {
- BiometricUnlockController.this.showBouncer();
- }
- }
- };
+ @Override
+ public void onFinishedWakingUp() {
+ if (mPendingShowBouncer) {
+ BiometricUnlockController.this.showBouncer();
+ }
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ resetMode();
+ mFadedAwayAfterWakeAndUnlock = false;
+ mPendingAuthenticated = null;
+ }
+
+ @Override
+ public void onFinishedGoingToSleep() {
+ Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
+ if (mPendingAuthenticated != null) {
+ PendingAuthenticated pendingAuthenticated = mPendingAuthenticated;
+ // Post this to make sure it's executed after the device is fully locked.
+ mHandler.post(() -> onBiometricAuthenticated(pendingAuthenticated.userId,
+ pendingAuthenticated.biometricSourceType,
+ pendingAuthenticated.isStrongBiometric));
+ mPendingAuthenticated = null;
+ }
+ Trace.endSection();
+ }
+ };
private final ScreenLifecycle.Observer mScreenObserver =
new ScreenLifecycle.Observer() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 7f1611fec551..347e05cc7f75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -1101,7 +1101,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
} else {
mQRCodeScannerButton.setVisibility(GONE);
- mIndicationArea.setPadding(0, 0, 0, 0);
+ if (mControlsButton.getVisibility() == GONE) {
+ mIndicationArea.setPadding(0, 0, 0, 0);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 95a2a6e75e7a..7d96240d0b36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -606,11 +606,47 @@ public class KeyguardBouncer {
mResetCallbacks.remove(callback);
}
+ /**
+ * Adds a callback to listen to bouncer expansion updates.
+ */
+ public void addBouncerExpansionCallback(BouncerExpansionCallback callback) {
+ if (!mExpansionCallbacks.contains(callback)) {
+ mExpansionCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Removes a previously added callback. If the callback was never added, this methood
+ * does nothing.
+ */
+ public void removeBouncerExpansionCallback(BouncerExpansionCallback callback) {
+ mExpansionCallbacks.remove(callback);
+ }
+
public interface BouncerExpansionCallback {
- void onFullyShown();
- void onStartingToHide();
- void onStartingToShow();
- void onFullyHidden();
+ /**
+ * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
+ */
+ default void onFullyShown() {
+ }
+
+ /**
+ * Invoked when the bouncer is starting to transition to a hidden state.
+ */
+ default void onStartingToHide() {
+ }
+
+ /**
+ * Invoked when the bouncer is starting to transition to a visible state.
+ */
+ default void onStartingToShow() {
+ }
+
+ /**
+ * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_HIDDEN}.
+ */
+ default void onFullyHidden() {
+ }
/**
* From 0f {@link KeyguardBouncer#EXPANSION_VISIBLE} when fully visible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 602d075b167d..83970dc15e61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -148,7 +148,7 @@ public class KeyguardClockPositionAlgorithm {
mStatusViewBottomMargin = res.getDimensionPixelSize(
R.dimen.keyguard_status_view_bottom_margin);
mSplitShadeTopNotificationsMargin =
- res.getDimensionPixelSize(R.dimen.split_shade_header_height);
+ res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
mSplitShadeTargetTopMargin =
res.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt
index 7555356af163..a5fcea789d3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt
@@ -31,35 +31,35 @@ import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
-import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
-import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_BATTERY_CONTROLLER
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_SHADE_HEADER
import java.io.FileDescriptor
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Named
@CentralSurfacesScope
-class SplitShadeHeaderController @Inject constructor(
- @Named(SPLIT_SHADE_HEADER) private val statusBar: View,
+class LargeScreenShadeHeaderController @Inject constructor(
+ @Named(LARGE_SCREEN_SHADE_HEADER) private val header: View,
private val statusBarIconController: StatusBarIconController,
private val privacyIconsController: HeaderPrivacyIconsController,
qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
featureFlags: FeatureFlags,
- @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController,
+ @Named(LARGE_SCREEN_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController,
dumpManager: DumpManager
) : Dumpable {
companion object {
private val HEADER_TRANSITION_ID = R.id.header_transition
- private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
+ private val LARGE_SCREEN_HEADER_TRANSITION_ID = R.id.large_screen_header_transition
private val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
private val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
- private val SPLIT_HEADER_CONSTRAINT = R.id.split_header_constraint
+ private val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint
private fun Int.stateToString() = when (this) {
QQS_HEADER_CONSTRAINT -> "QQS Header"
QS_HEADER_CONSTRAINT -> "QS Header"
- SPLIT_HEADER_CONSTRAINT -> "Split Header"
+ LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header"
else -> "Unknown state"
}
}
@@ -87,19 +87,19 @@ class SplitShadeHeaderController @Inject constructor(
onShadeExpandedChanged()
}
- var splitShadeMode = false
+ var active = false
set(value) {
if (field == value) {
return
}
field = value
- onSplitShadeModeChanged()
+ onHeaderStateChanged()
}
var shadeExpandedFraction = -1f
set(value) {
if (visible && field != value) {
- statusBar.alpha = ShadeInterpolation.getContentAlpha(value)
+ header.alpha = ShadeInterpolation.getContentAlpha(value)
field = value
}
}
@@ -123,53 +123,53 @@ class SplitShadeHeaderController @Inject constructor(
private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
override fun onChipVisibilityRefreshed(visible: Boolean) {
- if (statusBar is MotionLayout) {
- val state = statusBar.getConstraintSet(QQS_HEADER_CONSTRAINT).apply {
+ if (header is MotionLayout) {
+ val state = header.getConstraintSet(QQS_HEADER_CONSTRAINT).apply {
setAlpha(R.id.statusIcons, if (visible) 0f else 1f)
setAlpha(R.id.batteryRemainingIcon, if (visible) 0f else 1f)
}
- statusBar.updateState(QQS_HEADER_CONSTRAINT, state)
+ header.updateState(QQS_HEADER_CONSTRAINT, state)
}
}
}
init {
- if (statusBar is MotionLayout) {
- val context = statusBar.context
- val resources = statusBar.resources
- statusBar.getConstraintSet(QQS_HEADER_CONSTRAINT)
+ if (header is MotionLayout) {
+ val context = header.context
+ val resources = header.resources
+ header.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
- statusBar.getConstraintSet(QS_HEADER_CONSTRAINT)
+ header.getConstraintSet(QS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qs_header))
- statusBar.getConstraintSet(SPLIT_HEADER_CONSTRAINT)
- .load(context, resources.getXml(R.xml.split_header))
+ header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
+ .load(context, resources.getXml(R.xml.large_screen_shade_header))
privacyIconsController.chipVisibilityListener = chipVisibilityListener
}
}
init {
batteryMeterViewController.init()
- val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
+ val batteryIcon: BatteryMeterView = header.findViewById(R.id.batteryRemainingIcon)
// battery settings same as in QS icons
batteryMeterViewController.ignoreTunerUpdates()
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
- iconContainer = statusBar.findViewById(R.id.statusIcons)
+ iconContainer = header.findViewById(R.id.statusIcons)
iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
- iconManager.setTint(Utils.getColorAttrDefaultColor(statusBar.context,
+ iconManager.setTint(Utils.getColorAttrDefaultColor(header.context,
android.R.attr.textColorPrimary))
carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
listOf(
- statusBar.context.getString(com.android.internal.R.string.status_bar_no_calling),
- statusBar.context.getString(com.android.internal.R.string.status_bar_call_strength)
+ header.context.getString(com.android.internal.R.string.status_bar_no_calling),
+ header.context.getString(com.android.internal.R.string.status_bar_call_strength)
)
} else {
- listOf(statusBar.context.getString(com.android.internal.R.string.status_bar_mobile))
+ listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
}
qsCarrierGroupController = qsCarrierGroupControllerBuilder
- .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
+ .setQSCarrierGroup(header.findViewById(R.id.carrier_group))
.build()
dumpManager.registerDumpable(this)
@@ -179,8 +179,8 @@ class SplitShadeHeaderController @Inject constructor(
}
private fun updateScrollY() {
- if (!splitShadeMode && combinedHeaders) {
- statusBar.scrollY = qsScrollY
+ if (!active && combinedHeaders) {
+ header.scrollY = qsScrollY
}
}
@@ -194,8 +194,8 @@ class SplitShadeHeaderController @Inject constructor(
updatePosition()
}
- private fun onSplitShadeModeChanged() {
- if (splitShadeMode || combinedHeaders) {
+ private fun onHeaderStateChanged() {
+ if (active || combinedHeaders) {
privacyIconsController.onParentVisible()
} else {
privacyIconsController.onParentInvisible()
@@ -205,15 +205,15 @@ class SplitShadeHeaderController @Inject constructor(
}
private fun updateVisibility() {
- val visibility = if (!splitShadeMode && !combinedHeaders) {
+ val visibility = if (!active && !combinedHeaders) {
View.GONE
} else if (shadeExpanded) {
View.VISIBLE
} else {
View.INVISIBLE
}
- if (statusBar.visibility != visibility) {
- statusBar.visibility = visibility
+ if (header.visibility != visibility) {
+ header.visibility = visibility
visible = visibility == View.VISIBLE
}
}
@@ -222,20 +222,20 @@ class SplitShadeHeaderController @Inject constructor(
if (!combinedHeaders) {
return
}
- statusBar as MotionLayout
- if (splitShadeMode) {
- statusBar.setTransition(SPLIT_HEADER_TRANSITION_ID)
+ header as MotionLayout
+ if (active) {
+ header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
} else {
- statusBar.setTransition(HEADER_TRANSITION_ID)
- statusBar.transitionToStart()
+ header.setTransition(HEADER_TRANSITION_ID)
+ header.transitionToStart()
updatePosition()
updateScrollY()
}
}
private fun updatePosition() {
- if (statusBar is MotionLayout && !splitShadeMode && visible) {
- statusBar.setProgress(qsExpandedFraction)
+ if (header is MotionLayout && !active && visible) {
+ header.setProgress(qsExpandedFraction)
}
}
@@ -263,12 +263,12 @@ class SplitShadeHeaderController @Inject constructor(
pw.println("visible: $visible")
pw.println("shadeExpanded: $shadeExpanded")
pw.println("shadeExpandedFraction: $shadeExpandedFraction")
- pw.println("splitShadeMode: $splitShadeMode")
+ pw.println("active: $active")
pw.println("qsExpandedFraction: $qsExpandedFraction")
pw.println("qsScrollY: $qsScrollY")
if (combinedHeaders) {
- statusBar as MotionLayout
- pw.println("currentState: ${statusBar.currentState.stateToString()}")
+ header as MotionLayout
+ pw.println("currentState: ${header.currentState.stateToString()}")
}
}
}
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 6cca9045d867..ebdd257a9fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -187,6 +187,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.Utils;
import com.android.systemui.util.settings.SecureSettings;
@@ -311,7 +312,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
- private final SplitShadeHeaderController mSplitShadeHeaderController;
+ private final LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
private final RecordingController mRecordingController;
private final PanelEventsEmitter mPanelEventsEmitter;
private boolean mShouldUseSplitNotificationShade;
@@ -336,7 +337,7 @@ public class NotificationPanelViewController extends PanelViewController {
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationsQSContainerController mNotificationsQSContainerController;
private boolean mAnimateNextPositionUpdate;
- private float mQuickQsOffsetHeight;
+ private float mQuickQsHeaderHeight;
private ScreenOffAnimationController mScreenOffAnimationController;
private int mTrackingPointer;
@@ -387,7 +388,7 @@ public class NotificationPanelViewController extends PanelViewController {
private float mDownY;
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
- private int mSplitShadeStatusBarHeight;
+ private int mLargeScreenShadeHeaderHeight;
private int mSplitShadeNotificationsScrimMarginBottom;
private final KeyguardClockPositionAlgorithm
@@ -733,7 +734,7 @@ public class NotificationPanelViewController extends PanelViewController {
RecordingController recordingController,
@Main Executor uiExecutor,
SecureSettings secureSettings,
- SplitShadeHeaderController splitShadeHeaderController,
+ LargeScreenShadeHeaderController largeScreenShadeHeaderController,
ScreenOffAnimationController screenOffAnimationController,
LockscreenGestureLogger lockscreenGestureLogger,
PanelExpansionStateManager panelExpansionStateManager,
@@ -791,9 +792,9 @@ public class NotificationPanelViewController extends PanelViewController {
mFragmentService = fragmentService;
mSettingsChangeObserver = new SettingsChangeObserver(handler);
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(mResources);
+ LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
mView.setWillNotDraw(!DEBUG);
- mSplitShadeHeaderController = splitShadeHeaderController;
+ mLargeScreenShadeHeaderController = largeScreenShadeHeaderController;
mLayoutInflater = layoutInflater;
mFeatureFlags = featureFlags;
mFalsingManager = falsingManager;
@@ -1071,24 +1072,28 @@ public class NotificationPanelViewController extends PanelViewController {
}
public void updateResources() {
- mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
mSplitShadeNotificationsScrimMarginBottom =
mResources.getDimensionPixelSize(
R.dimen.split_shade_notifications_scrim_margin_bottom);
final boolean newShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(mResources);
+ LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
final boolean splitNotificationShadeChanged =
mShouldUseSplitNotificationShade != newShouldUseSplitNotificationShade;
mShouldUseSplitNotificationShade = newShouldUseSplitNotificationShade;
+ boolean useLargeScreenShadeHeader =
+ LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources());
if (mQs != null) {
mQs.setInSplitShade(mShouldUseSplitNotificationShade);
}
- mSplitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(mView.getContext());
- int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
+ mLargeScreenShadeHeaderHeight =
+ mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
+ mQuickQsHeaderHeight = useLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
+ SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
+ int topMargin = useLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
- mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade);
+ mLargeScreenShadeHeaderController.setActive(useLargeScreenShadeHeader);
mAmbientState.setStackTopMargin(topMargin);
mNotificationsQSContainerController.updateResources();
@@ -2268,9 +2273,9 @@ public class NotificationPanelViewController extends PanelViewController {
float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0
? mLockscreenShadeTransitionController.getQSDragProgress()
: getExpandedFraction();
- mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
- mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
- mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
+ mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
+ mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
+ mLargeScreenShadeHeaderController.setShadeExpanded(mQsVisible);
}
private void onStackYChanged(boolean shouldAnimate) {
@@ -2289,7 +2294,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void updateQSExpansionEnabledAmbient() {
- final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
+ final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsHeaderHeight;
mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
|| (mAmbientState.getScrollY() <= scrollRangeToTop);
setQsExpansionEnabled();
@@ -2315,7 +2320,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int calculateTopQsClippingBound(int qsPanelBottomY) {
int top;
if (mShouldUseSplitNotificationShade) {
- top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight);
+ top = Math.min(qsPanelBottomY, mLargeScreenShadeHeaderHeight);
} else {
if (mTransitioningToFullShadeProgress > 0.0f) {
// If we're transitioning, let's use the actual value. The else case
@@ -2452,8 +2457,8 @@ public class NotificationPanelViewController extends PanelViewController {
mQsTranslationForFullShadeTransition = qsTranslation;
updateQsFrameTranslation();
float currentTranslation = mQsFrame.getTranslationY();
- mQsClipTop = (int) (top - currentTranslation);
- mQsClipBottom = (int) (bottom - currentTranslation);
+ mQsClipTop = (int) (top - currentTranslation - mQsFrame.getTop());
+ mQsClipBottom = (int) (bottom - currentTranslation - mQsFrame.getTop());
mQsVisible = qsVisible;
mQs.setFancyClipping(
mQsClipTop,
@@ -2497,8 +2502,11 @@ public class NotificationPanelViewController extends PanelViewController {
private float getQSEdgePosition() {
// TODO: replace StackY with unified calculation
- return Math.max(mQuickQsOffsetHeight * mAmbientState.getExpansionFraction(),
- mAmbientState.getStackY() - mAmbientState.getScrollY());
+ return Math.max(mQuickQsHeaderHeight * mAmbientState.getExpansionFraction(),
+ mAmbientState.getStackY()
+ // need to adjust for extra margin introduced by large screen shade header
+ + mAmbientState.getStackTopMargin() * mAmbientState.getExpansionFraction()
+ - mAmbientState.getScrollY());
}
private int calculateQsBottomPosition(float qsExpansionFraction) {
@@ -3530,7 +3538,7 @@ public class NotificationPanelViewController extends PanelViewController {
public final QS.ScrollListener mScrollListener = new QS.ScrollListener() {
@Override
public void onQsPanelScrollChanged(int scrollY) {
- mSplitShadeHeaderController.setQsScrollY(scrollY);
+ mLargeScreenShadeHeaderController.setQsScrollY(scrollY);
if (scrollY > 0 && !mQsFullyExpanded) {
if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
// If we are scrolling QS, we should be fully expanded.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
index a15feeb9cda8..745228e72596 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -21,7 +21,7 @@ import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.shared.system.QuickStepContract
-import com.android.systemui.util.Utils
+import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.ViewController
import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.function.Consumer
@@ -50,7 +50,8 @@ class NotificationsQSContainerController @Inject constructor(
private var isQSCustomizing = false
private var isQSCustomizerAnimating = false
- private var splitShadeStatusBarHeight = 0
+ private var largeScreenShadeHeaderHeight = 0
+ private var largeScreenShadeHeaderActive = false
private var notificationsBottomMargin = 0
private var scrimShadeBottomMargin = 0
private var bottomStableInsets = 0
@@ -115,16 +116,18 @@ class NotificationsQSContainerController @Inject constructor(
}
fun updateResources() {
- val newSplitShadeEnabled = Utils.shouldUseSplitNotificationShade(resources)
+ val newSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
val splitShadeEnabledChanged = newSplitShadeEnabled != splitShadeEnabled
splitShadeEnabled = newSplitShadeEnabled
+ largeScreenShadeHeaderActive = LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)
notificationsBottomMargin = resources.getDimensionPixelSize(
R.dimen.notification_panel_margin_bottom)
- splitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(context)
+ largeScreenShadeHeaderHeight =
+ resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
panelMarginHorizontal = resources.getDimensionPixelSize(
R.dimen.notification_panel_margin_horizontal)
- topMargin = if (splitShadeEnabled) {
- splitShadeStatusBarHeight
+ topMargin = if (largeScreenShadeHeaderActive) {
+ largeScreenShadeHeaderHeight
} else {
resources.getDimensionPixelSize(R.dimen.notification_panel_margin_top)
}
@@ -229,13 +232,13 @@ class NotificationsQSContainerController @Inject constructor(
setKeyguardStatusViewConstraints(constraintSet)
setQsConstraints(constraintSet)
setNotificationsConstraints(constraintSet)
- setSplitShadeStatusBarConstraints(constraintSet)
+ setLargeScreenShadeHeaderConstraints(constraintSet)
mView.applyConstraints(constraintSet)
}
- private fun setSplitShadeStatusBarConstraints(constraintSet: ConstraintSet) {
- if (splitShadeEnabled) {
- constraintSet.constrainHeight(R.id.split_shade_status_bar, splitShadeStatusBarHeight)
+ private fun setLargeScreenShadeHeaderConstraints(constraintSet: ConstraintSet) {
+ if (largeScreenShadeHeaderActive) {
+ constraintSet.constrainHeight(R.id.split_shade_status_bar, largeScreenShadeHeaderHeight)
} else {
if (useCombinedQSHeaders) {
constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index a86ad6bc7012..5d38eea15723 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -31,10 +31,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks;
+import com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
-import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterModule;
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
@@ -133,9 +133,9 @@ public interface CentralSurfacesComponent {
CentralSurfacesCommandQueueCallbacks getCentralSurfacesCommandQueueCallbacks();
/**
- * Creates a SplitShadeHeaderController.
+ * Creates a {@link LargeScreenShadeHeaderController}.
*/
- SplitShadeHeaderController getSplitShadeHeaderController();
+ LargeScreenShadeHeaderController getLargeScreenShadeHeaderController();
/**
* Creates a new {@link CollapsedStatusBarFragment} each time it's called. See
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 79fe700191f7..6c6ec192646d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -73,9 +73,9 @@ import dagger.Provides;
@Module(subcomponents = StatusBarFragmentComponent.class)
public abstract class StatusBarViewModule {
- public static final String SPLIT_SHADE_HEADER = "split_shade_header";
+ public static final String LARGE_SCREEN_SHADE_HEADER = "large_screen_shade_header";
private static final String SPLIT_SHADE_BATTERY_VIEW = "split_shade_battery_view";
- public static final String SPLIT_SHADE_BATTERY_CONTROLLER = "split_shade_battery_controller";
+ public static final String LARGE_SCREEN_BATTERY_CONTROLLER = "split_shade_battery_controller";
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
/** */
@@ -159,15 +159,15 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @Named(SPLIT_SHADE_HEADER)
+ @Named(LARGE_SCREEN_SHADE_HEADER)
@CentralSurfacesComponent.CentralSurfacesScope
- public static View getSplitShadeStatusBarView(
+ public static View getLargeScreenShadeHeaderBarView(
NotificationShadeWindowView notificationShadeWindowView,
FeatureFlags featureFlags) {
ViewStub stub = notificationShadeWindowView.findViewById(R.id.qs_header_stub);
int layoutId = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
? R.layout.combined_qs_header
- : R.layout.split_shade_header;
+ : R.layout.large_screen_shade_header;
stub.setLayoutResource(layoutId);
View v = stub.inflate();
return v;
@@ -177,14 +177,15 @@ public abstract class StatusBarViewModule {
@Provides
@CentralSurfacesComponent.CentralSurfacesScope
public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
- @Named(SPLIT_SHADE_HEADER) View header) {
+ @Named(LARGE_SCREEN_SHADE_HEADER) View header) {
return header.findViewById(R.id.privacy_chip);
}
/** */
@Provides
@CentralSurfacesComponent.CentralSurfacesScope
- static StatusIconContainer providesStatusIconContainer(@Named(SPLIT_SHADE_HEADER) View header) {
+ static StatusIconContainer providesStatusIconContainer(
+ @Named(LARGE_SCREEN_SHADE_HEADER) View header) {
return header.findViewById(R.id.statusIcons);
}
@@ -192,13 +193,13 @@ public abstract class StatusBarViewModule {
@Provides
@CentralSurfacesComponent.CentralSurfacesScope
@Named(SPLIT_SHADE_BATTERY_VIEW)
- static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
+ static BatteryMeterView getBatteryMeterView(@Named(LARGE_SCREEN_SHADE_HEADER) View view) {
return view.findViewById(R.id.batteryRemainingIcon);
}
@Provides
@CentralSurfacesComponent.CentralSurfacesScope
- @Named(SPLIT_SHADE_BATTERY_CONTROLLER)
+ @Named(LARGE_SCREEN_BATTERY_CONTROLLER)
static BatteryMeterViewController getBatteryMeterViewController(
@Named(SPLIT_SHADE_BATTERY_VIEW) BatteryMeterView batteryMeterView,
ConfigurationController configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
index 31ef2f64e4a2..d5f2d210b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
@@ -20,7 +20,7 @@ import android.content.Context
import android.content.res.Configuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.Utils
+import com.android.systemui.util.LargeScreenUtils
import javax.inject.Inject
/**
@@ -42,7 +42,8 @@ class RemoteInputQuickSettingsDisabler @Inject constructor(
init {
isLandscape =
context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
- shouldUseSplitNotificationShade = Utils.shouldUseSplitNotificationShade(context.resources)
+ shouldUseSplitNotificationShade =
+ LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
configController.addCallback(this)
}
@@ -73,7 +74,7 @@ class RemoteInputQuickSettingsDisabler @Inject constructor(
needToRecompute = true
}
- val newSplitShadeFlag = Utils.shouldUseSplitNotificationShade(context.resources)
+ val newSplitShadeFlag = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
if (newSplitShadeFlag != shouldUseSplitNotificationShade) {
shouldUseSplitNotificationShade = newSplitShadeFlag
needToRecompute = true
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 4cb0a94e2585..ca5edb5c3fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -703,8 +703,7 @@ public class UserSwitcherController implements Dumpable {
if (mUsers.isEmpty()) return null;
UserRecord item = mUsers.stream().filter(x -> x.isCurrent).findFirst().orElse(null);
if (item == null || item.info == null) return null;
- if (item.isGuest) return mContext.getString(
- com.android.settingslib.R.string.guest_nickname);
+ if (item.isGuest) return mContext.getString(com.android.internal.R.string.guest_name);
return item.info.name;
}
@@ -941,7 +940,7 @@ public class UserSwitcherController implements Dumpable {
: com.android.settingslib.R.string.guest_exit_guest);
} else {
if (item.info != null) {
- return context.getString(com.android.settingslib.R.string.guest_nickname);
+ return context.getString(com.android.internal.R.string.guest_name);
} else {
if (mController.mGuestUserAutoCreated) {
// If mGuestIsResetting=true, we expect the guest user to be created
@@ -953,7 +952,7 @@ public class UserSwitcherController implements Dumpable {
return context.getString(
mController.mGuestIsResetting.get()
? com.android.settingslib.R.string.guest_resetting
- : com.android.settingslib.R.string.guest_nickname);
+ : com.android.internal.R.string.guest_name);
} else {
return context.getString(
com.android.settingslib.R.string.guest_new_guest);
@@ -961,7 +960,7 @@ public class UserSwitcherController implements Dumpable {
}
}
} else if (item.isAddUser) {
- return context.getString(R.string.user_add_user);
+ return context.getString(com.android.settingslib.R.string.user_add_user);
} else if (item.isAddSupervisedUser) {
return context.getString(R.string.add_user_supervised);
} else {
@@ -1128,7 +1127,7 @@ public class UserSwitcherController implements Dumpable {
super(context);
setTitle(mGuestUserAutoCreated
? com.android.settingslib.R.string.guest_reset_guest_dialog_title
- : R.string.guest_exit_guest_dialog_title);
+ : com.android.settingslib.R.string.guest_remove_guest_dialog_title);
setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
@@ -1165,8 +1164,8 @@ public class UserSwitcherController implements Dumpable {
public AddUserDialog(Context context) {
super(context);
- setTitle(R.string.user_add_user_title);
- setMessage(context.getString(R.string.user_add_user_message_short));
+ setTitle(com.android.settingslib.R.string.user_add_user_title);
+ setMessage(com.android.settingslib.R.string.user_add_user_message_short);
setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index c9de966080c5..cc6bf6a70d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -110,7 +110,7 @@ public class CreateUserActivity extends Activity {
},
null,
defaultUserName,
- getString(R.string.user_add_user),
+ getString(com.android.settingslib.R.string.user_add_user),
this::addUserNow,
this::finish
);
@@ -134,7 +134,7 @@ public class CreateUserActivity extends Activity {
mSetupUserDialog.dismiss();
userName = (userName == null || userName.trim().isEmpty())
- ? getString(R.string.user_new_user_name)
+ ? getString(com.android.settingslib.R.string.user_new_user_name)
: userName;
mUserCreator.createUser(userName, userIcon,
diff --git a/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt b/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
new file mode 100644
index 000000000000..8b29310cf1c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
@@ -0,0 +1,26 @@
+package com.android.systemui.util
+
+import android.content.res.Resources
+import com.android.systemui.R
+
+object LargeScreenUtils {
+
+ /**
+ * Returns true if the device should use the split notification shade, based on orientation and
+ * screen width.
+ */
+ @JvmStatic
+ fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
+ return resources.getBoolean(R.bool.config_use_split_notification_shade)
+ }
+
+ /**
+ * Returns true if we should use large screen shade header:
+ * [com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController]
+ * That should be true when we have enough horizontal space to show all info in one row.
+ */
+ @JvmStatic
+ fun shouldUseLargeScreenShadeHeader(resources: Resources): Boolean {
+ return resources.getBoolean(R.bool.config_use_large_screen_shade_header)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 7e3bce589f7e..8e5e1d2e1b87 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -25,12 +25,10 @@ import android.content.res.TypedArray;
import android.provider.Settings;
import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
-import android.view.View;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.CommandQueue;
import java.util.List;
import java.util.function.Consumer;
@@ -51,58 +49,6 @@ public class Utils {
}
/**
- * Sets the visibility of an UI element according to the DISABLE_* flags in
- * {@link android.app.StatusBarManager}.
- */
- public static class DisableStateTracker implements CommandQueue.Callbacks,
- View.OnAttachStateChangeListener {
- private final int mMask1;
- private final int mMask2;
- private final CommandQueue mCommandQueue;
- private View mView;
- private boolean mDisabled;
-
- public DisableStateTracker(int disableMask, int disable2Mask, CommandQueue commandQueue) {
- mMask1 = disableMask;
- mMask2 = disable2Mask;
- mCommandQueue = commandQueue;
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- mView = v;
- mCommandQueue.addCallback(this);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mCommandQueue.removeCallback(this);
- mView = null;
- }
-
- /**
- * Sets visibility of this {@link View} given the states passed from
- * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int, int)}.
- */
- @Override
- public void disable(int displayId, int state1, int state2, boolean animate) {
- if (displayId != mView.getDisplay().getDisplayId()) {
- return;
- }
- final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0);
- if (disabled == mDisabled) return;
- mDisabled = disabled;
- mView.setVisibility(disabled ? View.GONE : View.VISIBLE);
- }
-
- /** @return {@code true} if and only if this {@link View} is currently disabled */
- public boolean isDisabled() {
- return mDisabled;
- }
- }
-
-
- /**
* Returns {@code true} iff the package {@code packageName} is a headless remote display
* provider, i.e, that the package holds the privileged {@code REMOTE_DISPLAY_PROVIDER}
* permission and that it doesn't host a launcher icon.
@@ -151,17 +97,6 @@ public class Utils {
}
/**
- * Allow recommendations from smartspace to show in media controls.
- * Requires {@link #useQsMediaPlayer(Context)} to be enabled.
- * On by default, but can be disabled by setting to 0
- */
- public static boolean allowMediaRecommendations(Context context) {
- int flag = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1);
- return useQsMediaPlayer(context) && flag > 0;
- }
-
- /**
* Returns true if the device should use the collapsed layout for the media player when in
* landscape (or seascape) orientation
*/
@@ -170,14 +105,6 @@ public class Utils {
}
/**
- * Returns true if the device should use the split notification shade, based on orientation and
- * screen width.
- */
- public static boolean shouldUseSplitNotificationShade(Resources resources) {
- return resources.getBoolean(R.bool.config_use_split_notification_shade);
- }
-
- /**
* Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists,
* otherwise, returns the color from the private attribute {@param privAttrId}.
*/
@@ -197,33 +124,6 @@ public class Utils {
}
/**
- * Gets the {@link R.dimen#split_shade_header_height}.
- *
- * It should be fine to not ignore cutouts as split shade might not want to react to them:
- * for split shade header, which is only on bigger screens, either cutout won't be a problem
- * (it's usually centered and in split shade that's likely empty area) or we probably want to
- * handle it differently.
- */
- public static int getSplitShadeStatusBarHeight(Context context) {
- return context.getResources().getDimensionPixelSize(R.dimen.split_shade_header_height);
- }
-
- /**
- * Gets the {@link R.dimen#qs_header_system_icons_area_height}.
- *
- * It's the same as {@link com.android.internal.R.dimen#quick_qs_offset_height} except for
- * sw600dp-land.
- */
- public static int getQsHeaderSystemIconsAreaHeight(Context context) {
- final Resources res = context.getResources();
- if (Utils.shouldUseSplitNotificationShade(res)) {
- return res.getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height);
- } else {
- return SystemBarUtils.getQuickQsOffsetHeight(context);
- }
- }
-
- /**
* Gets the {@link R.dimen#status_bar_header_height_keyguard}.
*/
public static int getStatusBarHeaderHeightKeyguard(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 70728902f621..3c869e7ceb40 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -33,6 +33,7 @@ import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.util.settings.SecureSettings;
@@ -64,6 +65,7 @@ public class QuickAccessWalletController {
private final Context mContext;
private final Executor mExecutor;
private final Executor mCallbackExecutor;
+ private final Executor mBgExecutor;
private final SecureSettings mSecureSettings;
private final SystemClock mClock;
@@ -80,12 +82,14 @@ public class QuickAccessWalletController {
Context context,
@Main Executor executor,
@CallbackExecutor Executor callbackExecutor,
+ @Background Executor bgExecutor,
SecureSettings secureSettings,
QuickAccessWalletClient quickAccessWalletClient,
SystemClock clock) {
mContext = context;
mExecutor = executor;
mCallbackExecutor = callbackExecutor;
+ mBgExecutor = bgExecutor;
mSecureSettings = secureSettings;
mQuickAccessWalletClient = quickAccessWalletClient;
mClock = clock;
@@ -182,7 +186,7 @@ public class QuickAccessWalletController {
* Re-create the {@link QuickAccessWalletClient} of the controller.
*/
public void reCreateWalletClient() {
- mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext);
+ mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext, mBgExecutor);
mQawClientCreatedTimeMillis = mClock.elapsedRealtime();
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index c1f5516f02bb..2c901d285939 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -21,8 +21,11 @@ import android.content.Context;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.wallet.ui.WalletActivity;
+import java.util.concurrent.Executor;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -45,7 +48,8 @@ public abstract class WalletModule {
/** */
@SysUISingleton
@Provides
- public static QuickAccessWalletClient provideQuickAccessWalletClient(Context context) {
- return QuickAccessWalletClient.create(context);
+ public static QuickAccessWalletClient provideQuickAccessWalletClient(Context context,
+ @Background Executor bgExecutor) {
+ return QuickAccessWalletClient.create(context, bgExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 92e7f2d7c76b..89c31009a58e 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -122,7 +122,7 @@ public class WalletActivity extends LifecycleActivity implements
getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
WalletView walletView = requireViewById(R.id.wallet_view);
- mWalletClient = QuickAccessWalletClient.create(this);
+ mWalletClient = QuickAccessWalletClient.create(this, mExecutor);
mWalletScreenController = new WalletScreenController(
this,
walletView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewBoundAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index 214fd4d28398..8eb0918beedf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewBoundAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -19,7 +19,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
-class ViewBoundAnimatorTest : SysuiTestCase() {
+class ViewHierarchyAnimatorTest : SysuiTestCase() {
companion object {
private const val TEST_DURATION = 1000L
private val TEST_INTERPOLATOR = Interpolators.LINEAR
@@ -34,14 +34,14 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
@After
fun tearDown() {
- ViewBoundAnimator.stopAnimating(rootView)
+ ViewHierarchyAnimator.stopAnimating(rootView)
}
@Test
fun respectsAnimationParameters() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(
+ ViewHierarchyAnimator.animate(
rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
)
rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
@@ -56,7 +56,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun animatesFromStartToEnd() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -73,7 +73,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun animatesSuccessiveLayoutChanges() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -103,7 +103,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun animatesFromPreviousAnimationProgress() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animateNextUpdate(rootView, interpolator = TEST_INTERPOLATOR)
+ ViewHierarchyAnimator.animateNextUpdate(rootView, interpolator = TEST_INTERPOLATOR)
// Change all bounds.
rootView.layout(0 /* l */, 20 /* t */, 70 /* r */, 80 /* b */)
@@ -131,7 +131,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
firstChild.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
secondChild.layout(100 /* l */, 0 /* t */, 150 /* r */, 100 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
firstChild.layout(10 /* l */, 20 /* t */, 150 /* r */, 120 /* b */)
@@ -154,7 +154,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun doesNotAnimateInvisibleViews() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// GONE.
rootView.visibility = View.GONE
rootView.layout(0 /* l */, 15 /* t */, 55 /* r */, 80 /* b */)
@@ -171,7 +171,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun doesNotAnimateUnchangingBounds() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// No bounds are changed.
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
@@ -191,9 +191,9 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun doesNotAnimateExcludedBounds() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(
+ ViewHierarchyAnimator.animate(
rootView,
- bounds = setOf(ViewBoundAnimator.Bound.LEFT, ViewBoundAnimator.Bound.TOP),
+ bounds = setOf(ViewHierarchyAnimator.Bound.LEFT, ViewHierarchyAnimator.Bound.TOP),
interpolator = TEST_INTERPOLATOR
)
// Change all bounds.
@@ -211,7 +211,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun stopsAnimatingAfterSingleLayout() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animateNextUpdate(rootView)
+ ViewHierarchyAnimator.animateNextUpdate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -231,7 +231,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun stopsAnimatingWhenInstructed() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -240,7 +240,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
assertNull(rootView.getTag(R.id.tag_animator))
checkBounds(rootView, l = 0, t = 15, r = 70, b = 80)
- ViewBoundAnimator.stopAnimating(rootView)
+ ViewHierarchyAnimator.stopAnimating(rootView)
// Change all bounds again.
rootView.layout(10 /* l */, 10 /* t */, 50/* r */, 50 /* b */)
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 dcbdea0ac5a3..fa6288fb428c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,7 +17,10 @@
package com.android.systemui.dreams;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -25,12 +28,17 @@ import android.content.res.Resources;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import org.junit.Before;
import org.junit.Test;
@@ -67,6 +75,18 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
@Mock
Handler mHandler;
+ @Mock
+ BlurUtils mBlurUtils;
+
+ @Mock
+ StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ @Mock
+ KeyguardBouncer mBouncer;
+
+ @Mock
+ ViewRootImpl mViewRoot;
+
DreamOverlayContainerViewController mController;
@Before
@@ -75,12 +95,16 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+ when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(mBouncer);
+ when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);
mController = new DreamOverlayContainerViewController(
mDreamOverlayContainerView,
mComplicationHostViewController,
mDreamOverlayContentView,
mDreamOverlayStatusBarViewController,
+ mStatusBarKeyguardViewManager,
+ mBlurUtils,
mHandler,
MAX_BURN_IN_OFFSET,
BURN_IN_PROTECTION_UPDATE_INTERVAL,
@@ -125,4 +149,34 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
runnableCaptor.getValue().run();
verify(mHandler).postDelayed(runnableCaptor.getValue(), BURN_IN_PROTECTION_UPDATE_INTERVAL);
}
+
+ @Test
+ public void testBouncerAnimation_doesNotApply() {
+ final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
+ ArgumentCaptor.forClass(BouncerExpansionCallback.class);
+ mController.onViewAttached();
+ verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
+
+ bouncerExpansionCaptor.getValue().onExpansionChanged(0.5f);
+ verify(mBlurUtils, never()).applyBlur(eq(mViewRoot), anyInt(), eq(false));
+ verify(mDreamOverlayContainerView, never()).setAlpha(anyFloat());
+ }
+
+ @Test
+ public void testBouncerAnimation_updateBlurAndAlpha() {
+ final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
+ ArgumentCaptor.forClass(BouncerExpansionCallback.class);
+ mController.onViewAttached();
+ verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());
+
+ final float blurRadius = 1337f;
+ when(mBlurUtils.blurRadiusOfRatio(anyFloat())).thenReturn(blurRadius);
+
+ bouncerExpansionCaptor.getValue().onStartingToShow();
+ final float bouncerHideAmount = 0.1f;
+ bouncerExpansionCaptor.getValue().onExpansionChanged(bouncerHideAmount);
+ verify(mBlurUtils).blurRadiusOfRatio(1 - bouncerHideAmount);
+ verify(mBlurUtils).applyBlur(mViewRoot, (int) blurRadius, false);
+ verify(mDreamOverlayContainerView).setAlpha(bouncerHideAmount);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index ce92d5e30455..b08dd57f1c98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -31,6 +31,7 @@ import android.graphics.Region;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
+import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -111,6 +112,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
mFlingAnimationUtilsClosing,
TOUCH_REGION);
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
when(mCentralSurfaces.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -162,24 +164,58 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
- final float scrollAmount = .3f;
- final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
+ final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
+ verifyScroll(.3f, Direction.UP, true, gestureListener);
+
+ // Ensure that subsequent gestures are treated as expanding even if the bouncer state
+ // changes.
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+ verifyScroll(.7f, Direction.UP, true, gestureListener);
+ }
+
+ /**
+ * Makes sure collapse amount is proportional to scroll.
+ */
+ @Test
+ public void testCollapseAmount() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+ verifyScroll(.3f, Direction.DOWN, false, gestureListener);
+
+ // Ensure that subsequent gestures are treated as collapsing even if the bouncer state
+ // changes.
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
+ verifyScroll(.7f, Direction.DOWN, false, gestureListener);
+ }
+
+ private enum Direction {
+ DOWN,
+ UP,
+ }
+
+ private void verifyScroll(float percent, Direction direction, boolean expanding,
+ android.view.GestureDetector.OnGestureListener gestureListener) {
+
+ final float distanceY = SCREEN_HEIGHT_PX * percent;
final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX, 0);
+ 0, direction == Direction.UP ? SCREEN_HEIGHT_PX : 0, 0);
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX - distanceY, 0);
+ 0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0);
- assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, distanceY))
+ assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
.isTrue();
- // Ensure only called once
- verify(mStatusBarKeyguardViewManager)
- .onPanelExpansionChanged(anyFloat(), anyBoolean(), anyBoolean());
-
// Ensure correct expansion passed in.
verify(mStatusBarKeyguardViewManager)
- .onPanelExpansionChanged(eq(1 - scrollAmount), eq(false), eq(true));
+ .onPanelExpansionChanged(
+ eq(expanding ? 1 - percent : percent), eq(false), eq(true));
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 29f56e0d8bf0..a807407f170d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -345,6 +345,26 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
}
@Test
+ public void testPop() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final DreamTouchHandler.TouchSession.Callback callback =
+ Mockito.mock(DreamTouchHandler.TouchSession.Callback.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ session.registerCallback(callback);
+ session.pop();
+ environment.executeAll();
+
+ verify(callback).onRemoved();
+ }
+
+ @Test
public void testPause() {
final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 634d9e4642b3..edcf4791e6b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -16,8 +16,18 @@
package com.android.systemui.navigationbar;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -45,10 +55,15 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import dagger.Lazy;
+/**
+ * Tests for {@link NavBarHelper}.
+ */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NavBarHelperTest extends SysuiTestCase {
@@ -77,7 +92,11 @@ public class NavBarHelperTest extends SysuiTestCase {
DumpManager mDumpManager;
@Mock
NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+ private AccessibilityManager.AccessibilityServicesStateChangeListener
+ mAccessibilityServicesStateChangeListener;
+ private static final int ACCESSIBILITY_BUTTON_CLICKABLE_STATE =
+ SYSUI_STATE_A11Y_BUTTON_CLICKABLE | SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
private NavBarHelper mNavBarHelper;
@Before
@@ -87,6 +106,9 @@ public class NavBarHelperTest extends SysuiTestCase {
when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
when(mUserTracker.getUserId()).thenReturn(1);
+ doAnswer((invocation) -> mAccessibilityServicesStateChangeListener =
+ invocation.getArgument(0)).when(
+ mAccessibilityManager).addAccessibilityServicesStateChangeListener(any());
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
@@ -183,4 +205,42 @@ public class NavBarHelperTest extends SysuiTestCase {
verify(mNavbarTaskbarStateUpdater, times(1))
.updateAssistantAvailable(anyBoolean());
}
+
+ @Test
+ public void initNavBarHelper_buttonModeNavBar_a11yButtonClickableState() {
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON)).thenReturn(createFakeShortcutTargets());
+
+ mNavBarHelper.init();
+
+ assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(
+ ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
+ }
+
+ @Test
+ public void initAccessibilityStateWithFloatingMenuModeAndTargets_disableClickableState() {
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+ mNavBarHelper.init();
+
+ assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(/* disable_clickable_state */ 0);
+ }
+
+ @Test
+ public void onA11yServicesStateChangedWithMultipleServices_a11yButtonClickableState() {
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON)).thenReturn(createFakeShortcutTargets());
+ mAccessibilityServicesStateChangeListener.onAccessibilityServicesStateChanged(
+ mAccessibilityManager);
+
+ assertThat(mNavBarHelper.getA11yButtonState()).isEqualTo(
+ ACCESSIBILITY_BUTTON_CLICKABLE_STATE);
+ }
+
+ private List<String> createFakeShortcutTargets() {
+ return new ArrayList<>(List.of("a", "b", "c", "d"));
+ }
}
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 d1848e38ca06..9a4e10cec159 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
@@ -22,12 +22,10 @@ import android.view.View.VISIBLE
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.mockito.any
@@ -38,17 +36,13 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
class NotificationStackSizeCalculatorTest : SysuiTestCase() {
- @Mock private lateinit var groupManager: NotificationGroupManagerLegacy
-
- @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
-
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var stackLayout: NotificationStackScrollLayout
@@ -63,7 +57,6 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any()))
.thenReturn(GAP_HEIGHT)
- whenever(groupManager.isSummaryOfSuppressedGroup(any())).thenReturn(false)
with(testableResources) {
addOverride(R.integer.keyguard_max_notification_count, -1)
addOverride(R.dimen.notification_divider_height, NOTIFICATION_PADDING.toInt())
@@ -71,15 +64,13 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
sizeCalculator =
NotificationStackSizeCalculator(
- groupManager = groupManager,
- lockscreenUserManager = notificationLockscreenUserManager,
statusBarStateController = sysuiStatusBarStateController,
testableResources.resources)
}
@Test
fun computeMaxKeyguardNotifications_zeroSpace_returnZero() {
- val rows = listOf(createMockRow(height = ROW_HEIGHT, visibleOnLockscreen = true))
+ val rows = listOf(createMockRow(height = ROW_HEIGHT))
val maxNotifications =
computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f)
@@ -106,8 +97,8 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val spaceForOne = totalSpaceForEachRow
val rows =
listOf(
- createMockRow(rowHeight, visibleOnLockscreen = true),
- createMockRow(rowHeight, visibleOnLockscreen = true))
+ createMockRow(rowHeight),
+ createMockRow(rowHeight))
val maxNotifications =
computeMaxKeyguardNotifications(
@@ -124,8 +115,8 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val spaceForOne = totalSpaceForEachRow
val rows =
listOf(
- createMockRow(rowHeight, visibleOnLockscreen = true),
- createMockRow(rowHeight, visibleOnLockscreen = true))
+ createMockRow(rowHeight),
+ createMockRow(rowHeight))
val maxNotifications =
computeMaxKeyguardNotifications(
@@ -135,24 +126,15 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
- fun computeMaxKeyguardNotifications_invisibleOnLockscreen_returnsZero() {
- val rows = listOf(createMockRow(visibleOnLockscreen = false))
-
- val maxNotifications = computeMaxKeyguardNotifications(rows, Float.MAX_VALUE)
-
- assertThat(maxNotifications).isEqualTo(0)
- }
-
- @Test
fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
val rowHeight = ROW_HEIGHT
val totalSpaceForEachRow = GAP_HEIGHT + rowHeight
val spaceForTwo = totalSpaceForEachRow * 2 + NOTIFICATION_PADDING
val rows =
listOf(
- createMockRow(rowHeight, visibleOnLockscreen = true),
- createMockRow(rowHeight, visibleOnLockscreen = true),
- createMockRow(rowHeight, visibleOnLockscreen = true))
+ createMockRow(rowHeight),
+ createMockRow(rowHeight),
+ createMockRow(rowHeight))
val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f)
@@ -167,9 +149,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val availableSpace = totalSpaceForEachRow * 2
val rows =
listOf(
- createMockRow(rowHeight, visibleOnLockscreen = true),
- createMockRow(rowHeight, visibleOnLockscreen = true),
- createMockRow(rowHeight, visibleOnLockscreen = true))
+ createMockRow(rowHeight),
+ createMockRow(rowHeight),
+ createMockRow(rowHeight))
val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
assertThat(maxNotifications).isEqualTo(2)
@@ -178,31 +160,6 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT)
}
- @Test
- fun computeHeight_allInvisibleToLockscreen_NotInLockscreen_returnsHigherThanZero() {
- setOnLockscreen(false)
- val rowHeight = 10f
- setupChildren(listOf(createMockRow(rowHeight, visibleOnLockscreen = false)))
-
- val height =
- sizeCalculator.computeHeight(
- stackLayout, maxNotifications = Int.MAX_VALUE, SHELF_HEIGHT)
-
- assertThat(height).isGreaterThan(rowHeight)
- }
-
- @Test
- fun computeHeight_allInvisibleToLockscreen_onLockscreen_returnsZero() {
- setOnLockscreen(true)
- setupChildren(listOf(createMockRow(visibleOnLockscreen = false)))
-
- val height =
- sizeCalculator.computeHeight(
- stackLayout, maxNotifications = Int.MAX_VALUE, SHELF_HEIGHT)
-
- assertThat(height).isEqualTo(0)
- }
-
private fun computeMaxKeyguardNotifications(
rows: List<ExpandableView>,
availableSpace: Float,
@@ -222,14 +179,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
private fun createLockscreenRows(number: Int): List<ExpandableNotificationRow> =
- (1..number).map { createMockRow(visibleOnLockscreen = true) }.toList()
+ (1..number).map { createMockRow() }.toList()
private fun createMockRow(
height: Float = ROW_HEIGHT,
- visibleOnLockscreen: Boolean = true,
isRemoved: Boolean = false,
visibility: Int = VISIBLE,
- summaryOfSuppressed: Boolean = false
): ExpandableNotificationRow {
val row = mock(ExpandableNotificationRow::class.java)
val entry = mock(NotificationEntry::class.java)
@@ -238,24 +193,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
whenever(row.entry).thenReturn(entry)
whenever(row.isRemoved).thenReturn(isRemoved)
whenever(row.visibility).thenReturn(visibility)
- whenever(notificationLockscreenUserManager.shouldShowOnKeyguard(entry))
- .thenReturn(visibleOnLockscreen)
- whenever(groupManager.isSummaryOfSuppressedGroup(sbn)).thenReturn(summaryOfSuppressed)
whenever(row.getMinHeight(any())).thenReturn(height.toInt())
whenever(row.intrinsicHeight).thenReturn(height.toInt())
return row
}
- private fun setOnLockscreen(onLockscreen: Boolean) {
- whenever(sysuiStatusBarStateController.state)
- .thenReturn(
- if (onLockscreen) {
- KEYGUARD
- } else {
- SHADE
- })
- }
-
/** Default dimensions for tests that don't overwrite them. */
companion object {
const val GAP_HEIGHT = 12f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index ed144fa99369..142c2c1ed017 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -408,7 +408,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Test
public void onFinishedGoingToSleep_authenticatesWhenPending() {
when(mUpdateMonitor.isGoingToSleep()).thenReturn(true);
- mBiometricUnlockController.onFinishedGoingToSleep(-1);
+ mBiometricUnlockController.mWakefulnessObserver.onFinishedGoingToSleep();
verify(mHandler, never()).post(any());
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
@@ -416,7 +416,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
// value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(1 /* userId */,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- mBiometricUnlockController.onFinishedGoingToSleep(-1);
+ mBiometricUnlockController.mWakefulnessObserver.onFinishedGoingToSleep();
verify(mHandler).post(captor.capture());
captor.getValue().run();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index f391eff11ed2..4986792373db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN;
+import static com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -24,9 +27,11 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -53,6 +58,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
@@ -62,6 +68,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -79,7 +86,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Mock
private KeyguardHostViewController mKeyguardHostViewController;
@Mock
- private KeyguardBouncer.BouncerExpansionCallback mExpansionCallback;
+ private BouncerExpansionCallback mExpansionCallback;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -197,11 +204,11 @@ public class KeyguardBouncerTest extends SysuiTestCase {
mBouncer.ensureView();
mBouncer.setExpansion(0.5f);
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mBouncer.setExpansion(EXPANSION_HIDDEN);
verify(mFalsingCollector).onBouncerHidden();
verify(mExpansionCallback).onFullyHidden();
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ mBouncer.setExpansion(EXPANSION_VISIBLE);
verify(mFalsingCollector).onBouncerShown();
verify(mExpansionCallback).onFullyShown();
@@ -410,11 +417,11 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Test
public void testInTransit_whenTranslation() {
mBouncer.show(true);
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mBouncer.setExpansion(EXPANSION_HIDDEN);
assertThat(mBouncer.inTransit()).isFalse();
mBouncer.setExpansion(0.5f);
assertThat(mBouncer.inTransit()).isTrue();
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ mBouncer.setExpansion(EXPANSION_VISIBLE);
assertThat(mBouncer.inTransit()).isFalse();
}
@@ -435,4 +442,37 @@ public class KeyguardBouncerTest extends SysuiTestCase {
verify(mKeyguardHostViewController).updateKeyguardPosition(1.0f);
}
+
+ @Test
+ public void testExpansion_notifiesCallback() {
+ mBouncer.ensureView();
+ mBouncer.setExpansion(0.5f);
+
+ final BouncerExpansionCallback callback = mock(BouncerExpansionCallback.class);
+ mBouncer.addBouncerExpansionCallback(callback);
+
+ mBouncer.setExpansion(EXPANSION_HIDDEN);
+ verify(callback).onFullyHidden();
+ verify(callback).onExpansionChanged(EXPANSION_HIDDEN);
+
+ Mockito.clearInvocations(callback);
+ mBouncer.setExpansion(EXPANSION_VISIBLE);
+ verify(callback).onFullyShown();
+ verify(callback).onExpansionChanged(EXPANSION_VISIBLE);
+
+ Mockito.clearInvocations(callback);
+ float bouncerHideAmount = 0.9f;
+ // Ensure the callback only triggers once despite multiple calls to setExpansion
+ // with the same value.
+ mBouncer.setExpansion(bouncerHideAmount);
+ mBouncer.setExpansion(bouncerHideAmount);
+ verify(callback, times(1)).onStartingToHide();
+ verify(callback, times(1)).onExpansionChanged(bouncerHideAmount);
+
+ Mockito.clearInvocations(callback);
+ mBouncer.removeBouncerExpansionCallback(callback);
+ bouncerHideAmount = 0.5f;
+ mBouncer.setExpansion(bouncerHideAmount);
+ verify(callback, never()).onExpansionChanged(bouncerHideAmount);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 1827c7f0c0a6..6d3a5fecc826 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -279,7 +279,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void notifPaddingMakesUpToFullMarginInSplitShade() {
when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
.thenReturn(100);
- when(mResources.getDimensionPixelSize(R.dimen.split_shade_header_height))
+ when(mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height))
.thenReturn(70);
mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderControllerTest.kt
index 2b1826eab5aa..b086d681adf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderControllerTest.kt
@@ -27,7 +27,7 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class SplitShadeHeaderControllerTest : SysuiTestCase() {
+class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var view: View
@Mock private lateinit var statusIcons: StatusIconContainer
@@ -43,7 +43,7 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
- private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+ private lateinit var mLargeScreenShadeHeaderController: LargeScreenShadeHeaderController
private lateinit var carrierIconSlots: List<String>
@Before
@@ -62,7 +62,7 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(false)
- splitShadeHeaderController = SplitShadeHeaderController(
+ mLargeScreenShadeHeaderController = LargeScreenShadeHeaderController(
view,
statusBarIconController,
privacyIconsController,
@@ -76,11 +76,11 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
}
@Test
- fun setVisible_onlyInSplitShade() {
+ fun setVisible_onlyWhenActive() {
makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
- splitShadeHeaderController.splitShadeMode = false
+ mLargeScreenShadeHeaderController.active = false
assertThat(viewVisibility).isEqualTo(View.GONE)
}
@@ -94,7 +94,7 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
@Test
fun shadeExpandedFraction_updatesAlpha() {
makeShadeVisible()
- splitShadeHeaderController.shadeExpandedFraction = 0.5f
+ mLargeScreenShadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
@@ -117,7 +117,7 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
}
private fun makeShadeVisible() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ mLargeScreenShadeHeaderController.active = true
+ mLargeScreenShadeHeaderController.shadeExpanded = true
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 06b20380c0ab..21d03adb64ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -288,7 +288,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
- private SplitShadeHeaderController mSplitShadeHeaderController;
+ private LargeScreenShadeHeaderController mLargeScreenShadeHeaderController;
@Mock
private ContentResolver mContentResolver;
@Mock
@@ -498,7 +498,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mRecordingController,
mExecutor,
mSecureSettings,
- mSplitShadeHeaderController,
+ mLargeScreenShadeHeaderController,
mScreenOffAnimationController,
mLockscreenGestureLogger,
new PanelExpansionStateManager(),
@@ -918,6 +918,18 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
}
+ @Test
+ public void testLargeScreenHeaderMadeActiveForLargeScreen() {
+ mStatusBarStateController.setState(SHADE);
+ when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true);
+ mNotificationPanelViewController.updateResources();
+ verify(mLargeScreenShadeHeaderController).setActive(true);
+
+ when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false);
+ mNotificationPanelViewController.updateResources();
+ verify(mLargeScreenShadeHeaderController).setActive(false);
+ }
+
private void triggerPositionClockAndNotifications() {
mNotificationPanelViewController.closeQs();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index 9e7b6c514ca3..05a21db310fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -468,6 +468,29 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
}
@Test
+ fun testLargeScreenLayout_qsAndNotifsTopMarginIsOfHeaderHeight() {
+ setLargeScreen()
+ val largeScreenHeaderHeight = 100
+ overrideResource(R.dimen.large_screen_shade_header_height, largeScreenHeaderHeight)
+
+ controller.updateResources()
+
+ assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin)
+ .isEqualTo(largeScreenHeaderHeight)
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
+ .isEqualTo(largeScreenHeaderHeight)
+ }
+
+ @Test
+ fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
+ setSmallScreen()
+ controller.updateResources()
+ assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).topMargin)
+ .isEqualTo(0)
+ }
+
+ @Test
fun testSinglePaneShadeLayout_qsFrameHasHorizontalMarginsSetToCorrectValue() {
disableSplitShade()
controller.updateResources()
@@ -537,6 +560,18 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
controller.updateResources()
}
+ private fun setSmallScreen() {
+ setLargeScreenEnabled(false)
+ }
+
+ private fun setLargeScreen() {
+ setLargeScreenEnabled(true)
+ }
+
+ private fun setLargeScreenEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_large_screen_shade_header, enabled)
+ }
+
private fun given(
taskbarVisible: Boolean,
navigationMode: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index d8aef6668a27..68027450045b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -100,6 +100,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase {
mContext,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor(),
+ MoreExecutors.directExecutor(),
mSecureSettings,
mQuickAccessWalletClient,
mClock);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index e20b15a3e807..6846b2e3e6f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1006,12 +1006,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
break;
case ACTION_POINTER_UP:
- if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
mDispatcher.sendMotionEvent(
event, ACTION_UP, rawEvent, pointerIdBits, policyFlags);
- }
break;
case ACTION_UP:
if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) {
@@ -1146,6 +1144,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
* closet to an edge of the screen.
*/
private void computeDraggingPointerIdIfNeeded(MotionEvent event) {
+ if (event.getPointerCount() != 2) {
+ mDraggingPointerId = INVALID_POINTER_ID;
+ return;
+ }
if (mDraggingPointerId != INVALID_POINTER_ID) {
// If we have a valid pointer ID, we should be good
final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a65d5b3b94f7..312105a42b30 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1617,14 +1617,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
final int providerCount = mProviders.size();
for (int i = 0; i < providerCount; i++) {
Provider provider = mProviders.get(i);
- AppWidgetProviderInfo info = provider.getInfoLocked(mContext);
final String providerPackageName = provider.id.componentName.getPackageName();
- // Ignore an invalid provider, one not matching the filter,
- // or one that isn't in the given package, if any.
- boolean inPackage = packageName == null
- || providerPackageName.equals(packageName);
- if (provider.zombie || (info.widgetCategory & categoryFilter) == 0 || !inPackage) {
+ // Ignore an invalid provider or one that isn't in the given package, if any.
+ boolean inPackage = packageName == null || providerPackageName.equals(packageName);
+ if (provider.zombie || !inPackage) {
+ continue;
+ }
+
+ // Ignore the ones not matching the filter.
+ AppWidgetProviderInfo info = provider.getInfoLocked(mContext);
+ if ((info.widgetCategory & categoryFilter) == 0) {
continue;
}
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
deleted file mode 100644
index 715697d82cad..000000000000
--- a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill;
-
-import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
-
-import static com.android.server.autofill.Helper.sVerbose;
-
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.ICancellationSignal;
-import android.os.RemoteException;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.service.autofill.IFillCallback;
-import android.service.autofill.SaveInfo;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.inputmethod.InlineSuggestionsRequest;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AndroidFuture;
-
-import java.util.List;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Maintains a client suggestions session with the
- * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
- *
- */
-final class ClientSuggestionsSession {
-
- private static final String TAG = "ClientSuggestionsSession";
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
-
- private final int mSessionId;
- private final IAutoFillManagerClient mClient;
- private final Handler mHandler;
- private final ComponentName mComponentName;
-
- private final RemoteFillService.FillServiceCallbacks mCallbacks;
-
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private AndroidFuture<FillResponse> mPendingFillRequest;
- @GuardedBy("mLock")
- private int mPendingFillRequestId = INVALID_REQUEST_ID;
-
- ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
- ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
- mSessionId = sessionId;
- mClient = client;
- mHandler = handler;
- mComponentName = componentName;
- mCallbacks = callbacks;
- }
-
- void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
- final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
- final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
- final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
-
- mHandler.post(() -> {
- if (sVerbose) {
- Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
- }
-
- try {
- mClient.requestFillFromClient(requestId, inlineRequest,
- new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
- } catch (RemoteException e) {
- fillRequest.completeExceptionally(e);
- }
- });
-
- fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
- futureRef.set(fillRequest);
-
- synchronized (mLock) {
- mPendingFillRequest = fillRequest;
- mPendingFillRequestId = requestId;
- }
-
- fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
- synchronized (mLock) {
- mPendingFillRequest = null;
- mPendingFillRequestId = INVALID_REQUEST_ID;
- }
- if (err == null) {
- processAutofillId(res);
- mCallbacks.onFillRequestSuccess(requestId, res,
- mComponentName.getPackageName(), flags);
- } else {
- Slog.e(TAG, "Error calling on client fill request", err);
- if (err instanceof TimeoutException) {
- dispatchCancellationSignal(cancellationSink.get());
- mCallbacks.onFillRequestTimeout(requestId);
- } else if (err instanceof CancellationException) {
- dispatchCancellationSignal(cancellationSink.get());
- } else {
- mCallbacks.onFillRequestFailure(requestId, err.getMessage());
- }
- }
- }));
- }
-
- /**
- * Gets the application info for the component.
- */
- @Nullable
- static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
- try {
- ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
- comp.getPackageName(),
- PackageManager.GET_META_DATA,
- userId);
- if (si != null) {
- return si;
- }
- } catch (RemoteException e) {
- }
- return null;
- }
-
- /**
- * Gets the user-visible name of the application.
- */
- @Nullable
- @GuardedBy("mLock")
- static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
- return appInfo == null ? null : appInfo.loadSafeLabel(
- context.getPackageManager(), 0 /* do not ellipsize */,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
- }
-
- /**
- * Gets the user-visible icon of the application.
- */
- @Nullable
- @GuardedBy("mLock")
- static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
- return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
- }
-
- int cancelCurrentRequest() {
- synchronized (mLock) {
- return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
- ? mPendingFillRequestId
- : INVALID_REQUEST_ID;
- }
- }
-
- /**
- * The {@link AutofillId} which the client gets from its view is not contain the session id,
- * but Autofill framework is using the {@link AutofillId} with a session id. So before using
- * those ids in the Autofill framework, applies the current session id.
- *
- * @param res which response need to apply for a session id
- */
- private void processAutofillId(FillResponse res) {
- if (res == null) {
- return;
- }
-
- final List<Dataset> datasets = res.getDatasets();
- if (datasets != null && !datasets.isEmpty()) {
- for (int i = 0; i < datasets.size(); i++) {
- final Dataset dataset = datasets.get(i);
- if (dataset != null) {
- applySessionId(dataset.getFieldIds());
- }
- }
- }
-
- final SaveInfo saveInfo = res.getSaveInfo();
- if (saveInfo != null) {
- applySessionId(saveInfo.getOptionalIds());
- applySessionId(saveInfo.getRequiredIds());
- applySessionId(saveInfo.getSanitizerValues());
- applySessionId(saveInfo.getTriggerId());
- }
- }
-
- private void applySessionId(List<AutofillId> ids) {
- if (ids == null || ids.isEmpty()) {
- return;
- }
-
- for (int i = 0; i < ids.size(); i++) {
- applySessionId(ids.get(i));
- }
- }
-
- private void applySessionId(AutofillId[][] ids) {
- if (ids == null) {
- return;
- }
- for (int i = 0; i < ids.length; i++) {
- applySessionId(ids[i]);
- }
- }
-
- private void applySessionId(AutofillId[] ids) {
- if (ids == null) {
- return;
- }
- for (int i = 0; i < ids.length; i++) {
- applySessionId(ids[i]);
- }
- }
-
- private void applySessionId(AutofillId id) {
- if (id == null) {
- return;
- }
- id.setSessionId(mSessionId);
- }
-
- private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
- if (signal == null) {
- return;
- }
- try {
- signal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error requesting a cancellation", e);
- }
- }
-
- private class FillCallbackImpl extends IFillCallback.Stub {
- final AndroidFuture<FillResponse> mFillRequest;
- final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
- final AtomicReference<ICancellationSignal> mCancellationSink;
-
- FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
- AtomicReference<AndroidFuture<FillResponse>> futureRef,
- AtomicReference<ICancellationSignal> cancellationSink) {
- mFillRequest = fillRequest;
- mFutureRef = futureRef;
- mCancellationSink = cancellationSink;
- }
-
- @Override
- public void onCancellable(ICancellationSignal cancellation) {
- AndroidFuture<FillResponse> future = mFutureRef.get();
- if (future != null && future.isCancelled()) {
- dispatchCancellationSignal(cancellation);
- } else {
- mCancellationSink.set(cancellation);
- }
- }
-
- @Override
- public void onSuccess(FillResponse response) {
- mFillRequest.complete(response);
- }
-
- @Override
- public void onFailure(int requestId, CharSequence message) {
- String errorMessage = message == null ? "" : String.valueOf(message);
- mFillRequest.completeExceptionally(
- new RuntimeException(errorMessage));
- }
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 54183c98204e..75b6554a013b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -31,7 +31,6 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
-import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -62,7 +61,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -366,9 +364,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
- @Nullable
- private ClientSuggestionsSession mClientSuggestionsSession;
-
// TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
// new one per Session.
private final BroadcastReceiver mDelayedFillBroadcastReceiver =
@@ -461,9 +456,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/** Whether the current {@link FillResponse} is expired. */
private boolean mExpiredResponse;
- /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
- private boolean mClientSuggestionsEnabled;
-
/** Whether the fill dialog UI is disabled. */
private boolean mFillDialogDisabled;
}
@@ -494,21 +486,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mWaitForInlineRequest = inlineSuggestionsRequest != null;
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- mWaitForInlineRequest = inlineSuggestionsRequest != null;
- maybeRequestFillFromServiceLocked();
+ maybeRequestFillLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
} : null;
}
- void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
- mPendingFillRequest = null;
- mWaitForInlineRequest = inlineRequest != null;
- mPendingInlineSuggestionsRequest = inlineRequest;
- }
-
@GuardedBy("mLock")
- void maybeRequestFillFromServiceLocked() {
+ void maybeRequestFillLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -518,14 +503,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
- mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
- mPendingFillRequest.getFillContexts(),
- mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(),
- mPendingInlineSuggestionsRequest,
- mPendingFillRequest.getDelayedFillIntentSender());
- }
+ mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getDelayedFillIntentSender());
}
mLastFillRequest = mPendingFillRequest;
@@ -637,7 +618,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
: mDelayedFillPendingIntent.getIntentSender());
mPendingFillRequest = request;
- maybeRequestFillFromServiceLocked();
+ maybeRequestFillLocked();
}
if (mActivityToken != null) {
@@ -862,39 +843,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Cancels the last request sent to the {@link #mRemoteFillService} or the
- * {@link #mClientSuggestionsSession}.
+ * Cancels the last request sent to the {@link #mRemoteFillService}.
*/
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
- if (mRemoteFillService == null && mClientSuggestionsSession == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
- + "client suggestions session. mForAugmentedAutofillOnly: %s",
- mSessionFlags.mAugmentedAutofillOnly);
+ if (mRemoteFillService == null) {
+ wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
+ + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
return;
}
+ final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- if (mRemoteFillService != null) {
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
-
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
- }
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
}
}
}
-
- if (mClientSuggestionsSession != null) {
- mClientSuggestionsSession.cancelCurrentRequest();
- }
}
private boolean isViewFocusedLocked(int flags) {
@@ -959,30 +931,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// structure is taken. This causes only one fill request per burst of focus changes.
cancelCurrentRequestLocked();
- // Only ask IME to create inline suggestions request when
- // 1. Autofill provider supports it or client enabled client suggestions.
- // 2. The render service is available.
- // 3. The view is focused. (The view may not be focused if the autofill is triggered
- // manually.)
+ // Only ask IME to create inline suggestions request if Autofill provider supports it and
+ // the render service is available except the autofill is triggered manually and the view
+ // is also not focused.
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
+ if (mSessionFlags.mInlineSupportedByService
&& remoteRenderService != null
- && (isViewFocusedLocked(flags) || (isRequestSupportFillDialog(flags)))) {
- final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
- if (mSessionFlags.mClientSuggestionsEnabled) {
- final int finalRequestId = requestId;
- inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
- // Using client suggestions
- synchronized (mLock) {
- onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
- }
- viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
- };
- } else {
- inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
- viewState, /* isInlineRequest= */ true);
- }
+ && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
+ mAssistReceiver.newAutofillRequestLocked(viewState,
+ /* isInlineRequest= */ true);
if (inlineSuggestionsRequestConsumer != null) {
final AutofillId focusedId = mCurrentViewId;
final int requestIdCopy = requestId;
@@ -998,18 +957,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
- } else if (mSessionFlags.mClientSuggestionsEnabled) {
- // Request client suggestions for the dropdown mode
- onClientFillRequestLocked(requestId, null);
} else {
mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
}
- if (mSessionFlags.mClientSuggestionsEnabled) {
- // Using client suggestions, unnecessary request AssistStructure
- return;
- }
-
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
}
@@ -1069,13 +1020,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
-
synchronized (mLock) {
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
- mSessionFlags.mClientSuggestionsEnabled =
- (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
setClientLocked(client);
}
@@ -1187,13 +1135,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
- processNullResponseOrFallbackLocked(requestId, requestFlags);
+ processNullResponseLocked(requestId, requestFlags);
return;
}
fieldClassificationIds = response.getFieldClassificationIds();
- if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
- && !mService.isFieldClassificationEnabledLocked()) {
+ if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestId, requestFlags);
return;
@@ -1278,26 +1225,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- @GuardedBy("mLock")
- private void processNullResponseOrFallbackLocked(int requestId, int flags) {
- if (!mSessionFlags.mClientSuggestionsEnabled) {
- processNullResponseLocked(requestId, flags);
- return;
- }
-
- // fallback to the default platform password manager
- mSessionFlags.mClientSuggestionsEnabled = false;
-
- final InlineSuggestionsRequest inlineRequest =
- (mLastInlineSuggestionsRequest != null
- && mLastInlineSuggestionsRequest.first == requestId)
- ? mLastInlineSuggestionsRequest.second : null;
- mAssistReceiver.newAutofillRequestLocked(inlineRequest);
- requestAssistStructureLocked(requestId,
- flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
- return;
- }
-
// FillServiceCallbacks
@Override
public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
@@ -3269,22 +3196,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
filterText = value.getTextValue().toString();
}
- final CharSequence targetLabel;
- final Drawable targetIcon;
+ final CharSequence serviceLabel;
+ final Drawable serviceIcon;
synchronized (mLock) {
- if (mSessionFlags.mClientSuggestionsEnabled) {
- final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
- mService.getUserId());
- targetLabel = ClientSuggestionsSession.getAppLabelLocked(
- mService.getMaster().getContext(), appInfo);
- targetIcon = ClientSuggestionsSession.getAppIconLocked(
- mService.getMaster().getContext(), appInfo);
- } else {
- targetLabel = mService.getServiceLabelLocked();
- targetIcon = mService.getServiceIconLocked();
- }
+ serviceLabel = mService.getServiceLabelLocked();
+ serviceIcon = mService.getServiceIconLocked();
}
- if (targetLabel == null || targetIcon == null) {
+ if (serviceLabel == null || serviceIcon == null) {
wtf(null, "onFillReady(): no service label or icon");
return;
}
@@ -3315,7 +3233,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- targetLabel, targetIcon, this, id, mCompatMode);
+ serviceLabel, serviceIcon, this, id, mCompatMode);
synchronized (mLock) {
mService.logDatasetShown(id, mClientState, UI_TYPE_MENU);
@@ -3439,17 +3357,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
- final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
- if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
- || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
- if (sDebug) {
- Slog.d(TAG, "Inline suggestions not supported for "
- + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
- + ". Falling back to dropdown.");
- }
- return false;
- }
-
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService == null) {
@@ -3458,7 +3365,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(request, focusedId,
+ new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
filterText, remoteRenderService, userId, id);
InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
new InlineFillUi.InlineSuggestionUiCallback() {
@@ -4068,25 +3975,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- @GuardedBy("mLock")
- private void onClientFillRequestLocked(int requestId,
- InlineSuggestionsRequest inlineSuggestionsRequest) {
- if (mClientSuggestionsSession == null) {
- mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
- mComponentName, this);
- }
-
- if (mContexts == null) {
- mContexts = new ArrayList<>(1);
- }
-
- if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
- inlineSuggestionsRequest = null;
- }
-
- mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
- }
-
/**
* The result of checking whether to show the save dialog, when session can be saved.
*
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a2cfe4928249..af7dcd114066 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -38,7 +38,6 @@ import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT;
-import static android.os.storage.OnObbStateChangeListener.ERROR_INTERNAL;
import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
@@ -598,12 +597,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- /** List of crypto types.
- * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
- * corresponding commands in CommandListener.cpp */
- public static final String[] CRYPTO_TYPES
- = { "password", "default", "pattern", "pin" };
-
private final Context mContext;
private final ContentResolver mResolver;
@@ -622,18 +615,6 @@ class StorageManagerService extends IStorageManager.Stub
private final Callbacks mCallbacks;
private final LockPatternUtils mLockPatternUtils;
- /**
- * The size of the crypto algorithm key in bits for OBB files. Currently
- * Twofish is used which takes 128-bit keys.
- */
- private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
-
- /**
- * The number of times to run SHA1 in the PBKDF2 function for OBB files.
- * 1024 is reasonably secure and not too slow.
- */
- private static final int PBKDF2_HASH_ROUNDS = 1024;
-
private static final String ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY =
"anr_delay_millis";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 36fdb9d40e8e..c40f65f54210 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -575,6 +575,7 @@ public class ActivityManagerService extends IActivityManager.Stub
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+ static final String EXTRA_BUGREPORT_NONCE = "android.intent.extra.BUGREPORT_NONCE";
/**
* It is now required for apps to explicitly set either
@@ -6978,7 +6979,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Override
public void requestBugReport(@BugreportParams.BugreportMode int bugreportType) {
- requestBugReportWithDescription(null, null, bugreportType);
+ requestBugReportWithDescription(null, null, bugreportType, 0L);
}
/**
@@ -6988,6 +6989,15 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void requestBugReportWithDescription(@Nullable String shareTitle,
@Nullable String shareDescription, int bugreportType) {
+ requestBugReportWithDescription(shareTitle, shareDescription, bugreportType, /*nonce*/ 0L);
+ }
+
+ /**
+ * Takes a bugreport using bug report API ({@code BugreportManager}) which gets
+ * triggered by sending a broadcast to Shell.
+ */
+ public void requestBugReportWithDescription(@Nullable String shareTitle,
+ @Nullable String shareDescription, int bugreportType, long nonce) {
String type = null;
switch (bugreportType) {
case BugreportParams.BUGREPORT_MODE_FULL:
@@ -7038,6 +7048,7 @@ public class ActivityManagerService extends IActivityManager.Stub
triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
+ triggerShellBugreport.putExtra(EXTRA_BUGREPORT_NONCE, nonce);
triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
if (shareTitle != null) {
@@ -7104,8 +7115,8 @@ public class ActivityManagerService extends IActivityManager.Stub
* Takes a bugreport remotely
*/
@Override
- public void requestRemoteBugReport() {
- requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE);
+ public void requestRemoteBugReport(long nonce) {
+ requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE, nonce);
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a08ef4b5ab8d..a68fc05dbbf1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -96,6 +96,7 @@ import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
+import android.media.IDeviceVolumeBehaviorDispatcher;
import android.media.IMuteAwaitConnectionCallback;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
@@ -341,6 +342,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_ADD_ASSISTANT_SERVICE_UID = 44;
private static final int MSG_REMOVE_ASSISTANT_SERVICE_UID = 45;
private static final int MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID = 46;
+ private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -876,13 +878,23 @@ public class AudioService extends IAudioService.Stub
/** @hide */
public AudioService(Context context) {
- this(context, AudioSystemAdapter.getDefaultAdapter(),
+ this(context,
+ AudioSystemAdapter.getDefaultAdapter(),
SystemServerAdapter.getDefaultAdapter(context),
- SettingsAdapter.getDefaultAdapter());
+ SettingsAdapter.getDefaultAdapter(),
+ null);
}
+ /**
+ * @param context
+ * @param audioSystem Adapter for {@link AudioSystem}
+ * @param systemServer Adapter for privilieged functionality for system server components
+ * @param settings Adapter for {@link Settings}
+ * @param looper Looper to use for the service's message handler. If this is null, an
+ * {@link AudioSystemThread} is created as the messaging thread instead.
+ */
public AudioService(Context context, AudioSystemAdapter audioSystem,
- SystemServerAdapter systemServer, SettingsAdapter settings) {
+ SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper) {
sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
mContext = context;
mContentResolver = context.getContentResolver();
@@ -1010,7 +1022,11 @@ public class AudioService extends IAudioService.Stub
MAX_STREAM_VOLUME[AudioSystem.STREAM_SYSTEM];
}
- createAudioSystemThread();
+ if (looper == null) {
+ createAudioSystemThread();
+ } else {
+ mAudioHandler = new AudioHandler(looper);
+ }
AudioSystem.setErrorCallback(mAudioSystemCallback);
@@ -6457,34 +6473,41 @@ public class AudioService extends IAudioService.Stub
return;
}
- int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
- device.getType());
- setDeviceVolumeBehaviorInternal(audioSystemDeviceOut, deviceVolumeBehavior, pkgName);
-
- persistDeviceVolumeBehavior(audioSystemDeviceOut, deviceVolumeBehavior);
+ setDeviceVolumeBehaviorInternal(device, deviceVolumeBehavior, pkgName);
+ persistDeviceVolumeBehavior(device.getInternalType(), deviceVolumeBehavior);
}
- private void setDeviceVolumeBehaviorInternal(int audioSystemDeviceOut,
+ private void setDeviceVolumeBehaviorInternal(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @NonNull String caller) {
+ int audioSystemDeviceOut = device.getInternalType();
+ boolean volumeBehaviorChanged = false;
// update device masks based on volume behavior
switch (deviceVolumeBehavior) {
case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE:
- removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut);
- removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
+ volumeBehaviorChanged |=
+ removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
+ | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
break;
case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
- removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut);
- addAudioSystemDeviceOutToFixedVolumeDevices(audioSystemDeviceOut);
+ volumeBehaviorChanged |=
+ removeAudioSystemDeviceOutFromFullVolumeDevices(audioSystemDeviceOut)
+ | addAudioSystemDeviceOutToFixedVolumeDevices(audioSystemDeviceOut);
break;
case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
- addAudioSystemDeviceOutToFullVolumeDevices(audioSystemDeviceOut);
- removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
+ volumeBehaviorChanged |=
+ addAudioSystemDeviceOutToFullVolumeDevices(audioSystemDeviceOut)
+ | removeAudioSystemDeviceOutFromFixedVolumeDevices(audioSystemDeviceOut);
break;
case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
throw new IllegalArgumentException("Absolute volume unsupported for now");
}
+ if (volumeBehaviorChanged) {
+ sendMsg(mAudioHandler, MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR, SENDMSG_QUEUE,
+ deviceVolumeBehavior, 0, device, /*delay*/ 0);
+ }
+
// log event and caller
sDeviceLogger.log(new AudioEventLogger.StringEvent(
"Volume behavior " + deviceVolumeBehavior + " for dev=0x"
@@ -7761,6 +7784,14 @@ public class AudioService extends IAudioService.Stub
/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
+ AudioHandler() {
+ super();
+ }
+
+ AudioHandler(Looper looper) {
+ super(looper);
+ }
+
private void setAllVolumes(VolumeStreamState streamState) {
// Apply volume
@@ -8063,6 +8094,10 @@ public class AudioService extends IAudioService.Stub
case MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID:
updateActiveAssistantServiceUids();
break;
+
+ case MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR:
+ dispatchDeviceVolumeBehavior((AudioDeviceAttributes) msg.obj, msg.arg1);
+ break;
}
}
}
@@ -9080,6 +9115,35 @@ public class AudioService extends IAudioService.Stub
mMuteAwaitConnectionDispatchers.finishBroadcast();
}
+ final RemoteCallbackList<IDeviceVolumeBehaviorDispatcher> mDeviceVolumeBehaviorDispatchers =
+ new RemoteCallbackList<IDeviceVolumeBehaviorDispatcher>();
+
+ /**
+ * @see AudioDeviceVolumeManager#addOnDeviceVolumeBehaviorChangedListener and
+ * AudioDeviceVolumeManager#removeOnDeviceVolumeBehaviorChangedListener
+ */
+ public void registerDeviceVolumeBehaviorDispatcher(boolean register,
+ @NonNull IDeviceVolumeBehaviorDispatcher dispatcher) {
+ enforceQueryStateOrModifyRoutingPermission();
+ Objects.requireNonNull(dispatcher);
+ if (register) {
+ mDeviceVolumeBehaviorDispatchers.register(dispatcher);
+ } else {
+ mDeviceVolumeBehaviorDispatchers.unregister(dispatcher);
+ }
+ }
+
+ private void dispatchDeviceVolumeBehavior(AudioDeviceAttributes device, int volumeBehavior) {
+ final int dispatchers = mDeviceVolumeBehaviorDispatchers.beginBroadcast();
+ for (int i = 0; i < dispatchers; i++) {
+ try {
+ mDeviceVolumeBehaviorDispatchers.getBroadcastItem(i)
+ .dispatchDeviceVolumeBehaviorChanged(device, volumeBehavior);
+ } catch (RemoteException e) {
+ }
+ }
+ mDeviceVolumeBehaviorDispatchers.finishBroadcast();
+ }
//==========================================================================================
// Device orientation
@@ -9310,14 +9374,20 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.d(TAG, "CEC sink: setting HDMI as full vol device");
}
- addAudioSystemDeviceOutToFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
+ setDeviceVolumeBehaviorInternal(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ "AudioService.updateHdmiCecSinkLocked()");
} else {
if (DEBUG_VOL) {
Log.d(TAG, "TV, no CEC: setting HDMI as regular vol device");
}
// Android TV devices without CEC service apply software volume on
// HDMI output
- removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
+ setDeviceVolumeBehaviorInternal(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ "AudioService.updateHdmiCecSinkLocked()");
}
postUpdateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
"HdmiPlaybackClient.DisplayStatusCallback");
@@ -11463,8 +11533,8 @@ public class AudioService extends IAudioService.Stub
continue;
}
- setDeviceVolumeBehaviorInternal(deviceType, deviceVolumeBehavior,
- "AudioService.restoreDeviceVolumeBehavior()");
+ setDeviceVolumeBehaviorInternal(new AudioDeviceAttributes(deviceType, ""),
+ deviceVolumeBehavior, "AudioService.restoreDeviceVolumeBehavior()");
}
}
@@ -11479,36 +11549,36 @@ public class AudioService extends IAudioService.Stub
!= AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
}
- private void addAudioSystemDeviceOutToFixedVolumeDevices(int audioSystemDeviceOut) {
+ private boolean addAudioSystemDeviceOutToFixedVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " to mFixedVolumeDevices");
}
- mFixedVolumeDevices.add(audioSystemDeviceOut);
+ return mFixedVolumeDevices.add(audioSystemDeviceOut);
}
- private void removeAudioSystemDeviceOutFromFixedVolumeDevices(int audioSystemDeviceOut) {
+ private boolean removeAudioSystemDeviceOutFromFixedVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " from mFixedVolumeDevices");
}
- mFixedVolumeDevices.remove(audioSystemDeviceOut);
+ return mFixedVolumeDevices.remove(audioSystemDeviceOut);
}
- private void addAudioSystemDeviceOutToFullVolumeDevices(int audioSystemDeviceOut) {
+ private boolean addAudioSystemDeviceOutToFullVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " to mFullVolumeDevices");
}
- mFullVolumeDevices.add(audioSystemDeviceOut);
+ return mFullVolumeDevices.add(audioSystemDeviceOut);
}
- private void removeAudioSystemDeviceOutFromFullVolumeDevices(int audioSystemDeviceOut) {
+ private boolean removeAudioSystemDeviceOutFromFullVolumeDevices(int audioSystemDeviceOut) {
if (DEBUG_VOL) {
Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " from mFullVolumeDevices");
}
- mFullVolumeDevices.remove(audioSystemDeviceOut);
+ return mFullVolumeDevices.remove(audioSystemDeviceOut);
}
//====================
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 45c5116bea59..fff66629048d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -284,6 +284,17 @@ public class Installer extends SystemService {
}
/**
+ * Sets in Installd that it is first boot after data wipe
+ */
+ public void setFirstBoot() throws InstallerException {
+ try {
+ mInstalld.setFirstBoot();
+ } catch (RemoteException e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ /**
* Class that collects multiple {@code installd} operations together in an
* attempt to more efficiently execute them in bulk.
* <p>
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c7a101e2af4d..4c7243decb07 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1900,6 +1900,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService
/* excludePreCreated= */ false));
t.traceEnd();
+ if (mFirstBoot) {
+ t.traceBegin("setFirstBoot: ");
+ try {
+ mInstaller.setFirstBoot();
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Could not set First Boot: ", e);
+ }
+ t.traceEnd();
+ }
+
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
mPermissionManager.readLegacyPermissionStateTEMP();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 15753cd24e85..581e4e26da81 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -188,6 +188,8 @@ class PackageManagerShellCommand extends ShellCommand {
return runDump();
case "list":
return runList();
+ case "gc":
+ return runGc();
case "resolve-activity":
return runResolveActivity();
case "query-activities":
@@ -687,6 +689,13 @@ class PackageManagerShellCommand extends ShellCommand {
return -1;
}
+ private int runGc() throws RemoteException {
+ Runtime.getRuntime().gc();
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Ok");
+ return 0;
+ }
+
private int runListFeatures() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final List<FeatureInfo> list = mInterface.getSystemAvailableFeatures().getList();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 7a73412c9b9f..b2b59f194104 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -26,7 +26,6 @@ import android.app.appsearch.AppSearchSession;
import android.app.appsearch.BatchResultCallback;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByDocumentIdRequest;
-import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.RemoveByDocumentIdRequest;
import android.app.appsearch.ReportUsageRequest;
@@ -184,11 +183,6 @@ class ShortcutPackage extends ShortcutPackageItem {
private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
/**
- * All external packages that have gained access to the shortcuts from this package
- */
- private final Map<String, PackageIdentifier> mPackageIdentifiers = new ArrayMap<>(0);
-
- /**
* # of times the package has called rate-limited APIs.
*/
private int mApiCallCount;
@@ -2316,14 +2310,15 @@ class ShortcutPackage extends ShortcutPackageItem {
}
SetSchemaRequest.Builder schemaBuilder = new SetSchemaRequest.Builder()
.addSchemas(AppSearchShortcutPerson.SCHEMA, AppSearchShortcutInfo.SCHEMA)
- .setForceOverride(true);
- for (PackageIdentifier pi : mPackageIdentifiers.values()) {
- schemaBuilder = schemaBuilder
- .setSchemaTypeVisibilityForPackage(
- AppSearchShortcutPerson.SCHEMA_TYPE, true, pi)
- .setSchemaTypeVisibilityForPackage(
- AppSearchShortcutInfo.SCHEMA_TYPE, true, pi);
- }
+ .setForceOverride(true)
+ .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutInfo.SCHEMA_TYPE,
+ Collections.singleton(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+ .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutInfo.SCHEMA_TYPE,
+ Collections.singleton(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA))
+ .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutPerson.SCHEMA_TYPE,
+ Collections.singleton(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA))
+ .addRequiredPermissionsForSchemaTypeVisibility(AppSearchShortcutPerson.SCHEMA_TYPE,
+ Collections.singleton(SetSchemaRequest.READ_ASSISTANT_APP_SEARCH_DATA));
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
session.setSchema(
schemaBuilder.build(), mExecutor, mShortcutUser.mExecutor, result -> {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index f02f9f98d933..8ecc51b3087c 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -47,12 +47,16 @@ final class Vibration {
FINISHED,
FINISHED_UNEXPECTED, // Didn't terminate in the usual way.
FORWARDED_TO_INPUT_DEVICES,
- CANCELLED,
+ CANCELLED_BINDER_DIED,
+ CANCELLED_BY_SCREEN_OFF,
+ CANCELLED_BY_SETTINGS_UPDATE,
+ CANCELLED_BY_USER,
+ CANCELLED_BY_UNKNOWN_REASON,
+ CANCELLED_SUPERSEDED,
IGNORED_ERROR_APP_OPS,
IGNORED_ERROR_CANCELLING,
IGNORED_ERROR_SCHEDULING,
IGNORED_ERROR_TOKEN,
- IGNORED,
IGNORED_APP_OPS,
IGNORED_BACKGROUND,
IGNORED_UNKNOWN_VIBRATION,
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index e12426b2b02c..e3d806755c6e 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -79,12 +79,14 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final IntArray mSignalVibratorsComplete;
+ @Nullable
@GuardedBy("mLock")
- private boolean mSignalCancel = false;
+ private Vibration.Status mSignalCancelStatus = null;
@GuardedBy("mLock")
private boolean mSignalCancelImmediate = false;
- private boolean mCancelled = false;
+ @Nullable
+ private Vibration.Status mCancelStatus = null;
private boolean mCancelledImmediately = false; // hard stop
private int mPendingVibrateSteps;
private int mRemainingStartSequentialEffectSteps;
@@ -185,8 +187,8 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
expectIsVibrationThread(true);
}
- if (mCancelled) {
- return Vibration.Status.CANCELLED;
+ if (mCancelStatus != null) {
+ return mCancelStatus;
}
if (mPendingVibrateSteps > 0
|| mRemainingStartSequentialEffectSteps > 0) {
@@ -303,7 +305,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (DEBUG) {
Slog.d(TAG, "Binder died, cancelling vibration...");
}
- notifyCancelled(/* immediate= */ false);
+ notifyCancelled(Vibration.Status.CANCELLED_BINDER_DIED, /* immediate= */ false);
}
/**
@@ -312,22 +314,41 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
*
* @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
*/
- public void notifyCancelled(boolean immediate) {
+ public void notifyCancelled(@NonNull Vibration.Status cancelStatus, boolean immediate) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(false);
}
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel requested with status=" + cancelStatus
+ + ", immediate=" + immediate);
+ }
+ if ((cancelStatus == null) || !cancelStatus.name().startsWith("CANCEL")) {
+ Slog.w(TAG, "Vibration cancel requested with bad status=" + cancelStatus
+ + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation.");
+ cancelStatus = Vibration.Status.CANCELLED_BY_UNKNOWN_REASON;
+ }
synchronized (mLock) {
- if (immediate && mSignalCancelImmediate || mSignalCancel) {
- // Nothing to update: already cancelled previously.
+ if (immediate && mSignalCancelImmediate || (mSignalCancelStatus != null)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel request ignored as the vibration "
+ + mVibration.id + "is already being cancelled with status="
+ + mSignalCancelStatus + ", immediate=" + mSignalCancelImmediate);
+ }
return;
}
mSignalCancelImmediate |= immediate;
- mSignalCancel = true;
+ if (mSignalCancelStatus == null) {
+ mSignalCancelStatus = cancelStatus;
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel request new status=" + cancelStatus
+ + " ignored as the vibration was already cancelled with status="
+ + mSignalCancelStatus + ", but immediate flag was updated to "
+ + mSignalCancelImmediate);
+ }
+ }
mLock.notify();
}
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancel requested, immediate=" + immediate);
- }
}
/**
@@ -380,7 +401,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals.
}
- return (mSignalCancel && !mCancelled)
+ return (mSignalCancelStatus != mCancelStatus)
|| (mSignalCancelImmediate && !mCancelledImmediately)
|| (mSignalVibratorsComplete.size() > 0);
}
@@ -395,7 +416,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
}
int[] vibratorsToProcess = null;
- boolean doCancel = false;
+ Vibration.Status doCancelStatus = null;
boolean doCancelImmediate = false;
// Collect signals to process, but don't keep the lock while processing them.
synchronized (mLock) {
@@ -405,9 +426,10 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
}
// This should only happen once.
doCancelImmediate = true;
+ doCancelStatus = mSignalCancelStatus;
}
- if (mSignalCancel && !mCancelled) {
- doCancel = true;
+ if (mSignalCancelStatus != mCancelStatus) {
+ doCancelStatus = mSignalCancelStatus;
}
if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
// Swap out the queue of completions to process.
@@ -421,11 +443,11 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
// completion signals that were collected in this call, but we won't process them
// anyway as all steps are cancelled.
if (doCancelImmediate) {
- processCancelImmediately();
+ processCancelImmediately(doCancelStatus);
return;
}
- if (doCancel) {
- processCancel();
+ if (doCancelStatus != null) {
+ processCancel(doCancelStatus);
}
if (vibratorsToProcess != null) {
processVibratorsComplete(vibratorsToProcess);
@@ -438,12 +460,12 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
* <p>This will remove all steps and replace them with respective results of
* {@link Step#cancel()}.
*/
- public void processCancel() {
+ public void processCancel(Vibration.Status cancelStatus) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- mCancelled = true;
+ mCancelStatus = cancelStatus;
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
// and clean up steps are added back to the queue, so they can handle the callback.
List<Step> cleanUpSteps = new ArrayList<>();
@@ -461,13 +483,13 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
*
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
*/
- public void processCancelImmediately() {
+ public void processCancelImmediately(Vibration.Status cancelStatus) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
mCancelledImmediately = true;
- mCancelled = true;
+ mCancelStatus = cancelStatus;
Step step;
while ((step = pollNext()) != null) {
step.cancelImmediately();
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 1f08b35d5540..f749773d14a0 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -162,10 +162,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// When the system is entering a non-interactive state, we want to cancel
// vibrations in case a misbehaving app has abandoned them.
if (shouldCancelOnScreenOffLocked(mNextVibration)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_SCREEN_OFF);
}
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SCREEN_OFF,
+ /* immediate= */ false);
}
}
}
@@ -426,7 +427,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
final long ident = Binder.clearCallingIdentity();
try {
if (mCurrentVibration != null) {
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ /* immediate= */ false);
}
Vibration.Status status = startVibrationLocked(vib);
if (status != Vibration.Status.RUNNING) {
@@ -459,19 +461,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mNextVibration != null
&& shouldCancelVibration(mNextVibration.getVibration(),
usageFilter, token)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_USER);
}
if (mCurrentVibration != null
&& shouldCancelVibration(mCurrentVibration.getVibration(),
usageFilter, token)) {
- mCurrentVibration.notifyCancelled(/* immediate= */false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_USER,
+ /* immediate= */false);
}
if (mCurrentExternalVibration != null
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_BY_USER,
/* continueExternalControl= */ false);
}
} finally {
@@ -600,7 +603,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.d(TAG, "Canceling vibration because settings changed: "
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
}
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE,
+ /* immediate= */ false);
}
}
}
@@ -1319,7 +1323,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (DEBUG) {
Slog.d(TAG, "External vibration finished because binder died");
}
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_BINDER_DIED,
/* continueExternalControl= */ false);
}
}
@@ -1543,7 +1547,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// vibration that may be playing and ready the vibrator for external control.
if (mCurrentVibration != null) {
clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
- mCurrentVibration.notifyCancelled(/* immediate= */ true);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ /* immediate= */ true);
waitForCompletion = true;
}
} else {
@@ -1557,7 +1562,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// would need to mute the old one still if it came from a different controller.
alreadyUnderExternalControl = true;
mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_SUPERSEDED,
/* continueExternalControl= */ true);
}
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4bef126ee2c8..94f86462045a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -116,6 +116,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -155,6 +156,7 @@ import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
+import static com.android.server.wm.ActivityRecordProto.LAST_DROP_INPUT_MODE;
import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING;
import static com.android.server.wm.ActivityRecordProto.MIN_ASPECT_RATIO;
import static com.android.server.wm.ActivityRecordProto.NAME;
@@ -216,7 +218,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
@@ -786,6 +787,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** The last set {@link DropInputMode} for this activity surface. */
@DropInputMode
private int mLastDropInputMode = DropInputMode.NONE;
+ /** Whether the input to this activity will be dropped during the current playing animation. */
+ private boolean mIsInputDroppedForAnimation;
/**
* If it is non-null, it requires all activities who have the same starting data to be drawn
@@ -1567,6 +1570,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ /** Sets if all input will be dropped as a protection during the client-driven animation. */
+ void setDropInputForAnimation(boolean isInputDroppedForAnimation) {
+ if (mIsInputDroppedForAnimation == isInputDroppedForAnimation) {
+ return;
+ }
+ mIsInputDroppedForAnimation = isInputDroppedForAnimation;
+ updateUntrustedEmbeddingInputProtection();
+ }
+
/**
* Sets to drop input when obscured to activity if it is embedded in untrusted mode.
*
@@ -1576,11 +1588,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* all untrusted activities.
*/
private void updateUntrustedEmbeddingInputProtection() {
- final SurfaceControl sc = getSurfaceControl();
- if (sc == null) {
+ if (getSurfaceControl() == null) {
return;
}
- if (isEmbeddedInUntrustedMode()) {
+ if (mIsInputDroppedForAnimation) {
+ // Disable all input during the animation.
+ setDropInputMode(DropInputMode.ALL);
+ } else if (isEmbeddedInUntrustedMode()) {
// Set drop input to OBSCURED when untrusted embedded.
setDropInputMode(DropInputMode.OBSCURED);
} else {
@@ -1591,7 +1605,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@VisibleForTesting
void setDropInputMode(@DropInputMode int mode) {
- if (mLastDropInputMode != mode && getSurfaceControl() != null) {
+ if (mLastDropInputMode != mode) {
mLastDropInputMode = mode;
mWmService.mTransactionFactory.get()
.setDropInputMode(getSurfaceControl(), mode)
@@ -7237,11 +7251,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getDisplayContent().computeImeTargetIfNeeded(this);
- if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + this
- + ": reportedVisible=" + reportedVisible
- + " okToDisplay=" + okToDisplay()
- + " okToAnimate=" + okToAnimate()
- + " startingDisplayed=" + startingDisplayed);
+ ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s"
+ + ": reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
+ this, reportedVisible, okToDisplay(), okToAnimate(), startingDisplayed);
// clean up thumbnail window
if (mThumbnail != null) {
@@ -9301,6 +9313,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// permission to access the device configs.
proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
proto.write(ENABLE_RECENTS_SCREENSHOT, mEnableRecentsScreenshot);
+ proto.write(LAST_DROP_INPUT_MODE, mLastDropInputMode);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e50aff438ecd..7b60ea7b9558 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -135,6 +135,7 @@ import com.android.server.wm.LaunchParamsController.LaunchParams;
import java.io.PrintWriter;
import java.text.DateFormat;
+import java.util.ArrayList;
import java.util.Date;
/**
@@ -2010,6 +2011,27 @@ class ActivityStarter {
return START_PERMISSION_DENIED;
}
+ // Do not start the activity if target display's DWPC does not allow it.
+ // We can't return fatal error code here because it will crash the caller of
+ // startActivity() if they don't catch the exception. We don't expect 3P apps to make
+ // changes.
+ if (mPreferredTaskDisplayArea != null) {
+ final DisplayContent displayContent = mRootWindowContainer.getDisplayContentOrCreate(
+ mPreferredTaskDisplayArea.getDisplayId());
+ if (displayContent != null && displayContent.mDwpcHelper.hasController()) {
+ final ArrayList<ActivityInfo> activities = new ArrayList<>();
+ activities.add(r.info);
+ final int targetWindowingMode = (targetTask != null)
+ ? targetTask.getWindowingMode() : displayContent.getWindowingMode();
+ if (!displayContent.mDwpcHelper
+ .canContainActivities(activities, targetWindowingMode)) {
+ Slog.w(TAG, "Abort to launch " + r.info.getComponentName()
+ + " on display area " + mPreferredTaskDisplayArea);
+ return START_ABORTED;
+ }
+ }
+ }
+
return START_SUCCESS;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f708a7fa5bb9..5c09f09d9313 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -81,11 +81,11 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -130,6 +130,7 @@ import android.view.animation.TranslateAnimation;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.TransitionAnimation;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.DumpUtils.Dump;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -237,7 +238,8 @@ public class AppTransition implements Dump {
mService = service;
mHandler = new Handler(service.mH.getLooper());
mDisplayContent = displayContent;
- mTransitionAnimation = new TransitionAnimation(context, DEBUG_ANIM, TAG);
+ mTransitionAnimation = new TransitionAnimation(
+ context, ProtoLogImpl.isEnabled(WM_DEBUG_ANIM), TAG);
mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 457ea131a51f..a31d8603e2f2 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -98,6 +98,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.LinkedList;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -541,12 +542,13 @@ public class AppTransitionController {
}
/**
- * Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all app windows
- * in the current transition.
- * @return {@code null} if there is no such organizer, or if there are more than one.
+ * Finds the common parent {@link Task} that is parent of all embedded app windows in the
+ * current transition.
+ * @return {@code null} if app windows in the transition are not children of the same Task, or
+ * if none of the app windows is embedded.
*/
@Nullable
- private ITaskFragmentOrganizer findTaskFragmentOrganizerForAllWindows() {
+ private Task findParentTaskForAllEmbeddedWindows() {
mTempTransitionWindows.clear();
mTempTransitionWindows.addAll(mDisplayContent.mClosingApps);
mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps);
@@ -600,13 +602,22 @@ public class AppTransitionController {
leafTask = task;
}
mTempTransitionWindows.clear();
- if (leafTask == null) {
+ return leafTask;
+ }
+
+ /**
+ * Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all embedded
+ * {@link TaskFragment} belong to the given {@link Task}.
+ * @return {@code null} if there is no such organizer, or if there are more than one.
+ */
+ @Nullable
+ private ITaskFragmentOrganizer findTaskFragmentOrganizer(@Nullable Task task) {
+ if (task == null) {
return null;
}
-
// We don't support remote animation for Task with multiple TaskFragmentOrganizers.
final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1];
- final boolean hasMultipleOrganizers = leafTask.forAllLeafTaskFragments(taskFragment -> {
+ final boolean hasMultipleOrganizers = task.forAllLeafTaskFragments(taskFragment -> {
final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer();
if (tfOrganizer == null) {
return false;
@@ -638,7 +649,8 @@ public class AppTransitionController {
return false;
}
- final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizerForAllWindows();
+ final Task task = findParentTaskForAllEmbeddedWindows();
+ final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task);
final RemoteAnimationDefinition definition = organizer != null
? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
.getRemoteAnimationDefinition(organizer)
@@ -653,6 +665,24 @@ public class AppTransitionController {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Override with TaskFragment remote animation for transit=%s",
AppTransition.appTransitionOldToString(transit));
+
+ final boolean hasUntrustedEmbedding = task.forAllLeafTasks(
+ taskFragment -> !taskFragment.isAllowedToBeEmbeddedInTrustedMode());
+ final RemoteAnimationController remoteAnimationController =
+ mDisplayContent.mAppTransition.getRemoteAnimationController();
+ if (hasUntrustedEmbedding && remoteAnimationController != null) {
+ // We are going to use client-driven animation, but the Task is in untrusted embedded
+ // mode. We need to disable all input on activity windows during the animation to
+ // ensure it is safe. This is needed for all activity windows in the animation Task.
+ remoteAnimationController.setOnRemoteAnimationReady(() -> {
+ final Consumer<ActivityRecord> updateActivities =
+ activity -> activity.setDropInputForAnimation(true);
+ task.forAllActivities(updateActivities);
+ });
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment in"
+ + " untrusted mode. Disabled all input during TaskFragment remote animation.",
+ task.mTaskId);
+ }
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 88d7dff4ff1a..4573ede13f7f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -75,6 +75,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
@@ -83,7 +84,6 @@ import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -1366,8 +1366,7 @@ public class DisplayPolicy {
* @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none.
*/
int selectAnimation(WindowState win, int transit) {
- if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
- + ": transit=" + transit);
+ ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
if (win == mStatusBar) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
@@ -1460,7 +1459,7 @@ public class DisplayPolicy {
// with old content because home is easier to have different UI states.
return ANIMATION_NONE;
}
- if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT");
+ ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
return R.anim.app_starting_exit;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 262ddae02765..43ff58083657 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
@@ -32,7 +33,6 @@ import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATI
import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
import static com.android.server.wm.DisplayRotationProto.ROTATION;
import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
@@ -747,10 +747,11 @@ public class DisplayRotation {
final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully()
|| !mService.mPolicy.okToAnimate(false /* ignoreScreenOn */);
final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow();
- if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen="
- + topFullscreen + " rotationAnimation="
- + (topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation)
- + " forceJumpcut=" + forceJumpcut);
+ ProtoLog.i(WM_DEBUG_ANIM, "selectRotationAnimation topFullscreen=%s"
+ + " rotationAnimation=%d forceJumpcut=%b",
+ topFullscreen,
+ topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation,
+ forceJumpcut);
if (forceJumpcut) {
mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index eeac230489f9..4a0a6e3c204b 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -23,6 +23,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
@@ -49,6 +50,7 @@ import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Helper class to run app animations in a remote process.
@@ -72,6 +74,8 @@ class RemoteAnimationController implements DeathRecipient {
private FinishedCallback mFinishedCallback;
private boolean mCanceled;
private boolean mLinkedToDeathOfRunner;
+ @Nullable
+ private Runnable mOnRemoteAnimationReady;
RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
@@ -101,6 +105,11 @@ class RemoteAnimationController implements DeathRecipient {
return adapters;
}
+ /** Sets callback to run before starting remote animation. */
+ void setOnRemoteAnimationReady(@Nullable Runnable onRemoteAnimationReady) {
+ mOnRemoteAnimationReady = onRemoteAnimationReady;
+ }
+
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
@@ -110,7 +119,7 @@ class RemoteAnimationController implements DeathRecipient {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"goodToGo(): Animation canceled already");
onAnimationFinished();
- invokeAnimationCancelled();
+ invokeAnimationCancelled("already_cancelled");
return;
}
@@ -129,10 +138,15 @@ class RemoteAnimationController implements DeathRecipient {
"goodToGo(): No apps to animate, mPendingAnimations=%d",
mPendingAnimations.size());
onAnimationFinished();
- invokeAnimationCancelled();
+ invokeAnimationCancelled("no_app_targets");
return;
}
+ if (mOnRemoteAnimationReady != null) {
+ mOnRemoteAnimationReady.run();
+ mOnRemoteAnimationReady = null;
+ }
+
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
@@ -169,7 +183,7 @@ class RemoteAnimationController implements DeathRecipient {
mCanceled = true;
}
onAnimationFinished();
- invokeAnimationCancelled();
+ invokeAnimationCancelled(reason);
}
private void writeStartDebugStatement() {
@@ -292,16 +306,22 @@ class RemoteAnimationController implements DeathRecipient {
mService.closeSurfaceTransaction("RemoteAnimationController#finished");
}
}
+ // Reset input for all activities when the remote animation is finished.
+ final Consumer<ActivityRecord> updateActivities =
+ activity -> activity.setDropInputForAnimation(false);
+ mDisplayContent.forAllActivities(updateActivities);
setRunningRemoteAnimation(false);
ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
}
- private void invokeAnimationCancelled() {
+ private void invokeAnimationCancelled(String reason) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
try {
mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify cancel", e);
}
+ mOnRemoteAnimationReady = null;
}
private void releaseFinishedCallback() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 9ad25ac8876f..f75a06d99d6c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -116,6 +116,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
private float mLastReportedAnimatorScale;
private String mPackageName;
private String mRelayoutTag;
+ private String mUpdateViewVisibilityTag;
+ private String mUpdateWindowLayoutTag;
private final InsetsVisibilities mDummyRequestedVisibilities = new InsetsVisibilities();
private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0];
final boolean mSetsUnrestrictedKeepClearAreas;
@@ -223,6 +225,27 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
+ public int updateVisibility(IWindow client, WindowManager.LayoutParams attrs,
+ int viewVisibility, MergedConfiguration outMergedConfiguration,
+ SurfaceControl outSurfaceControl, InsetsState outInsetsState,
+ InsetsSourceControl[] outActiveControls) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mUpdateViewVisibilityTag);
+ int res = mService.updateViewVisibility(this, client, attrs, viewVisibility,
+ outMergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ return res;
+ }
+
+ @Override
+ public void updateLayout(IWindow window, WindowManager.LayoutParams attrs, int flags,
+ ClientWindowFrames clientFrames, int requestedWidth, int requestedHeight) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mUpdateWindowLayoutTag);
+ mService.updateWindowLayout(this, window, attrs, flags, clientFrames, requestedWidth,
+ requestedHeight);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ @Override
public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
mService.setWillReplaceWindows(appToken, childrenOnly);
}
@@ -689,6 +712,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (wpc != null) {
mPackageName = wpc.mInfo.packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
+ mUpdateViewVisibilityTag = "updateVisibility: " + mPackageName;
+ mUpdateWindowLayoutTag = "updateLayout: " + mPackageName;
} else {
Slog.e(TAG_WM, "Unknown process pid=" + mPid);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 53e33781bca6..fbf04262cc37 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,8 +32,11 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
@@ -185,10 +188,16 @@ class SurfaceAnimator {
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
- if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
+ ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
return;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mAnimation.dump(pw, "");
+ ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw);
+ }
if (snapshotAnim != null) {
mSnapshot = freezer.takeSnapshotForAnimation();
if (mSnapshot == null) {
@@ -340,7 +349,8 @@ class SurfaceAnimator {
* to another animator.
*/
private void cancelAnimation(Transaction t, boolean restarting, boolean forwardCancel) {
- if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
+ ProtoLog.i(WM_DEBUG_ANIM, "Cancelling animation restarting=%b for %s",
+ restarting, mAnimatable);
final SurfaceControl leash = mLeash;
final AnimationAdapter animation = mAnimation;
final @AnimationType int animationType = mAnimationType;
@@ -419,7 +429,8 @@ class SurfaceAnimator {
final boolean reparent = surface != null && (curAnimationLeash == null
|| curAnimationLeash.equals(leash));
if (reparent) {
- if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
+ ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to original parent: %s for %s",
+ parent, animatable);
// We shouldn't really need these isValid checks but we do
// b/130364451
if (surface.isValid() && parent != null && parent.isValid()) {
@@ -444,7 +455,7 @@ class SurfaceAnimator {
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
boolean hidden, Supplier<Transaction> transactionFactory) {
- if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
+ ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
.setName(surface + " - animation-leash of " + animationTypeToString(type))
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index bdec49e2ec40..0a92ffced48d 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -304,18 +304,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
- if (organizerState == null) {
- return null;
- }
- for (TaskFragment tf : organizerState.mOrganizedTaskFragments) {
- if (!tf.isAllowedToBeEmbeddedInTrustedMode()) {
- // Disable client-driven animations for organizer if at least one of the
- // embedded task fragments is not embedding in trusted mode.
- // TODO(b/197364677): replace with a stub or Shell-driven one instead of skip?
- return null;
- }
- }
- return organizerState.mRemoteAnimationDefinition;
+ return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 7d738241e4a3..b8b151f0e9d4 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -826,7 +826,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
// Avoid WindowState#getRootTask() so we don't attribute system windows to a task.
- final Task task = imeLayeringTarget.getWindow().asTask();
+ final Task task = imeLayeringTarget.getWindow().getTask();
if (task == null) {
return null;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 21ca4bb6038b..a0d68a1adb8a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -520,7 +520,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Legacy dispatch relies on this (for now).
ar.mEnteringAnimation = visibleAtTransitionEnd;
}
- mController.dispatchLegacyAppTransitionFinished(ar);
}
final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
if (wt != null) {
@@ -532,6 +531,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
}
+ // dispatch legacy callback in a different loop. This is because multiple legacy handlers
+ // (fixed-rotation/displaycontent) make global changes, so we want to ensure that we've
+ // processed all the participants first (in particular, we want to trigger pip-enter first)
+ for (int i = 0; i < mParticipants.size(); ++i) {
+ final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
+ if (ar != null) {
+ mController.dispatchLegacyAppTransitionFinished(ar);
+ }
+ }
if (activitiesWentInvisible) {
// Always schedule stop processing when transition finishes because activities don't
// stop while they are in a transition thus their stop could still be pending.
@@ -1127,7 +1135,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
continue;
}
targets.add(wc);
- targets.mValidParticipants.add(wc);
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Initial targets: %s",
targets.mArray);
@@ -1145,15 +1152,17 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private static void populateParentChanges(Targets targets,
ArrayMap<WindowContainer, ChangeInfo> changes) {
final ArrayList<WindowContainer<?>> intermediates = new ArrayList<>();
- for (int i = targets.mValidParticipants.size() - 1; i >= 0; --i) {
- WindowContainer<?> wc = targets.mValidParticipants.get(i);
- // Go up if the participant has been represented by its parent.
- while (targets.mArray.indexOfValue(wc) < 0 && wc.getParent() != null) {
- wc = wc.getParent();
- }
+ // Make a copy to iterate because the original array may be modified.
+ final ArrayList<WindowContainer<?>> targetList = new ArrayList<>(targets.mArray.size());
+ for (int i = targets.mArray.size() - 1; i >= 0; --i) {
+ targetList.add(targets.mArray.valueAt(i));
+ }
+ for (int i = targetList.size() - 1; i >= 0; --i) {
+ final WindowContainer<?> wc = targetList.get(i);
// Wallpaper must belong to the top (regardless of how nested it is in DisplayAreas).
final boolean skipIntermediateReports = isWallpaper(wc);
intermediates.clear();
+ boolean foundParentInTargets = false;
// Collect the intermediate parents between target and top changed parent.
for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
final ChangeInfo parentChange = changes.get(p);
@@ -1167,19 +1176,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// The chain above the parent was processed.
break;
}
- if (targets.mValidParticipants.contains(p)) {
+ if (targetList.contains(p)) {
if (skipIntermediateReports) {
changes.get(wc).mParent = p;
} else {
intermediates.add(p);
}
- // The parent reaches a participant.
+ foundParentInTargets = true;
break;
} else if (reportIfNotTop(p) && !skipIntermediateReports) {
intermediates.add(p);
}
}
- if (intermediates.isEmpty()) continue;
+ if (!foundParentInTargets || intermediates.isEmpty()) continue;
// Add any always-report parents along the way.
changes.get(wc).mParent = intermediates.get(0);
for (int j = 0; j < intermediates.size() - 1; j++) {
@@ -1657,8 +1666,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private static class Targets {
/** All targets. Its keys (depth) are sorted in ascending order naturally. */
final SparseArray<WindowContainer<?>> mArray = new SparseArray<>();
- /** The initial participants which have changes. */
- final ArrayList<WindowContainer<?>> mValidParticipants = new ArrayList<>();
/** The targets which were represented by their parent. */
private ArrayList<WindowContainer<?>> mRemovedTargets;
private int mDepthFactor;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5b39a9980c37..99e39f1969e1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -33,6 +33,7 @@ import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -58,10 +59,8 @@ import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
@@ -106,6 +105,7 @@ import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -2726,9 +2726,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
- if (DEBUG_ANIM) {
- Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
- }
+ ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
+ this, type, anim);
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
@@ -3097,9 +3096,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// ActivityOption#makeCustomAnimation or WindowManager#overridePendingTransition.
a.restrictDuration(MAX_APP_TRANSITION_DURATION);
}
- if (DEBUG_ANIM) {
- logWithStack(TAG, "Loaded animation " + a + " for " + this
- + ", duration: " + ((a != null) ? a.getDuration() : 0));
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ ProtoLog.i(WM_DEBUG_ANIM, "Loaded animation %s for %s, duration: %d, stack=%s",
+ a, this, ((a != null) ? a.getDuration() : 0), Debug.getCallers(20));
}
final int containingWidth = frame.width();
final int containingHeight = frame.height();
@@ -3871,7 +3870,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
try {
overlay.getRemoteInterface().onConfigurationChanged(getConfiguration());
} catch (Exception e) {
- Slog.e(TAG, "Error sending initial configuration change to WindowContainer overlay");
+ ProtoLog.e(WM_DEBUG_ANIM,
+ "Error sending initial configuration change to WindowContainer overlay");
removeTrustedOverlay(overlay);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index c95470071e2d..42b556f77ab6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -34,7 +34,6 @@ public class WindowManagerDebugConfig {
static final String TAG_WM = "WindowManager";
static final boolean DEBUG = false;
- static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 29e292da5697..41215014f859 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -97,6 +97,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -2560,7 +2561,9 @@ public class WindowManagerService extends IWindowManager.Stub
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
+ String reason = null;
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
+ reason = "applyAnimation";
focusMayChange = true;
win.mAnimatingExit = true;
} else if (win.mDisplayContent.okToAnimate() && win.isExitAnimationRunningSelfOrParent()) {
@@ -2570,6 +2573,7 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (win.mDisplayContent.okToAnimate()
&& win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)
&& win.mAttrs.type != TYPE_NOTIFICATION_SHADE) {
+ reason = "isWallpaperTarget";
// If the wallpaper is currently behind this app window, we need to change both of them
// inside of a transaction to avoid artifacts.
// For NotificationShade, sysui is in charge of running window animation and it updates
@@ -2585,6 +2589,10 @@ public class WindowManagerService extends IWindowManager.Stub
win.mDestroying = true;
win.destroySurface(false, stopped);
}
+ if (reason != null) {
+ ProtoLog.d(WM_DEBUG_ANIM, "Set animatingExit: reason=startExitingAnimation/%s win=%s",
+ reason, win);
+ }
if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.onWindowTransition(win, transit);
}
@@ -2619,6 +2627,19 @@ public class WindowManagerService extends IWindowManager.Stub
return result;
}
+ int updateViewVisibility(Session session, IWindow client, LayoutParams attrs,
+ int viewVisibility, MergedConfiguration outMergedConfiguration,
+ SurfaceControl outSurfaceControl, InsetsState outInsetsState,
+ InsetsSourceControl[] outActiveControls) {
+ // TODO(b/161810301): Finish the implementation.
+ return 0;
+ }
+
+ void updateWindowLayout(Session session, IWindow client, LayoutParams attrs, int flags,
+ ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
+ // TODO(b/161810301): Finish the implementation.
+ }
+
public boolean outOfMemoryWindow(Session session, IWindow client) {
final long origId = Binder.clearCallingIdentity();
@@ -3776,7 +3797,8 @@ public class WindowManagerService extends IWindowManager.Stub
final int uid = Binder.getCallingUid();
final boolean hasPermission =
mAtmService.instrumentationSourceHasPermission(pid, MODIFY_TOUCH_MODE_STATE)
- || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()");
+ || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()",
+ /* printlog= */ false);
final long token = Binder.clearCallingIdentity();
try {
if (mInputManager.setInTouchMode(mode, pid, uid, hasPermission)) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 65cbcb123982..0ca1058a80b2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -106,6 +106,7 @@ import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -140,7 +141,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -252,6 +252,7 @@ import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ToBooleanFunction;
@@ -262,6 +263,7 @@ import com.android.server.wm.SurfaceAnimator.AnimationType;
import dalvik.annotation.optimization.NeverCompile;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
@@ -2241,6 +2243,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Mark all relevant flags for that onExitAnimationDone will proceed all the way
// to actually remove it.
if (!visible && isVisibleNow && mActivityRecord.isAnimating(PARENTS | TRANSITION)) {
+ ProtoLog.d(WM_DEBUG_ANIM,
+ "Set animatingExit: reason=onAppVisibilityChanged win=%s", this);
mAnimatingExit = true;
mRemoveOnExit = true;
mWindowRemovalAllowed = true;
@@ -2580,6 +2584,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// been removed. We probably need another flag to indicate that window removal
// should be deffered vs. overloading the flag that says we are playing an exit
// animation.
+ ProtoLog.v(WM_DEBUG_ANIM,
+ "Set animatingExit: reason=remove/replaceWindow win=%s", this);
mAnimatingExit = true;
mReplacingRemoveRequested = true;
return;
@@ -2608,6 +2614,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Try starting an animation.
if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ ProtoLog.v(WM_DEBUG_ANIM,
+ "Set animatingExit: reason=remove/applyAnimation win=%s", this);
mAnimatingExit = true;
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
@@ -2634,6 +2642,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// The exit animation is running or should run... wait for it!
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"Not removing %s due to exit animation", this);
+ ProtoLog.v(WM_DEBUG_ANIM, "Set animatingExit: reason=remove/isAnimating win=%s",
+ this);
setupWindowForRemoveOnExit();
if (mActivityRecord != null) {
mActivityRecord.updateReportedVisibilityLocked();
@@ -3593,6 +3603,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Clear animating flags now, since the surface is now gone. (Note this is true even
// if the surface is saved, to outside world the surface is still NO_SURFACE.)
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);
if (useBLASTSync()) {
immediatelyNotifyBlastSync();
@@ -4663,8 +4674,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
- if (DEBUG_ANIM) Slog.v(TAG,
- "performShowLocked: mDrawState=HAS_DRAWN in " + this);
+ ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
@@ -5047,9 +5057,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void onExitAnimationDone() {
- if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
- + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
- + " selfAnimating=" + isAnimating());
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ final AnimationAdapter animationAdapter = mSurfaceAnimator.getAnimation();
+ StringWriter sw = new StringWriter();
+ if (animationAdapter != null) {
+ PrintWriter pw = new PrintWriter(sw);
+ animationAdapter.dump(pw, "");
+ }
+ ProtoLog.v(WM_DEBUG_ANIM, "onExitAnimationDone in %s"
+ + ": exiting=%b remove=%b selfAnimating=%b anim=%s",
+ this, mAnimatingExit, mRemoveOnExit, isAnimating(), sw);
+ }
if (!mChildren.isEmpty()) {
// Copying to a different list as multiple children can be removed.
@@ -5103,6 +5121,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=exitAnimationDone win=%s", this);
getDisplayContent().mWallpaperController.hideWallpapers(this);
}
@@ -5134,6 +5153,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// show up with wrong position or scale.
if (mAnimatingExit) {
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=clearAnimatingFlags win=%s",
+ this);
didSomething = true;
}
if (mDestroying) {
@@ -5208,6 +5229,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
cancelAnimation();
}
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=relayoutVisibleWindow win=%s",
+ this);
}
if (mDestroying) {
mDestroying = false;
@@ -5292,7 +5315,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
+ ProtoLog.v(WM_DEBUG_ANIM, "Setting move animation on %s", this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
transformFrameToSurfacePosition(mWindowFrames.mLastFrame.left, mWindowFrames.mLastFrame.top,
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 285a6d5bdf5f..5c0557f2d1f4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DRAW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
@@ -34,7 +35,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMAT
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
@@ -60,6 +60,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -178,10 +179,9 @@ class WindowStateAnimator {
void onAnimationFinished() {
// Done animating, clean up.
- if (DEBUG_ANIM) Slog.v(
- TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
- + ", reportedVisible="
- + (mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
+ ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s: exiting=%b, reportedVisible=%b",
+ this, mWin.mAnimatingExit,
+ (mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
@@ -264,9 +264,8 @@ class WindowStateAnimator {
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
- if (DEBUG_ANIM) {
- Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
- }
+ ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
+ mSurfaceController);
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
@@ -298,9 +297,7 @@ class WindowStateAnimator {
w.setHasSurface(false);
- if (DEBUG_ANIM) {
- Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING");
- }
+ ProtoLog.i(WM_DEBUG_ANIM, "createSurface %s: mDrawState=DRAW_PENDING", this);
resetDrawState();
@@ -503,8 +500,8 @@ class WindowStateAnimator {
}
}
} else {
- if (DEBUG_ANIM && mWin.isAnimating(TRANSITION | PARENTS)) {
- Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
+ if (mWin.isAnimating(TRANSITION | PARENTS)) {
+ ProtoLog.v(WM_DEBUG_ANIM, "prepareSurface: No changes in animation for %s", this);
}
}
@@ -646,15 +643,12 @@ class WindowStateAnimator {
mWin.mAttrs, attr, TRANSIT_OLD_NONE);
}
}
- if (DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation: win=" + this
- + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
- + " a=" + a
- + " transit=" + transit
- + " type=" + mAttrType
- + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ ProtoLog.v(WM_DEBUG_ANIM, "applyAnimation: win=%s"
+ + " anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
+ this, anim, attr, a, transit, mAttrType, isEntrance, Debug.getCallers(20));
+ }
if (a != null) {
- if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
mWin.startAnimation(a);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c5bb1bfb4663..55e504a3b611 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1164,7 +1164,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
pushActiveAdminPackagesLocked(userHandle);
- pushMeteredDisabledPackagesLocked(userHandle);
+ pushMeteredDisabledPackages(userHandle);
}
}
} catch (RemoteException re) {
@@ -3571,7 +3571,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
for (int i = users.size() - 1; i >= 0; --i) {
final int userId = users.get(i).id;
mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackagesAsync(
- getMeteredDisabledPackagesLocked(userId), userId);
+ getMeteredDisabledPackages(userId), userId);
}
}
}
@@ -12269,7 +12269,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ " only profile owner or device owner may control the preferential"
+ " network service");
synchronized (getLockObject()) {
- final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(
+ final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
caller.getUserId());
if (!requiredAdmin.mPreferentialNetworkServiceConfigs.equals(
preferentialNetworkServiceConfigs)) {
@@ -12299,7 +12299,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ " only profile owner or device owner may retrieve the preferential"
+ " network service configurations");
synchronized (getLockObject()) {
- final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(caller.getUserId());
+ final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
+ caller.getUserId());
return requiredAdmin.mPreferentialNetworkServiceConfigs;
}
}
@@ -14692,7 +14693,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final List<String> excludedPkgs = removeInvalidPkgsForMeteredDataRestriction(
caller.getUserId(), packageNames);
admin.meteredDisabledPackages = packageNames;
- pushMeteredDisabledPackagesLocked(caller.getUserId());
+ pushMeteredDisabledPackages(caller.getUserId());
saveSettingsLocked(caller.getUserId());
return excludedPkgs;
});
@@ -14844,21 +14845,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
isProfileOwnerOnOrganizationOwnedDevice);
}
- private void pushMeteredDisabledPackagesLocked(int userId) {
+ private void pushMeteredDisabledPackages(int userId) {
+ wtfIfInLock();
mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages(
- getMeteredDisabledPackagesLocked(userId), userId);
+ getMeteredDisabledPackages(userId), userId);
}
- private Set<String> getMeteredDisabledPackagesLocked(int userId) {
- final ComponentName who = getOwnerComponent(userId);
- final Set<String> restrictedPkgs = new ArraySet<>();
- if (who != null) {
- final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
+ private Set<String> getMeteredDisabledPackages(int userId) {
+ synchronized (getLockObject()) {
+ final Set<String> restrictedPkgs = new ArraySet<>();
+ final ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
if (admin != null && admin.meteredDisabledPackages != null) {
restrictedPkgs.addAll(admin.meteredDisabledPackages);
}
+
+ return restrictedPkgs;
}
- return restrictedPkgs;
}
@Override
@@ -15302,13 +15304,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
resetGlobalProxyLocked(policy);
}
pushActiveAdminPackagesLocked(userHandle);
- pushMeteredDisabledPackagesLocked(userHandle);
saveSettingsLocked(userHandle);
updateMaximumTimeToLockLocked(userHandle);
policy.mRemovingAdmins.remove(adminReceiver);
Slogf.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
}
+ pushMeteredDisabledPackages(userHandle);
// The removed admin might have disabled camera, so update user
// restrictions.
pushUserRestrictions(userHandle);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 4620ea68ceda..9a8e421d5d12 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -31,6 +31,7 @@ import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -54,7 +55,9 @@ import com.android.server.utils.Slogf;
import java.io.FileNotFoundException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Class managing bugreport collection upon device owner's request.
@@ -78,6 +81,9 @@ public class RemoteBugreportManager {
private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
+ private final SecureRandom mRng = new SecureRandom();
+
+ private final AtomicLong mRemoteBugreportNonce = new AtomicLong();
private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
private final Context mContext;
@@ -197,8 +203,13 @@ public class RemoteBugreportManager {
final long callingIdentity = mInjector.binderClearCallingIdentity();
try {
- mInjector.getIActivityManager().requestRemoteBugReport();
+ long nonce;
+ do {
+ nonce = mRng.nextLong();
+ } while (nonce == 0);
+ mInjector.getIActivityManager().requestRemoteBugReport(nonce);
+ mRemoteBugreportNonce.set(nonce);
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
registerRemoteBugreportReceivers();
@@ -232,6 +243,11 @@ public class RemoteBugreportManager {
}
private void onBugreportFinished(Intent intent) {
+ long nonce = intent.getLongExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_NONCE, 0);
+ if (nonce == 0 || mRemoteBugreportNonce.get() != nonce) {
+ Slogf.w(LOG_TAG, "Invalid nonce provided, ignoring " + nonce);
+ return;
+ }
mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
mRemoteBugreportServiceIsActive.set(false);
final Uri bugreportUri = intent.getData();
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 f9bdad6c62ba..296e8a3bb563 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -30,7 +30,6 @@ import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -56,11 +55,6 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.platform.test.annotations.LargeTest;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseLongArray;
import com.android.server.AppStateTracker;
import com.android.server.AppStateTrackerImpl;
@@ -82,16 +76,10 @@ import org.mockito.quality.Strictness;
import java.time.Clock;
import java.time.Duration;
import java.time.ZoneOffset;
-import java.util.Random;
public class JobSchedulerServiceTest {
private static final String TAG = JobSchedulerServiceTest.class.getSimpleName();
- private static final int[] sRegJobPriorities = {
- JobInfo.PRIORITY_HIGH, JobInfo.PRIORITY_DEFAULT,
- JobInfo.PRIORITY_LOW, JobInfo.PRIORITY_MIN
- };
-
private JobSchedulerService mService;
private MockitoSession mMockingSession;
@@ -769,7 +757,7 @@ public class JobSchedulerServiceTest {
job.setStandbyBucket(RARE_INDEX);
// Not enough RARE jobs to run.
- mService.mPendingJobs.clear();
+ mService.mPendingJobQueue.clear();
maybeQueueFunctor.reset();
for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) {
maybeQueueFunctor.accept(job);
@@ -778,10 +766,10 @@ public class JobSchedulerServiceTest {
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
}
maybeQueueFunctor.postProcessLocked();
- assertEquals(0, mService.mPendingJobs.size());
+ assertEquals(0, mService.mPendingJobQueue.size());
// Enough RARE jobs to run.
- mService.mPendingJobs.clear();
+ mService.mPendingJobQueue.clear();
maybeQueueFunctor.reset();
for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) {
maybeQueueFunctor.accept(job);
@@ -790,10 +778,10 @@ public class JobSchedulerServiceTest {
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
}
maybeQueueFunctor.postProcessLocked();
- assertEquals(5, mService.mPendingJobs.size());
+ assertEquals(5, mService.mPendingJobQueue.size());
// Not enough RARE jobs to run, but a non-batched job saves the day.
- mService.mPendingJobs.clear();
+ mService.mPendingJobQueue.clear();
maybeQueueFunctor.reset();
JobStatus activeJob = createJobStatus(
"testRareJobBatching",
@@ -807,10 +795,10 @@ public class JobSchedulerServiceTest {
}
maybeQueueFunctor.accept(activeJob);
maybeQueueFunctor.postProcessLocked();
- assertEquals(3, mService.mPendingJobs.size());
+ assertEquals(3, mService.mPendingJobQueue.size());
// Not enough RARE jobs to run, but an old RARE job saves the day.
- mService.mPendingJobs.clear();
+ mService.mPendingJobQueue.clear();
maybeQueueFunctor.reset();
JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo());
oldRareJob.setStandbyBucket(RARE_INDEX);
@@ -826,7 +814,7 @@ public class JobSchedulerServiceTest {
maybeQueueFunctor.accept(oldRareJob);
assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed());
maybeQueueFunctor.postProcessLocked();
- assertEquals(3, mService.mPendingJobs.size());
+ assertEquals(3, mService.mPendingJobQueue.size());
}
/** Tests that jobs scheduled by the app itself are counted towards scheduling limits. */
@@ -914,358 +902,4 @@ public class JobSchedulerServiceTest {
0, ""));
}
}
-
- @Test
- public void testPendingJobSorting() {
- // First letter in job variable name indicate regular (r) or expedited (e).
- // Capital letters in job variable name indicate the app/UID.
- // Numbers in job variable name indicate the enqueue time.
- // Expected sort order:
- // eA7 > rA1 > eB6 > rB2 > eC3 > rD4 > eE5 > eF9 > rF8 > eC11 > rC10 > rG12 > rG13 > eE14
- // Intentions:
- // * A jobs let us test skipping both regular and expedited jobs of other apps
- // * B jobs let us test skipping only regular job of another app without going too far
- // * C jobs test that regular jobs don't skip over other app's jobs and that EJs only
- // skip up to level of the earliest regular job
- // * E jobs test that expedited jobs don't skip the line when the app has no regular jobs
- // * F jobs test correct expedited/regular ordering doesn't push jobs too high in list
- // * G jobs test correct ordering for regular jobs
- // * H job tests correct behavior when enqueue times are the same
- JobStatus rA1 = createJobStatus("testPendingJobSorting", createJobInfo(1), 1);
- JobStatus rB2 = createJobStatus("testPendingJobSorting", createJobInfo(2), 2);
- JobStatus eC3 = createJobStatus("testPendingJobSorting",
- createJobInfo(3).setExpedited(true), 3);
- JobStatus rD4 = createJobStatus("testPendingJobSorting", createJobInfo(4), 4);
- JobStatus eE5 = createJobStatus("testPendingJobSorting",
- createJobInfo(5).setExpedited(true), 5);
- JobStatus eB6 = createJobStatus("testPendingJobSorting",
- createJobInfo(6).setExpedited(true), 2);
- JobStatus eA7 = createJobStatus("testPendingJobSorting",
- createJobInfo(7).setExpedited(true), 1);
- JobStatus rH8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 8);
- JobStatus rF8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 6);
- JobStatus eF9 = createJobStatus("testPendingJobSorting",
- createJobInfo(9).setExpedited(true), 6);
- JobStatus rC10 = createJobStatus("testPendingJobSorting", createJobInfo(10), 3);
- JobStatus eC11 = createJobStatus("testPendingJobSorting",
- createJobInfo(11).setExpedited(true), 3);
- JobStatus rG12 = createJobStatus("testPendingJobSorting", createJobInfo(12), 7);
- JobStatus rG13 = createJobStatus("testPendingJobSorting", createJobInfo(13), 7);
- JobStatus eE14 = createJobStatus("testPendingJobSorting",
- createJobInfo(14).setExpedited(true), 5);
-
- rA1.enqueueTime = 10;
- rB2.enqueueTime = 20;
- eC3.enqueueTime = 30;
- rD4.enqueueTime = 40;
- eE5.enqueueTime = 50;
- eB6.enqueueTime = 60;
- eA7.enqueueTime = 70;
- rF8.enqueueTime = 80;
- rH8.enqueueTime = 80;
- eF9.enqueueTime = 90;
- rC10.enqueueTime = 100;
- eC11.enqueueTime = 110;
- rG12.enqueueTime = 120;
- rG13.enqueueTime = 130;
- eE14.enqueueTime = 140;
-
- mService.mPendingJobs.clear();
- // Add in random order so sorting is apparent.
- mService.mPendingJobs.add(eC3);
- mService.mPendingJobs.add(eE5);
- mService.mPendingJobs.add(rA1);
- mService.mPendingJobs.add(rG13);
- mService.mPendingJobs.add(rD4);
- mService.mPendingJobs.add(eA7);
- mService.mPendingJobs.add(rG12);
- mService.mPendingJobs.add(rH8);
- mService.mPendingJobs.add(rF8);
- mService.mPendingJobs.add(eB6);
- mService.mPendingJobs.add(eE14);
- mService.mPendingJobs.add(eF9);
- mService.mPendingJobs.add(rB2);
- mService.mPendingJobs.add(rC10);
- mService.mPendingJobs.add(eC11);
-
- mService.mPendingJobComparator.refreshLocked();
- mService.mPendingJobs.sort(mService.mPendingJobComparator);
-
- final JobStatus[] expectedOrder = new JobStatus[]{
- eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rH8, rF8, eC11, rC10, rG12, rG13, eE14};
- for (int i = 0; i < expectedOrder.length; ++i) {
- assertEquals("List wasn't correctly sorted @ index " + i,
- expectedOrder[i].getJobId(), mService.mPendingJobs.get(i).getJobId());
- }
- }
-
- private void checkPendingJobInvariants() {
- final SparseBooleanArray regJobSeen = new SparseBooleanArray();
- // Latest priority enqueue times seen for each priority for each app.
- final SparseArray<SparseLongArray> latestPriorityRegEnqueueTimesPerUid =
- new SparseArray<>();
- final SparseArray<SparseLongArray> latestPriorityEjEnqueueTimesPerUid = new SparseArray<>();
- final long noEntry = -1;
-
- for (int i = 0; i < mService.mPendingJobs.size(); ++i) {
- final JobStatus job = mService.mPendingJobs.get(i);
- final int uid = job.getSourceUid();
-
- // Invariant #1: All jobs (for a UID) are sorted by priority order
- // Invariant #2: Jobs (for a UID) with the same priority are sorted by enqueue time.
- // Invariant #3: EJs (for a UID) should be before regular jobs
-
- final int priority = job.getEffectivePriority();
- final SparseArray<SparseLongArray> latestPriorityEnqueueTimesPerUid =
- job.isRequestedExpeditedJob()
- ? latestPriorityEjEnqueueTimesPerUid
- : latestPriorityRegEnqueueTimesPerUid;
- SparseLongArray latestPriorityEnqueueTimes = latestPriorityEnqueueTimesPerUid.get(uid);
- if (latestPriorityEnqueueTimes != null) {
- // Invariant 1
- for (int p = priority - 1; p >= JobInfo.PRIORITY_MIN; --p) {
- // If we haven't seen the priority, there shouldn't be an entry in the array.
- assertEquals("Jobs not properly sorted by priority for uid " + uid,
- noEntry, latestPriorityEnqueueTimes.get(p, noEntry));
- }
-
- // Invariant 2
- final long lastSeenPriorityEnqueueTime =
- latestPriorityEnqueueTimes.get(priority, noEntry);
- if (lastSeenPriorityEnqueueTime != noEntry) {
- assertTrue("Jobs with same priority not sorted by enqueue time: "
- + lastSeenPriorityEnqueueTime + " vs " + job.enqueueTime,
- lastSeenPriorityEnqueueTime <= job.enqueueTime);
- }
- } else {
- latestPriorityEnqueueTimes = new SparseLongArray();
- latestPriorityEnqueueTimesPerUid.put(uid, latestPriorityEnqueueTimes);
- }
- latestPriorityEnqueueTimes.put(priority, job.enqueueTime);
-
- // Invariant 3
- if (!job.isRequestedExpeditedJob()) {
- regJobSeen.put(uid, true);
- } else if (regJobSeen.get(uid)) {
- fail("UID " + uid + " had an EJ ordered after a regular job");
- }
- }
- }
-
- private static String sortedJobToString(JobStatus job) {
- return "testJob " + job.getSourceUid() + "/" + job.getJobId()
- + "/p" + job.getEffectivePriority()
- + "/" + job.isRequestedExpeditedJob() + "@" + job.enqueueTime;
- }
-
- @Test
- public void testPendingJobSorting_Random() {
- Random random = new Random(1); // Always use the same series of pseudo random values.
-
- mService.mPendingJobs.clear();
-
- for (int i = 0; i < 5000; ++i) {
- JobStatus job = createJobStatus("testPendingJobSorting_Random",
- createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(250));
- job.enqueueTime = random.nextInt(1_000_000);
- mService.mPendingJobs.add(job);
- }
-
- mService.mPendingJobComparator.refreshLocked();
- try {
- mService.mPendingJobs.sort(mService.mPendingJobComparator);
- } catch (Exception e) {
- for (JobStatus toDump : mService.mPendingJobs) {
- Log.i(TAG, sortedJobToString(toDump));
- }
- throw e;
- }
- checkPendingJobInvariants();
- }
-
- private int sign(int i) {
- if (i > 0) {
- return 1;
- }
- if (i < 0) {
- return -1;
- }
- return 0;
- }
-
- @Test
- public void testPendingJobSortingTransitivity() {
- // Always use the same series of pseudo random values.
- for (int seed : new int[]{1337, 7357, 606, 6357, 41106010, 3, 2, 1}) {
- Random random = new Random(seed);
-
- mService.mPendingJobs.clear();
-
- for (int i = 0; i < 300; ++i) {
- JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
- createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(50));
- job.enqueueTime = random.nextInt(1_000_000);
- job.overrideState = random.nextInt(4);
- mService.mPendingJobs.add(job);
- }
-
- verifyPendingJobComparatorTransitivity();
- }
- }
-
- @Test
- @LargeTest
- public void testPendingJobSortingTransitivity_Concentrated() {
- // Always use the same series of pseudo random values.
- for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
- Random random = new Random(seed);
-
- mService.mPendingJobs.clear();
-
- for (int i = 0; i < 300; ++i) {
- JobStatus job = createJobStatus("testPendingJobSortingTransitivity_Concentrated",
- createJobInfo(i).setExpedited(random.nextFloat() < .03),
- random.nextInt(20));
- job.enqueueTime = random.nextInt(250);
- job.overrideState = random.nextFloat() < .01
- ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_NONE;
- mService.mPendingJobs.add(job);
- Log.d(TAG, sortedJobToString(job));
- }
-
- verifyPendingJobComparatorTransitivity();
- }
- }
-
- @Test
- public void testPendingJobSorting_Random_WithPriority() {
- Random random = new Random(1); // Always use the same series of pseudo random values.
-
- mService.mPendingJobs.clear();
-
- for (int i = 0; i < 5000; ++i) {
- final boolean isEj = random.nextBoolean();
- final int priority;
- if (isEj) {
- priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
- } else {
- priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
- }
- JobStatus job = createJobStatus("testPendingJobSorting_Random_WithPriority",
- createJobInfo(i).setExpedited(isEj).setPriority(priority),
- random.nextInt(250));
- job.enqueueTime = random.nextInt(1_000_000);
- mService.mPendingJobs.add(job);
- }
-
- mService.mPendingJobComparator.refreshLocked();
- try {
- mService.mPendingJobs.sort(mService.mPendingJobComparator);
- } catch (Exception e) {
- for (JobStatus toDump : mService.mPendingJobs) {
- Log.i(TAG, sortedJobToString(toDump));
- }
- throw e;
- }
- checkPendingJobInvariants();
- }
-
- @Test
- public void testPendingJobSortingTransitivity_WithPriority() {
- // Always use the same series of pseudo random values.
- for (int seed : new int[]{1337, 7357, 606, 6357, 41106010, 3, 2, 1}) {
- Random random = new Random(seed);
-
- mService.mPendingJobs.clear();
-
- for (int i = 0; i < 300; ++i) {
- final boolean isEj = random.nextBoolean();
- final int priority;
- if (isEj) {
- priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
- } else {
- priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
- }
- JobStatus job = createJobStatus("testPendingJobSortingTransitivity_WithPriority",
- createJobInfo(i).setExpedited(isEj).setPriority(priority),
- random.nextInt(50));
- job.enqueueTime = random.nextInt(1_000_000);
- job.overrideState = random.nextInt(4);
- mService.mPendingJobs.add(job);
- }
-
- verifyPendingJobComparatorTransitivity();
- }
- }
-
- @Test
- @LargeTest
- public void testPendingJobSortingTransitivity_Concentrated_WithPriority() {
- // Always use the same series of pseudo random values.
- for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
- Random random = new Random(seed);
-
- mService.mPendingJobs.clear();
-
- for (int i = 0; i < 300; ++i) {
- final boolean isEj = random.nextFloat() < .03;
- final int priority;
- if (isEj) {
- priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
- } else {
- priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
- }
- JobStatus job = createJobStatus(
- "testPendingJobSortingTransitivity_Concentrated_WithPriority",
- createJobInfo(i).setExpedited(isEj).setPriority(priority),
- random.nextInt(20));
- job.enqueueTime = random.nextInt(250);
- job.overrideState = random.nextFloat() < .01
- ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_NONE;
- mService.mPendingJobs.add(job);
- Log.d(TAG, sortedJobToString(job));
- }
-
- verifyPendingJobComparatorTransitivity();
- }
- }
-
- private void verifyPendingJobComparatorTransitivity() {
- mService.mPendingJobComparator.refreshLocked();
-
- for (int i = 0; i < mService.mPendingJobs.size(); ++i) {
- final JobStatus job1 = mService.mPendingJobs.get(i);
-
- for (int j = 0; j < mService.mPendingJobs.size(); ++j) {
- final JobStatus job2 = mService.mPendingJobs.get(j);
- final int sign12 = sign(mService.mPendingJobComparator.compare(job1, job2));
- final int sign21 = sign(mService.mPendingJobComparator.compare(job2, job1));
- if (sign12 != -sign21) {
- final String job1String = sortedJobToString(job1);
- final String job2String = sortedJobToString(job2);
- fail("compare(" + job1String + ", " + job2String + ") != "
- + "-compare(" + job2String + ", " + job1String + ")");
- }
-
- for (int k = 0; k < mService.mPendingJobs.size(); ++k) {
- final JobStatus job3 = mService.mPendingJobs.get(k);
- final int sign23 = sign(mService.mPendingJobComparator.compare(job2, job3));
- final int sign13 = sign(mService.mPendingJobComparator.compare(job1, job3));
-
- // Confirm 1 < 2 < 3 or 1 > 2 > 3 or 1 == 2 == 3
- if ((sign12 == sign23 && sign12 != sign13)
- // Confirm that if 1 == 2, then (1 < 3 AND 2 < 3) OR (1 > 3 && 2 > 3)
- || (sign12 == 0 && sign13 != sign23)) {
- final String job1String = sortedJobToString(job1);
- final String job2String = sortedJobToString(job2);
- final String job3String = sortedJobToString(job3);
- fail("Transitivity fail"
- + ": compare(" + job1String + ", " + job2String + ")=" + sign12
- + ", compare(" + job2String + ", " + job3String + ")=" + sign23
- + ", compare(" + job1String + ", " + job3String + ")=" + sign13);
- }
- }
- }
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index 5b6aebccbc11..91c45b4f8b60 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -62,7 +62,7 @@ public class AudioServiceTest {
mSpySystemServer = spy(new NoOpSystemServerAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
- mSettingsAdapter);
+ mSettingsAdapter, null);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
new file mode 100644
index 000000000000..d89c6d5c3c78
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.server.audio;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for AudioService's tracking and reporting of device volume behaviors.
+ */
+public class DeviceVolumeBehaviorTest {
+ private static final String TAG = "DeviceVolumeBehaviorTest";
+
+ private static final String PACKAGE_NAME = "";
+ private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+
+ private Context mContext;
+ private AudioSystemAdapter mAudioSystem;
+ private SystemServerAdapter mSystemServer;
+ private SettingsAdapter mSettingsAdapter;
+ private TestLooper mTestLooper;
+
+ private AudioService mAudioService;
+
+ /**
+ * Volume behaviors that can be set using AudioService#setDeviceVolumeBehavior
+ */
+ public static final int[] BASIC_VOLUME_BEHAVIORS = {
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mContext = InstrumentationRegistry.getTargetContext();
+ mAudioSystem = new NoOpAudioSystemAdapter();
+ mSystemServer = new NoOpSystemServerAdapter();
+ mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioService = new AudioService(mContext, mAudioSystem, mSystemServer,
+ mSettingsAdapter, mTestLooper.getLooper());
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void setDeviceVolumeBehavior_changesDeviceVolumeBehavior() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ int actualBehavior = mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT);
+
+ assertWithMessage("Expected volume behavior to be " + behavior
+ + " but was instead " + actualBehavior)
+ .that(actualBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToNewBehavior_triggersDeviceVolumeBehaviorDispatcher() {
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ dispatcher.reset();
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ assertThat(dispatcher.mTimesCalled).isEqualTo(1);
+ assertThat(dispatcher.mDevice).isEqualTo(DEVICE_SPEAKER_OUT);
+ assertWithMessage("Expected dispatched volume behavior to be " + behavior
+ + " but was instead " + dispatcher.mVolumeBehavior)
+ .that(dispatcher.mVolumeBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToSameBehavior_doesNotTriggerDeviceVolumeBehaviorDispatcher() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void unregisterDeviceVolumeBehaviorDispatcher_noLongerTriggered() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(false, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL, PACKAGE_NAME);
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ private static class TestDeviceVolumeBehaviorDispatcherStub
+ extends IDeviceVolumeBehaviorDispatcher.Stub {
+
+ private AudioDeviceAttributes mDevice;
+ private int mVolumeBehavior;
+ private int mTimesCalled;
+
+ @Override
+ public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ mDevice = device;
+ mVolumeBehavior = volumeBehavior;
+ mTimesCalled++;
+ }
+
+ public void reset() {
+ mTimesCalled = 0;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 1f355b096335..09e5d4be14a4 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -17,10 +17,12 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
import android.util.Log;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -122,4 +124,11 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
public boolean isStreamActive(int stream, int inPastMs) {
return mIsStreamActive;
}
+
+ @Override
+ @NonNull
+ public ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
+ @NonNull AudioAttributes attributes, boolean forVolume) {
+ return new ArrayList<>();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
index 150e3c60b91e..12fc95832a3e 100644
--- a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
@@ -28,7 +28,6 @@ import android.platform.test.annotations.LargeTest;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import android.util.SparseLongArray;
import com.android.server.job.controllers.JobStatus;
@@ -212,13 +211,26 @@ public class PendingJobQueueTest {
jobQueue.add(eC11);
checkPendingJobInvariants(jobQueue);
- final JobStatus[] expectedOrder = new JobStatus[]{
- eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rH8, rF8, eC11, rC10, rG12, rG13, eE14};
- int idx = 0;
JobStatus job;
+ final JobStatus[] expectedPureOrder = new JobStatus[]{
+ eC3, rD4, eE5, eB6, rB2, eA7, rA1, rH8, eF9, rF8, eC11, rC10, rG12, rG13, eE14};
+ int idx = 0;
+ jobQueue.setOptimizeIteration(false);
+ jobQueue.resetIterator();
while ((job = jobQueue.next()) != null) {
assertEquals("List wasn't correctly sorted @ index " + idx,
- expectedOrder[idx].getJobId(), job.getJobId());
+ expectedPureOrder[idx].getJobId(), job.getJobId());
+ idx++;
+ }
+
+ final JobStatus[] expectedOptimizedOrder = new JobStatus[]{
+ eC3, eC11, rD4, eE5, eE14, eB6, rB2, eA7, rA1, rH8, eF9, rF8, rC10, rG12, rG13};
+ idx = 0;
+ jobQueue.setOptimizeIteration(true);
+ jobQueue.resetIterator();
+ while ((job = jobQueue.next()) != null) {
+ assertEquals("Optimized list wasn't correctly sorted @ index " + idx,
+ expectedOptimizedOrder[idx].getJobId(), job.getJobId());
idx++;
}
}
@@ -371,38 +383,36 @@ public class PendingJobQueueTest {
private void checkPendingJobInvariants(PendingJobQueue jobQueue) {
final SparseBooleanArray regJobSeen = new SparseBooleanArray();
- final SparseIntArray lastOverrideStateSeen = new SparseIntArray();
// Latest priority enqueue times seen for each priority for each app.
final SparseArray<SparseLongArray> latestPriorityRegEnqueueTimesPerUid =
new SparseArray<>();
final SparseArray<SparseLongArray> latestPriorityEjEnqueueTimesPerUid = new SparseArray<>();
final int noEntry = -1;
+ int prevOverrideState = noEntry;
JobStatus job;
jobQueue.resetIterator();
+ int count = 0;
while ((job = jobQueue.next()) != null) {
+ count++;
final int uid = job.getSourceUid();
- // Invariant #1: All jobs (for a UID) are sorted by override state
+ // Invariant #1: All jobs are sorted by override state
// Invariant #2: All jobs (for a UID) are sorted by priority order
// Invariant #3: Jobs (for a UID) with the same priority are sorted by enqueue time.
// Invariant #4: EJs (for a UID) should be before regular jobs
- final int prevOverrideState = lastOverrideStateSeen.get(uid, noEntry);
- lastOverrideStateSeen.put(uid, job.overrideState);
- if (prevOverrideState == noEntry) {
- // First job for UID
- continue;
- }
-
// Invariant 1
if (prevOverrideState != job.overrideState) {
- assertTrue(prevOverrideState > job.overrideState);
- // Override state can make ordering weird. Clear the other cached states for this
- // UID to avoid confusion in the other checks.
- latestPriorityEjEnqueueTimesPerUid.remove(uid);
- latestPriorityRegEnqueueTimesPerUid.remove(uid);
- regJobSeen.delete(uid);
+ if (prevOverrideState != noEntry) {
+ assertTrue(prevOverrideState > job.overrideState);
+ }
+ // Override state can make ordering weird. Clear the other cached states
+ // to avoid confusion in the other checks.
+ latestPriorityEjEnqueueTimesPerUid.clear();
+ latestPriorityRegEnqueueTimesPerUid.clear();
+ regJobSeen.clear();
+ prevOverrideState = job.overrideState;
}
final int priority = job.getEffectivePriority();
@@ -423,8 +433,9 @@ public class PendingJobQueueTest {
final long lastSeenPriorityEnqueueTime =
latestPriorityEnqueueTimes.get(priority, noEntry);
if (lastSeenPriorityEnqueueTime != noEntry) {
- assertTrue("Jobs with same priority not sorted by enqueue time: "
- + lastSeenPriorityEnqueueTime + " vs " + job.enqueueTime,
+ assertTrue("Jobs with same priority for uid " + uid
+ + " not sorted by enqueue time: "
+ + lastSeenPriorityEnqueueTime + " before " + job.enqueueTime,
lastSeenPriorityEnqueueTime <= job.enqueueTime);
}
} else {
@@ -440,6 +451,7 @@ public class PendingJobQueueTest {
fail("UID " + uid + " had an EJ ordered after a regular job");
}
}
+ assertEquals("Iterator didn't go through all jobs", jobQueue.size(), count);
}
private static String testJobToString(JobStatus job) {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 704729ea22c4..9f135918daa2 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -257,13 +257,13 @@ public class VibrationThreadTest {
assertTrue(mThread.isRunningVibrationId(vibrationId));
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
waitForCompletion();
assertFalse(mThread.isRunningVibrationId(vibrationId));
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
@@ -288,10 +288,10 @@ public class VibrationThreadTest {
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(1000)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -310,10 +310,10 @@ public class VibrationThreadTest {
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(5550)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -334,10 +334,10 @@ public class VibrationThreadTest {
assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
1000 + TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(2, fakeVibrator.getEffectSegments(vibrationId).size());
// First time turn vibrator ON for minimum of 1s.
@@ -371,13 +371,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(/* immediate= */ false));
+ new Thread(() -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -397,13 +398,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(/* immediate= */ false));
+ new Thread(() -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -647,7 +649,7 @@ public class VibrationThreadTest {
waitForCompletion();
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
}
@Test
@@ -1043,7 +1045,8 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(cancellingThread).
Thread cancellingThread = new Thread(
- () -> conductor.notifyCancelled(/* immediate= */ false));
+ () -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false));
cancellingThread.start();
// Cancelling the vibration should be fast and return right away, even if the thread is
@@ -1052,7 +1055,7 @@ public class VibrationThreadTest {
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS);
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -1080,13 +1083,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread = new Thread(
- () -> conductor.notifyCancelled(/* immediate= */ false));
+ () -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1113,13 +1117,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(/* immediate= */ false));
+ new Thread(() -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1139,7 +1144,7 @@ public class VibrationThreadTest {
verify(mVibrationToken).linkToDeath(same(conductor), eq(0));
verify(mVibrationToken).unlinkToDeath(same(conductor), eq(0));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -1193,12 +1198,13 @@ public class VibrationThreadTest {
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
// Will stop the ramp down right away.
- conductor.notifyCancelled(/* immediate= */ true);
+ conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE, /* immediate= */ true);
waitForCompletion();
// Does not cancel already finished vibration, but releases vibrator.
verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
- eq(Vibration.Status.CANCELLED));
+ eq(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE));
verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
}
@@ -1214,10 +1220,10 @@ public class VibrationThreadTest {
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
// Duration extended for 10000 + 15.
assertEquals(Arrays.asList(expectedOneShot(10_015)),
@@ -1337,7 +1343,7 @@ public class VibrationThreadTest {
VibrationStepConductor conductor2 = startThreadAndDispatcher(vibrationId2, effect2);
// Effect2 won't complete on its own. Cancel it after a couple of repeats.
Thread.sleep(150); // More than two TICKs.
- conductor2.notifyCancelled(/* immediate= */ false);
+ conductor2.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
startThreadAndDispatcher(vibrationId3, effect3);
@@ -1346,7 +1352,7 @@ public class VibrationThreadTest {
// Effect4 is a long oneshot, but it gets cancelled as fast as possible.
long start4 = System.currentTimeMillis();
VibrationStepConductor conductor4 = startThreadAndDispatcher(vibrationId4, effect4);
- conductor4.notifyCancelled(/* immediate= */ true);
+ conductor4.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ true);
waitForCompletion();
long duration4 = System.currentTimeMillis() - start4;
@@ -1366,7 +1372,7 @@ public class VibrationThreadTest {
// Effect2: repeating, cancelled.
verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2);
- verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED_BY_USER);
// The exact count of segments might vary, so just check that there's more than 2 and
// all elements are the same segment.
@@ -1384,7 +1390,7 @@ public class VibrationThreadTest {
fakeVibrator.getEffectSegments(vibrationId3));
// Effect4: cancelled quickly.
- verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_SUPERSEDED);
assertTrue("Tested duration=" + duration4, duration4 < 2000);
// Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 72521fd06245..94f900efb845 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -33,21 +33,26 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
+import android.gui.DropInputMode;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -739,20 +744,36 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
static class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
+ private IRemoteAnimationFinishedCallback mFinishedCallback;
+
@Override
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ mFinishedCallback = finishedCallback;
}
@Override
public void onAnimationCancelled() throws RemoteException {
+ mFinishedCallback = null;
}
@Override
public IBinder asBinder() {
return new Binder();
}
+
+ boolean isAnimationStarted() {
+ return mFinishedCallback != null;
+ }
+
+ void finishAnimation() {
+ try {
+ mFinishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ fail();
+ }
+ }
}
@Test
@@ -841,144 +862,139 @@ public class AppTransitionControllerTest extends WindowTestsBase {
@Test
public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- new TestRemoteAnimationRunner(), 10, 1);
- setupTaskFragmentRemoteAnimation(organizer, adapter);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
createTask(mDisplayContent), organizer);
final ActivityRecord activity = taskFragment.getTopMostActivity();
- activity.allDrawn = true;
+ prepareActivityForAppTransition(activity);
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition.
+ // Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // Should be overridden.
- verify(mDisplayContent.mAppTransition)
- .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- new TestRemoteAnimationRunner(), 10, 1);
- setupTaskFragmentRemoteAnimation(organizer, adapter);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
final Task task = createTask(mDisplayContent);
// Closing non-embedded activity.
final ActivityRecord closingActivity = createActivityRecord(task);
- closingActivity.allDrawn = true;
+ prepareActivityForAppTransition(closingActivity);
// Opening TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
- openingActivity.allDrawn = true;
+ prepareActivityForAppTransition(openingActivity);
task.effectiveUid = openingActivity.getUid();
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition.
+ // Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // Should be overridden.
- verify(mDisplayContent.mAppTransition)
- .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- new TestRemoteAnimationRunner(), 10, 1);
- setupTaskFragmentRemoteAnimation(organizer, adapter);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
final Task task = createTask(mDisplayContent);
// Closing TaskFragment with embedded activity.
final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord closingActivity = taskFragment1.getTopMostActivity();
- closingActivity.allDrawn = true;
+ prepareActivityForAppTransition(closingActivity);
closingActivity.info.applicationInfo.uid = 12345;
// Opening TaskFragment with embedded activity with different UID.
final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment2.getTopMostActivity();
+ prepareActivityForAppTransition(openingActivity);
openingActivity.info.applicationInfo.uid = 54321;
- openingActivity.allDrawn = true;
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition.
+ // Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // Should be overridden.
- verify(mDisplayContent.mAppTransition)
- .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- new TestRemoteAnimationRunner(), 10, 1);
- setupTaskFragmentRemoteAnimation(organizer, adapter);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Closing activity in Task1.
final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
- closingActivity.allDrawn = true;
+ prepareActivityForAppTransition(closingActivity);
// Opening TaskFragment with embedded activity in Task2.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
createTask(mDisplayContent), organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
- openingActivity.allDrawn = true;
+ prepareActivityForAppTransition(openingActivity);
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition for TaskFragment.
+ // Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // Should not be overridden.
- verify(mDisplayContent.mAppTransition, never())
- .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ // Animation not run by the remote handler.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- new TestRemoteAnimationRunner(), 10, 1);
- setupTaskFragmentRemoteAnimation(organizer, adapter);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
final Task task = createTask(mDisplayContent);
// Closing TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
- closingActivity.allDrawn = true;
+ prepareActivityForAppTransition(closingActivity);
closingActivity.info.applicationInfo.uid = 12345;
task.effectiveUid = closingActivity.getUid();
// Opening non-embedded activity with different UID.
final ActivityRecord openingActivity = createActivityRecord(task);
+ prepareActivityForAppTransition(openingActivity);
openingActivity.info.applicationInfo.uid = 54321;
- openingActivity.allDrawn = true;
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition.
+ // Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // Should not be overridden
- verify(mDisplayContent.mAppTransition, never())
- .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ // Animation should not run by the remote handler when there are non-embedded activities of
+ // different UID.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- new TestRemoteAnimationRunner(), 10, 1);
- setupTaskFragmentRemoteAnimation(organizer, adapter);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
createTask(mDisplayContent), organizer);
final ActivityRecord activity = taskFragment.getTopMostActivity();
- activity.allDrawn = true;
+ prepareActivityForAppTransition(activity);
// Set wallpaper as visible.
final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
@@ -986,12 +1002,66 @@ public class AppTransitionControllerTest extends WindowTestsBase {
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition.
+ // Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // Should not be overridden when there is wallpaper in the transition.
- verify(mDisplayContent.mAppTransition, never())
- .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ // Animation should not run by the remote handler when there is wallpaper in the transition.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_inputProtectedForUntrustedAnimation() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activities, one is trusted embedded, and the other
+ // one is untrusted embedded.
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(2)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord activity0 = taskFragment.getChildAt(0).asActivityRecord();
+ final ActivityRecord activity1 = taskFragment.getChildAt(1).asActivityRecord();
+ // Also create a non-embedded activity in the Task.
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+ task.addChild(activity2, POSITION_BOTTOM);
+ prepareActivityForAppTransition(activity0);
+ prepareActivityForAppTransition(activity1);
+ prepareActivityForAppTransition(activity2);
+ doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0);
+ doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1);
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+
+ // The animation will be animated remotely by client and all activities are input disabled
+ // for untrusted animation.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ verify(activity0).setDropInputForAnimation(true);
+ verify(activity1).setDropInputForAnimation(true);
+ verify(activity2).setDropInputForAnimation(true);
+ verify(activity0).setDropInputMode(DropInputMode.ALL);
+ verify(activity1).setDropInputMode(DropInputMode.ALL);
+ verify(activity2).setDropInputMode(DropInputMode.ALL);
+
+ // Reset input after animation is finished.
+ clearInvocations(activity0);
+ clearInvocations(activity1);
+ clearInvocations(activity2);
+ remoteAnimationRunner.finishAnimation();
+
+ verify(activity0).setDropInputForAnimation(false);
+ verify(activity1).setDropInputForAnimation(false);
+ verify(activity2).setDropInputForAnimation(false);
+ verify(activity0).setDropInputMode(DropInputMode.OBSCURED);
+ verify(activity1).setDropInputMode(DropInputMode.NONE);
+ verify(activity2).setDropInputMode(DropInputMode.NONE);
}
@Test
@@ -1004,7 +1074,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
.setParentTask(task)
.setOrganizer(organizer)
.build();
- changeTaskFragment.getTopMostActivity().allDrawn = true;
+ prepareActivityForAppTransition(changeTaskFragment.getTopMostActivity());
spyOn(mDisplayContent.mAppTransition);
spyOn(emptyTaskFragment);
@@ -1034,7 +1104,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
.setParentTask(task)
.setOrganizer(organizer)
.build();
- changeTaskFragment.getTopMostActivity().allDrawn = true;
+ prepareActivityForAppTransition(changeTaskFragment.getTopMostActivity());
// To make sure that having a detached activity won't cause any issue.
final ActivityRecord detachedActivity = createActivityRecord(task);
detachedActivity.removeImmediately();
@@ -1060,7 +1130,9 @@ public class AppTransitionControllerTest extends WindowTestsBase {
/** Registers remote animation for the organizer. */
private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
- RemoteAnimationAdapter adapter) {
+ TestRemoteAnimationRunner remoteAnimationRunner) {
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ remoteAnimationRunner, 10, 1);
final ITaskFragmentOrganizer iOrganizer =
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -1087,4 +1159,14 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
mDisplayContent.mAppTransitionController.handleAppTransitionReady();
}
+
+ private static void prepareActivityForAppTransition(ActivityRecord activity) {
+ // Transition will wait until all participated activities to be drawn.
+ activity.allDrawn = true;
+ // Skip manipulate the SurfaceControl.
+ doNothing().when(activity).setDropInputMode(anyInt());
+ // Make sure activity can create remote animation target.
+ doReturn(mock(RemoteAnimationTarget.class)).when(activity).createRemoteAnimationTarget(
+ any());
+ }
} \ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 9d4fe2783992..a0c20c2c6b68 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -292,7 +292,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
boolean currentTouchMode = mWm.getInTouchMode();
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
- doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString());
+ doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
@@ -307,7 +307,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
boolean currentTouchMode = mWm.getInTouchMode();
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
- doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString());
+ doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(false);
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 1db018edf776..8c9e80f7e04d 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
@@ -296,8 +296,8 @@ public final class UsbPortAidl implements UsbPortHal {
+ operationID + " key:" + key);
}
try {
- sCallbacks.put(operationID, callback);
- mProxy.resetUsbPort(portName, operationID);
+ sCallbacks.put(key, callback);
+ mProxy.resetUsbPort(portName, key);
} catch (RemoteException e) {
logAndPrintException(mPw,
"resetUsbPort: Failed to resetUsbPort: portID="
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index c8bcb83cae74..ee2d23558010 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -923,7 +923,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (modelData != null && modelData.isModelStarted()) {
modelData.setStoppedLocked();
try {
- modelData.getCallback().onRecognitionPaused();
+ IRecognitionStatusCallback callback = modelData.getCallback();
+ if (callback != null) {
+ callback.onRecognitionPaused();
+ }
} catch (DeadObjectException e) {
forceStopAndUnloadModelLocked(modelData, e);
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index dda5ea7aab53..3691fb06abdb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9499,42 +9499,26 @@ public class CarrierConfigManager {
private void addConfig(String key, Object value, PersistableBundle configs) {
if (value instanceof String) {
configs.putString(key, (String) value);
- }
-
- if (value instanceof String[]) {
+ } else if (value instanceof String[]) {
configs.putStringArray(key, (String[]) value);
- }
-
- if (value instanceof Integer) {
+ } else if (value instanceof Integer) {
configs.putInt(key, (Integer) value);
- }
-
- if (value instanceof Long) {
+ } else if (value instanceof Long) {
configs.putLong(key, (Long) value);
- }
-
- if (value instanceof Double) {
+ } else if (value instanceof Double) {
configs.putDouble(key, (Double) value);
- }
-
- if (value instanceof Boolean) {
+ } else if (value instanceof Boolean) {
configs.putBoolean(key, (Boolean) value);
- }
-
- if (value instanceof int[]) {
+ } else if (value instanceof int[]) {
configs.putIntArray(key, (int[]) value);
- }
-
- if (value instanceof double[]) {
+ } else if (value instanceof double[]) {
configs.putDoubleArray(key, (double[]) value);
- }
-
- if (value instanceof boolean[]) {
+ } else if (value instanceof boolean[]) {
configs.putBooleanArray(key, (boolean[]) value);
- }
-
- if (value instanceof long[]) {
+ } else if (value instanceof long[]) {
configs.putLongArray(key, (long[]) value);
+ } else if (value instanceof PersistableBundle) {
+ configs.putPersistableBundle(key, (PersistableBundle) value);
}
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1b3a29d80bef..725518d0626b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16870,7 +16870,7 @@ public class TelephonyManager {
* @param executor The executor where {@code listener} will be invoked
* @param listener The callback to register
* @hide
- * @deprecated Use {@link #unregisterCarrierPrivilegesCallback} instead. This API will be
+ * @deprecated Use {@link #registerCarrierPrivilegesCallback} instead. This API will be
* removed prior to API finalization.
*/
@Deprecated
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 77d4837ccfb6..8287a038c841 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -50,8 +50,7 @@ public class DataServiceCallback {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
- RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_TEMPORARILY_UNAVAILABLE,
- RESULT_ERROR_INVALID_RESPONSE})
+ RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_TEMPORARILY_UNAVAILABLE})
public @interface ResultCode {}
/** Request is completed successfully */
@@ -69,11 +68,6 @@ public class DataServiceCallback {
* @hide
*/
public static final int RESULT_ERROR_TEMPORARILY_UNAVAILABLE = 5;
- /**
- * Request failed to complete due to an invalid response.
- * @hide
- */
- public static final int RESULT_ERROR_INVALID_RESPONSE = 6;
private final IDataServiceCallback mCallback;
@@ -261,8 +255,6 @@ public class DataServiceCallback {
return "RESULT_ERROR_ILLEGAL_STATE";
case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
- case RESULT_ERROR_INVALID_RESPONSE:
- return "RESULT_ERROR_INVALID_RESPONSE";
default:
return "Unknown(" + resultCode + ")";
}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b56aa9687aee..f5b158fedd37 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -375,12 +375,16 @@ public abstract class ImsFeature {
*/
@SystemApi
public final void setFeatureState(@ImsState int state) {
+ boolean isNotify = false;
synchronized (mLock) {
if (mState != state) {
mState = state;
- notifyFeatureState(state);
+ isNotify = true;
}
}
+ if (isNotify) {
+ notifyFeatureState(state);
+ }
}
/**
@@ -412,14 +416,16 @@ public abstract class ImsFeature {
* Internal method called by ImsFeature when setFeatureState has changed.
*/
private void notifyFeatureState(@ImsState int state) {
- mStatusCallbacks.broadcastAction((c) -> {
- try {
- c.notifyImsFeatureStatus(state);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
- + "callback.");
- }
- });
+ synchronized (mStatusCallbacks) {
+ mStatusCallbacks.broadcastAction((c) -> {
+ try {
+ c.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
+ + "callback.");
+ }
+ });
+ }
}
/**
@@ -491,14 +497,19 @@ public abstract class ImsFeature {
synchronized (mLock) {
mCapabilityStatus = caps.copy();
}
- mCapabilityCallbacks.broadcastAction((callback) -> {
- try {
- callback.onCapabilitiesStatusChanged(caps.mCapabilities);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
- + "callback.");
- }
- });
+
+ synchronized (mCapabilityCallbacks) {
+ mCapabilityCallbacks.broadcastAction((callback) -> {
+ try {
+ Log.d(LOG_TAG, "ImsFeature notifyCapabilitiesStatusChanged Capabilities = "
+ + caps.mCapabilities);
+ callback.onCapabilitiesStatusChanged(caps.mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
+ + "callback.");
+ }
+ });
+ }
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 11fc328a42c7..f371ec3a28a7 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -587,13 +587,15 @@ public class ImsConfigImplBase {
if (mCallbacks == null) {
return;
}
- mCallbacks.broadcastAction(c -> {
- try {
- c.onIntConfigChanged(item, value);
- } catch (RemoteException e) {
- Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
- }
- });
+ synchronized (mCallbacks) {
+ mCallbacks.broadcastAction(c -> {
+ try {
+ c.onIntConfigChanged(item, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
+ }
+ });
+ }
}
private void notifyConfigChanged(int item, String value) {
@@ -601,13 +603,15 @@ public class ImsConfigImplBase {
if (mCallbacks == null) {
return;
}
- mCallbacks.broadcastAction(c -> {
- try {
- c.onStringConfigChanged(item, value);
- } catch (RemoteException e) {
- Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
- }
- });
+ synchronized (mCallbacks) {
+ mCallbacks.broadcastAction(c -> {
+ try {
+ c.onStringConfigChanged(item, value);
+ } catch (RemoteException e) {
+ Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+ }
+ });
+ }
}
private void addRcsConfigCallback(IRcsConfigCallback c) {
@@ -635,13 +639,15 @@ public class ImsConfigImplBase {
// can be null in testing
if (mRcsCallbacks != null) {
- mRcsCallbacks.broadcastAction(c -> {
- try {
- c.onConfigurationChanged(mRcsConfigData);
- } catch (RemoteException e) {
- Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping.");
- }
- });
+ synchronized (mRcsCallbacks) {
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onConfigurationChanged(mRcsConfigData);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping.");
+ }
+ });
+ }
}
notifyRcsAutoConfigurationReceived(config, isCompressed);
}
@@ -649,13 +655,15 @@ public class ImsConfigImplBase {
private void onNotifyRcsAutoConfigurationRemoved() {
mRcsConfigData = null;
if (mRcsCallbacks != null) {
- mRcsCallbacks.broadcastAction(c -> {
- try {
- c.onConfigurationReset();
- } catch (RemoteException e) {
- Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping.");
- }
- });
+ synchronized (mRcsCallbacks) {
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onConfigurationReset();
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping.");
+ }
+ });
+ }
}
notifyRcsAutoConfigurationRemoved();
}
@@ -801,13 +809,15 @@ public class ImsConfigImplBase {
if (mRcsCallbacks == null) {
return;
}
- mRcsCallbacks.broadcastAction(c -> {
- try {
- c.onAutoConfigurationErrorReceived(errorCode, errorString);
- } catch (RemoteException e) {
- Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
- }
- });
+ synchronized (mRcsCallbacks) {
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onAutoConfigurationErrorReceived(errorCode, errorString);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
+ }
+ });
+ }
}
/**
@@ -825,13 +835,15 @@ public class ImsConfigImplBase {
if (mRcsCallbacks == null) {
return;
}
- mRcsCallbacks.broadcastAction(c -> {
- try {
- c.onPreProvisioningReceived(configXml);
- } catch (RemoteException e) {
- Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
- }
- });
+ synchronized (mRcsCallbacks) {
+ mRcsCallbacks.broadcastAction(c -> {
+ try {
+ c.onPreProvisioningReceived(configXml);
+ } catch (RemoteException e) {
+ Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+ }
+ });
+ }
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 2783e299236b..fb997d118419 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -219,22 +219,25 @@ public class ImsSmsImplBase {
*/
public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
throws RuntimeException {
+ IImsSmsListener listener = null;
synchronized (mLock) {
- if (mListener == null) {
- throw new RuntimeException("Feature not ready.");
- }
- try {
- mListener.onSmsReceived(token, format, pdu);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
- SmsMessage message = SmsMessage.createFromPdu(pdu, format);
- if (message != null && message.mWrappedSmsMessage != null) {
- acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
- DELIVER_STATUS_ERROR_GENERIC);
- } else {
- Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
- acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
- }
+ listener = mListener;
+ }
+
+ if (listener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ listener.onSmsReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
+ SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
+ DELIVER_STATUS_ERROR_GENERIC);
+ } else {
+ Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
+ acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
}
}
}
@@ -254,16 +257,19 @@ public class ImsSmsImplBase {
*/
public final void onSendSmsResultSuccess(int token,
@IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
+ IImsSmsListener listener = null;
synchronized (mLock) {
- if (mListener == null) {
- throw new RuntimeException("Feature not ready.");
- }
- try {
- mListener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
- SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
+ listener = mListener;
+ }
+
+ if (listener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ listener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
+ SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
@@ -288,16 +294,19 @@ public class ImsSmsImplBase {
@Deprecated
public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
@SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
+ IImsSmsListener listener = null;
synchronized (mLock) {
- if (mListener == null) {
- throw new RuntimeException("Feature not ready.");
- }
- try {
- mListener.onSendSmsResult(token, messageRef, status, reason,
- RESULT_NO_NETWORK_ERROR);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
+ listener = mListener;
+ }
+
+ if (listener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ listener.onSendSmsResult(token, messageRef, status, reason,
+ RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
@@ -322,15 +331,18 @@ public class ImsSmsImplBase {
public final void onSendSmsResultError(int token,
@IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
@SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
+ IImsSmsListener listener = null;
synchronized (mLock) {
- if (mListener == null) {
- throw new RuntimeException("Feature not ready.");
- }
- try {
- mListener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
+ listener = mListener;
+ }
+
+ if (listener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ listener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
@@ -357,16 +369,19 @@ public class ImsSmsImplBase {
public final void onSmsStatusReportReceived(int token,
@IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
byte[] pdu) throws RuntimeException {
+ IImsSmsListener listener = null;
synchronized (mLock) {
- if (mListener == null) {
- throw new RuntimeException("Feature not ready.");
- }
- try {
- mListener.onSmsStatusReportReceived(token, format, pdu);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
- acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
- }
+ listener = mListener;
+ }
+
+ if (listener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ listener.onSmsStatusReportReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
}
}
@@ -386,24 +401,27 @@ public class ImsSmsImplBase {
*/
public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
byte[] pdu) throws RuntimeException {
+ IImsSmsListener listener = null;
synchronized (mLock) {
- if (mListener == null) {
- throw new RuntimeException("Feature not ready.");
- }
- try {
- mListener.onSmsStatusReportReceived(token, format, pdu);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
- SmsMessage message = SmsMessage.createFromPdu(pdu, format);
- if (message != null && message.mWrappedSmsMessage != null) {
- acknowledgeSmsReport(
- token,
- message.mWrappedSmsMessage.mMessageRef,
- STATUS_REPORT_STATUS_ERROR);
- } else {
- Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered.");
- acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
- }
+ listener = mListener;
+ }
+
+ if (listener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ listener.onSmsStatusReportReceived(token, format, pdu);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+ SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ acknowledgeSmsReport(
+ token,
+ message.mWrappedSmsMessage.mMessageRef,
+ STATUS_REPORT_STATUS_ERROR);
+ } else {
+ Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered.");
+ acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
}
}
}
diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml
index fc54ca645a2e..5fb260f174e6 100644
--- a/tests/InputMethodStressTest/AndroidTest.xml
+++ b/tests/InputMethodStressTest/AndroidTest.xml
@@ -19,8 +19,8 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="adb shell settings put secure show_ime_with_hard_keyboard 1" />
- <option name="teardown-command" value="adb shell settings delete secure show_ime_with_hard_keyboard" />
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+ <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 0e86bc8de152..b3c63c870421 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -18,16 +18,19 @@ package com.android.inputmethod.stresstest;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.isImeShown;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
-import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
-import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Bundle;
+import android.os.SystemClock;
import android.platform.test.annotations.RootPermissionTest;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -39,11 +42,13 @@ import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
@RootPermissionTest
@RunWith(AndroidJUnit4.class)
public final class ImeOpenCloseStressTest {
- private static final int NUM_TEST_ITERATIONS = 100;
+ private static final int NUM_TEST_ITERATIONS = 10;
@Test
public void test() {
@@ -57,16 +62,46 @@ public final class ImeOpenCloseStressTest {
EditText editText = activity.getEditText();
waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ String msgPrefix = "Iteration #" + i + " ";
instrumentation.runOnMainSync(activity::showIme);
- waitOnMainUntilImeIsShown(editText);
+ waitOnMainUntil(msgPrefix + "IME should be visible",
+ () -> !activity.isAnimating() && isImeShown(editText));
instrumentation.runOnMainSync(activity::hideIme);
- waitOnMainUntilImeIsHidden(editText);
+ waitOnMainUntil(msgPrefix + "IME should be hidden",
+ () -> !activity.isAnimating() && !isImeShown(editText));
+ // b/b/221483132, wait until IMS and IMMS handles IMM#notifyImeHidden.
+ // There is no good signal, so we just wait a second.
+ SystemClock.sleep(1000);
}
}
public static class TestActivity extends Activity {
private EditText mEditText;
+ private boolean mIsAnimating;
+
+ private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
+ WindowInsetsAnimation.Bounds bounds) {
+ mIsAnimating = true;
+ return super.onStart(animation, bounds);
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ super.onEnd(animation);
+ mIsAnimating = false;
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets insets,
+ List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+ };
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -76,6 +111,9 @@ public final class ImeOpenCloseStressTest {
mEditText = new EditText(this);
rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(rootView);
+ // Enable WindowInsetsAnimation.
+ getWindow().setDecorFitsSystemWindows(false);
+ mEditText.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
}
public EditText getEditText() {
@@ -92,5 +130,9 @@ public final class ImeOpenCloseStressTest {
InputMethodManager imm = getSystemService(InputMethodManager.class);
imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
}
+
+ public boolean isAnimating() {
+ return mIsAnimating;
+ }
}
}
diff --git a/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
index 493f3bd22d2b..cf4965f1655d 100644
--- a/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
+++ b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
@@ -41,7 +41,7 @@ abstract class BaseTrustAgentService : TrustAgentService() {
private const val TAG = "BaseTrustAgentService"
fun instance(serviceClass: KClass<out BaseTrustAgentService>): BaseTrustAgentService? {
- return instances[serviceClass]!!
+ return instances[serviceClass]
}
}
}
diff --git a/tests/TrustTests/src/android/trust/test/LockUserTest.kt b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
index a7dd41ad2e98..1194afa0123f 100644
--- a/tests/TrustTests/src/android/trust/test/LockUserTest.kt
+++ b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
@@ -57,7 +57,6 @@ class LockUserTest {
companion object {
private const val TAG = "LockUserTest"
- private fun await() = Thread.sleep(250)
}
}
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 00f457b5328a..2031af2cf0c9 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -22,7 +22,6 @@ import android.content.Context
import android.util.Log
import android.view.WindowManagerGlobal
import androidx.test.core.app.ApplicationProvider.getApplicationContext
-import com.google.common.truth.Truth.assertThat
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -53,37 +52,12 @@ class LockStateTrackingRule : TestRule {
}
fun assertLocked() {
- val maxWaits = 50
- var waitCount = 0
-
- // First verify we get the call in LockState via TrustListener
- while ((lockState.locked == false) && waitCount < maxWaits) {
- Log.i(TAG, "phone still unlocked (TrustListener), wait 50ms more ($waitCount)")
- Thread.sleep(50)
- waitCount++
- }
- assertThat(lockState.locked).isTrue()
-
- // TODO(b/225231929): refactor checks into one loop and re-use for assertUnlocked
- // Then verify we get the window manager locked
- while (!windowManager.isKeyguardLocked && waitCount < maxWaits) {
- Log.i(TAG, "phone still unlocked (WindowManager), wait 50ms more ($waitCount)")
- Thread.sleep(50)
- waitCount++
- }
- assertThat(windowManager.isKeyguardLocked).isTrue()
+ wait("un-locked per TrustListener") { lockState.locked == true }
+ wait("keyguard lock") { windowManager.isKeyguardLocked }
}
fun assertUnlocked() {
- val maxWaits = 50
- var waitCount = 0
-
- while ((lockState.locked == true) && waitCount < maxWaits) {
- Log.i(TAG, "phone still unlocked, wait 50ms more ($waitCount)")
- Thread.sleep(50)
- waitCount++
- }
- assertThat(lockState.locked).isFalse()
+ wait("locked per TrustListener") { lockState.locked == false }
}
inner class Listener : TrustListener {
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
index 127653d13b10..7eb8157d4b24 100644
--- a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -42,7 +42,7 @@ class ScreenLockRule : TestRule {
override fun apply(base: Statement, description: Description) = object : Statement() {
override fun evaluate() {
verifyNoScreenLockAlreadySet()
- verifyKeyguardDismissed()
+ dismissKeyguard()
setScreenLock()
setLockOnPowerButton()
@@ -51,7 +51,7 @@ class ScreenLockRule : TestRule {
} finally {
removeScreenLock()
revertLockOnPowerButton()
- verifyKeyguardDismissed()
+ dismissKeyguard()
}
}
}
@@ -62,30 +62,21 @@ class ScreenLockRule : TestRule {
.isFalse()
}
- private fun verifyKeyguardDismissed() {
- val maxWaits = 30
- var waitCount = 0
-
- while (windowManager.isKeyguardLocked && waitCount < maxWaits) {
- Log.i(TAG, "Keyguard still showing; attempting to dismiss and wait 50ms ($waitCount)")
+ fun dismissKeyguard() {
+ wait("keyguard dismissed") { count ->
windowManager.dismissKeyguard(null, null)
// Sometimes, bouncer gets shown due to a race, so we have to put display to sleep
// and wake it back up to get it to go away
- if (waitCount >= 10 && waitCount % 5 == 0) {
- Log.i(TAG, "Escalation: attempting screen off/on to get rid of bouncer (+500ms)")
+ if (count >= 10 && count % 5 == 0) {
+ Log.i(TAG, "Escalation: attempting screen off/on to get rid of bouncer")
uiDevice.sleep()
Thread.sleep(250)
uiDevice.wakeUp()
- Thread.sleep(250)
}
- Thread.sleep(50)
- waitCount++
+ !windowManager.isKeyguardLocked
}
- assertWithMessage("Keyguard should be unlocked")
- .that(windowManager.isKeyguardLocked)
- .isFalse()
}
private fun setScreenLock() {
@@ -94,9 +85,7 @@ class ScreenLockRule : TestRule {
LockscreenCredential.createNone(),
context.userId
)
- assertWithMessage("Screen Lock should now be set")
- .that(lockPatternUtils.isSecure(context.userId))
- .isTrue()
+ wait("screen lock set") { lockPatternUtils.isSecure(context.userId) }
Log.i(TAG, "Device PIN set to $PIN")
}
@@ -110,21 +99,16 @@ class ScreenLockRule : TestRule {
LockscreenCredential.createNone(),
LockscreenCredential.createPin(PIN),
context.userId)
- Thread.sleep(100)
+ Log.i(TAG, "Removing screen lock")
assertWithMessage("Lock screen credential should be unset")
.that(lockCredentialUnset)
.isTrue()
lockPatternUtils.setLockScreenDisabled(true, context.userId)
- Thread.sleep(100)
- assertWithMessage("Lockscreen needs to be disabled")
- .that(lockPatternUtils.isLockScreenDisabled(context.userId))
- .isTrue()
-
- // this is here because somehow it helps the keyguard not get stuck
- uiDevice.sleep()
- Thread.sleep(500) // delay added to avoid initiating camera by double clicking power
- uiDevice.wakeUp()
+ wait("screen lock un-set") {
+ lockPatternUtils.isLockScreenDisabled(context.userId)
+ }
+ wait("screen lock insecure") { !lockPatternUtils.isSecure(context.userId) }
}
private fun revertLockOnPowerButton() {
diff --git a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
index 2a9e00276475..18bc029b6845 100644
--- a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
@@ -50,7 +50,6 @@ class TrustAgentRule<T : BaseTrustAgentService>(
verifyTrustServiceRunning()
unlockDeviceWithCredential()
enableTrustAgent()
- waitForEnablement()
try {
verifyAgentIsRunning()
@@ -80,15 +79,10 @@ class TrustAgentRule<T : BaseTrustAgentService>(
lockPatternUtils.setEnabledTrustAgents(agents, userId)
}
- private fun waitForEnablement() {
- Log.d(TAG, "Waiting for $WAIT_TIME ms")
- Thread.sleep(WAIT_TIME)
- Log.d(TAG, "Done waiting")
- }
-
private fun verifyAgentIsRunning() {
- assertWithMessage("${serviceClass.simpleName} should be running")
- .that(BaseTrustAgentService.instance(serviceClass)).isNotNull()
+ wait("${serviceClass.simpleName} to be running") {
+ BaseTrustAgentService.instance(serviceClass) != null
+ }
}
private fun disableTrustAgent() {
@@ -112,6 +106,5 @@ class TrustAgentRule<T : BaseTrustAgentService>(
TrustAgentRule(T::class)
private const val TAG = "TrustAgentRule"
- private val WAIT_TIME = 1000L
}
}
diff --git a/tests/TrustTests/src/android/trust/test/lib/utils.kt b/tests/TrustTests/src/android/trust/test/lib/utils.kt
new file mode 100644
index 000000000000..78140abec210
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/utils.kt
@@ -0,0 +1,51 @@
+/*
+ * 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 android.trust.test.lib
+
+import android.util.Log
+import com.google.common.truth.Truth.assertWithMessage
+
+private const val TAG = "TrustTestUtils"
+
+/**
+ * Waits for [conditionFunction] to be true with a failed assertion if it is not after [maxWait]
+ * ms.
+ *
+ * The condition function can perform additional logic (for example, logging or attempting to make
+ * the condition become true).
+ *
+ * @param conditionFunction function which takes the attempt count & returns whether the condition
+ * is met
+ */
+internal fun wait(
+ description: String? = null,
+ maxWait: Long = 1500L,
+ rate: Long = 50L,
+ conditionFunction: (count: Int) -> Boolean
+) {
+ var waited = 0L
+ var count = 0
+ while (!conditionFunction.invoke(count)) {
+ assertWithMessage("Condition exceeded maximum wait time of $maxWait ms: $description")
+ .that(waited <= maxWait)
+ .isTrue()
+ waited += rate
+ count++
+ Log.i(TAG, "Waiting for $description ($waited/$maxWait) #$count")
+ Thread.sleep(rate)
+ }
+}
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 3e1297190622..6efd1f64d8fe 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -12,10 +12,10 @@ java_binary_host {
manifest: "manifest.txt",
srcs: ["src/**/*.java"],
static_libs: [
- "asm-7.0",
- "asm-commons-7.0",
- "asm-tree-7.0",
- "asm-analysis-7.0",
+ "asm-9.2",
+ "asm-commons-9.2",
+ "asm-tree-9.2",
+ "asm-analysis-9.2",
"guava-21.0",
],
}
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index ec2bffdfaf57..6ebacd8a0b14 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -14,7 +14,7 @@ java_binary_host {
"src/**/*.kt",
],
static_libs: [
- "asm-7.0",
+ "asm-9.2",
],
}
diff --git a/tools/traceinjection/Android.bp b/tools/traceinjection/Android.bp
index 1395c5f2e635..39d1b1c2defd 100644
--- a/tools/traceinjection/Android.bp
+++ b/tools/traceinjection/Android.bp
@@ -12,10 +12,10 @@ java_binary_host {
manifest: "manifest.txt",
srcs: ["src/**/*.java"],
static_libs: [
- "asm-7.0",
- "asm-commons-7.0",
- "asm-tree-7.0",
- "asm-analysis-7.0",
+ "asm-9.2",
+ "asm-commons-9.2",
+ "asm-tree-9.2",
+ "asm-analysis-9.2",
"guava-21.0",
],
}