summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--Android.mk20
-rw-r--r--apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java2
-rw-r--r--api/system-current.txt7
-rw-r--r--api/test-current.txt1
-rw-r--r--config/hiddenapi-force-blacklist.txt62
-rw-r--r--config/hiddenapi-light-greylist.txt635
-rw-r--r--config/hiddenapi-vendor-list.txt59
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java3
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java2
-rw-r--r--core/java/android/animation/Animator.java2
-rw-r--r--core/java/android/animation/ArgbEvaluator.java3
-rw-r--r--core/java/android/animation/LayoutTransition.java3
-rw-r--r--core/java/android/animation/ValueAnimator.java4
-rw-r--r--core/java/android/app/IBackupAgent.aidl3
-rw-r--r--core/java/android/app/backup/BackupAgent.java10
-rw-r--r--core/java/android/app/backup/IBackupCallback.aidl29
-rw-r--r--core/java/android/ddm/DdmHandleAppName.java3
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java11
-rw-r--r--core/java/android/inputmethodservice/Keyboard.java5
-rw-r--r--core/java/android/inputmethodservice/KeyboardView.java7
-rw-r--r--core/java/android/nfc/ErrorCodes.java3
-rw-r--r--core/java/android/nfc/NdefRecord.java2
-rw-r--r--core/java/android/nfc/NfcActivityManager.java2
-rw-r--r--core/java/android/nfc/NfcAdapter.java10
-rw-r--r--core/java/android/nfc/NfcManager.java2
-rw-r--r--core/java/android/nfc/Tag.java4
-rw-r--r--core/java/android/nfc/cardemulation/AidGroup.java10
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java13
-rw-r--r--core/java/android/provider/Settings.java26
-rw-r--r--core/java/android/service/autofill/AutofillFieldClassificationService.java20
-rw-r--r--core/java/android/service/notification/Adjustment.java6
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java34
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java11
-rw-r--r--core/java/android/util/Log.java4
-rw-r--r--core/java/android/util/SparseArray.java36
-rw-r--r--core/java/android/view/RecordingCanvas.java3
-rw-r--r--core/java/android/view/animation/Animation.java9
-rw-r--r--core/java/android/view/animation/AnimationUtils.java2
-rw-r--r--core/java/android/view/animation/Transformation.java2
-rw-r--r--core/java/android/view/animation/TranslateAnimation.java5
-rw-r--r--core/java/android/view/animation/TranslateYAnimation.java2
-rw-r--r--core/java/android/view/autofill/AutofillManager.java5
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java272
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java8
-rw-r--r--core/java/com/android/internal/app/procstats/PssTable.java145
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java137
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl16
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl16
-rw-r--r--core/jni/android_graphics_Canvas.cpp6
-rw-r--r--core/proto/android/service/procstats.proto4
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java128
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java2
-rw-r--r--graphics/java/android/graphics/Bitmap.java19
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java5
-rw-r--r--graphics/java/android/graphics/BitmapRegionDecoder.java3
-rw-r--r--graphics/java/android/graphics/BitmapShader.java4
-rw-r--r--graphics/java/android/graphics/Camera.java3
-rw-r--r--graphics/java/android/graphics/Canvas.java15
-rw-r--r--graphics/java/android/graphics/CanvasProperty.java3
-rw-r--r--graphics/java/android/graphics/ColorMatrixColorFilter.java4
-rw-r--r--graphics/java/android/graphics/FontFamily.java8
-rw-r--r--graphics/java/android/graphics/FontListParser.java2
-rw-r--r--graphics/java/android/graphics/GraphicBuffer.java5
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java2
-rw-r--r--graphics/java/android/graphics/ImageFormat.java3
-rw-r--r--graphics/java/android/graphics/LightingColorFilter.java3
-rw-r--r--graphics/java/android/graphics/LinearGradient.java10
-rw-r--r--graphics/java/android/graphics/Matrix.java3
-rw-r--r--graphics/java/android/graphics/Movie.java3
-rw-r--r--graphics/java/android/graphics/NinePatch.java5
-rw-r--r--graphics/java/android/graphics/Outline.java2
-rw-r--r--graphics/java/android/graphics/Paint.java8
-rw-r--r--graphics/java/android/graphics/Path.java3
-rw-r--r--graphics/java/android/graphics/Picture.java2
-rw-r--r--graphics/java/android/graphics/PorterDuff.java3
-rw-r--r--graphics/java/android/graphics/PorterDuffColorFilter.java3
-rw-r--r--graphics/java/android/graphics/RadialGradient.java9
-rw-r--r--graphics/java/android/graphics/Rect.java3
-rw-r--r--graphics/java/android/graphics/Region.java6
-rw-r--r--graphics/java/android/graphics/Shader.java2
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java7
-rw-r--r--graphics/java/android/graphics/SweepGradient.java7
-rw-r--r--graphics/java/android/graphics/TableMaskFilter.java3
-rw-r--r--graphics/java/android/graphics/TemporaryBuffer.java3
-rw-r--r--graphics/java/android/graphics/Typeface.java13
-rw-r--r--graphics/java/android/graphics/Xfermode.java3
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedImageDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java3
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/ClipDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/ColorDrawable.java3
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java7
-rw-r--r--graphics/java/android/graphics/drawable/DrawableInflater.java2
-rw-r--r--graphics/java/android/graphics/drawable/DrawableWrapper.java2
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java24
-rw-r--r--graphics/java/android/graphics/drawable/Icon.java10
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java3
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/ScaleDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/TransitionDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java9
-rw-r--r--graphics/java/android/graphics/fonts/FontVariationAxis.java3
-rw-r--r--graphics/java/android/graphics/pdf/PdfRenderer.java3
-rw-r--r--keystore/java/android/security/KeyStore.java6
-rw-r--r--media/java/android/media/MediaPlayer2.java20
-rw-r--r--media/java/android/media/MediaPlayer2Impl.java151
-rw-r--r--packages/ExtServices/AndroidManifest.xml6
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/Assistant.java21
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java42
-rw-r--r--packages/PackageInstaller/res/layout/uninstall_confirm.xml130
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml8
-rw-r--r--packages/SystemUI/README.md2
-rw-r--r--packages/SystemUI/proguard.flags2
-rw-r--r--packages/SystemUI/res/layout/car_fullscreen_user_pod.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java (renamed from packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java)117
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java (renamed from packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java)46
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java123
-rw-r--r--services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java13
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java13
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java38
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupState.java1
-rw-r--r--services/backup/java/com/android/server/backup/internal/PerformBackupTask.java1383
-rw-r--r--services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java39
-rw-r--r--services/backup/java/com/android/server/backup/remote/RemoteCall.java134
-rw-r--r--services/backup/java/com/android/server/backup/remote/RemoteCallable.java28
-rw-r--r--services/backup/java/com/android/server/backup/remote/RemoteResult.java116
-rw-r--r--services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java43
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java11
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java25
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java15
-rw-r--r--services/core/java/com/android/server/biometrics/common/AuthenticationClient.java14
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java11
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java13
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java90
-rw-r--r--services/core/java/com/android/server/location/ContextHubClientManager.java2
-rw-r--r--services/core/java/com/android/server/location/ContextHubService.java17
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java12
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java1036
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java85
-rw-r--r--services/core/java/com/android/server/policy/WindowOrientationListener.java4
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java2
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java20
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java113
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java260
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java881
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java3
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java134
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java2
-rw-r--r--services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java71
-rw-r--r--services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java24
-rw-r--r--services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java46
-rw-r--r--services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java244
-rw-r--r--services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java111
-rw-r--r--services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java46
-rw-r--r--services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java13
-rw-r--r--services/robotests/src/com/android/server/backup/testing/TestUtils.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java137
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java33
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java13
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java79
-rw-r--r--telecomm/java/android/telecom/Connection.java11
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java7
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapter.java12
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapterServant.java5
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java5
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java3
-rw-r--r--telephony/java/com/android/ims/internal/uce/common/CapInfo.java45
-rw-r--r--telephony/java/com/android/ims/internal/uce/common/StatusCode.java4
-rw-r--r--telephony/java/com/android/ims/internal/uce/common/UceLong.java6
-rw-r--r--telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java6
-rw-r--r--telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java3
-rw-r--r--telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java6
-rw-r--r--telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java7
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java4
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java3
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java6
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java3
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java5
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java7
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java10
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java5
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java12
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java3
-rw-r--r--telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java5
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--tests/FlickerTests/Android.mk35
-rw-r--r--tests/FlickerTests/AndroidManifest.xml36
-rw-r--r--tests/FlickerTests/AndroidTest.xml28
-rw-r--r--tests/FlickerTests/README.md146
-rw-r--r--tests/FlickerTests/TEST_MAPPING7
-rw-r--r--tests/FlickerTests/lib/Android.mk48
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java134
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java183
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java260
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java27
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java412
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java140
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java423
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java240
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java143
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java193
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java63
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java74
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java78
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java66
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java100
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java55
-rw-r--r--tests/FlickerTests/lib/test/Android.mk36
-rw-r--r--tests/FlickerTests/lib/test/AndroidManifest.xml26
-rw-r--r--tests/FlickerTests/lib/test/AndroidTest.xml20
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pbbin0 -> 565800 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pbbin0 -> 3580523 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pbbin0 -> 1936878 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pbbin0 -> 355010 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pbbin0 -> 194353 bytes
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java181
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java60
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java88
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java229
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java35
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java258
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java108
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java49
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java78
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java70
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java52
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java79
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java147
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java78
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java91
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java317
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java167
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java125
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java106
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java96
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java106
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java68
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java187
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java169
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java95
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java59
-rw-r--r--tests/FlickerTests/test-apps/Android.mk15
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/Android.mk31
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml64
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml23
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java33
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java45
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java72
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java33
-rw-r--r--vr/java/com/google/vr/platform/DeviceInfo.java2
-rw-r--r--vr/java/com/google/vr/platform/Dvr.java3
-rw-r--r--wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java84
291 files changed, 12095 insertions, 3276 deletions
diff --git a/Android.bp b/Android.bp
index 21b84040ac0c..6815a0db9d44 100644
--- a/Android.bp
+++ b/Android.bp
@@ -94,6 +94,7 @@ java_library {
"core/java/android/app/trust/IStrongAuthTracker.aidl",
"core/java/android/app/trust/ITrustManager.aidl",
"core/java/android/app/trust/ITrustListener.aidl",
+ "core/java/android/app/backup/IBackupCallback.aidl",
"core/java/android/app/backup/IBackupManager.aidl",
"core/java/android/app/backup/IBackupObserver.aidl",
"core/java/android/app/backup/IBackupManagerMonitor.aidl",
diff --git a/Android.mk b/Android.mk
index f81f756d43cd..edbb94ddb22a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -171,15 +171,17 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-hidePackage com.android.server
# Convert an sdk level to a "since" argument.
-since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.*) $(1)
+since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.$(2)) $(1)
-finalized_sdks := $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml))
-finalized_sdks += $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
- $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt))
-finalized_sdks := $(call numerically_sort,$(finalized_sdks))
+finalized_xml_sdks := $(call numerically_sort,\
+ $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
+ $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml)))
+finalized_txt_sdks := $(call numerically_sort,\
+ $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
+ $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt)))
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_sdks),$(call since-arg,$(sdk)))
+framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_xml_sdks),$(call since-arg,$(sdk),xml))
+framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_txt_sdks),$(call since-arg,$(sdk),txt))
ifneq ($(PLATFORM_VERSION_CODENAME),REL)
framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-since ./frameworks/base/api/current.txt $(PLATFORM_VERSION_CODENAME)
@@ -361,8 +363,8 @@ LOCAL_BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
LOCAL_SRC_GREYLIST := frameworks/base/config/hiddenapi-light-greylist.txt
LOCAL_SRC_VENDOR_LIST := frameworks/base/config/hiddenapi-vendor-list.txt
LOCAL_SRC_FORCE_BLACKLIST := frameworks/base/config/hiddenapi-force-blacklist.txt
-LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_DEX_API_FILE)
-LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
+LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)
LOCAL_SRC_REMOVED_API := $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
LOCAL_SRC_ALL := \
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 05293a201911..66a2600d946a 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -48,7 +48,7 @@ public class BinderCallsStatsPerfTest {
@Before
public void setUp() {
- mBinderCallsStats = new BinderCallsStats(new Random());
+ mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
}
@After
diff --git a/api/system-current.txt b/api/system-current.txt
index 924d3afbd88f..01f4709b9df5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4524,9 +4524,11 @@ package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
method public android.os.IBinder onBind(android.content.Intent);
method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+ field public static final java.lang.String RESOURCE_AVAILABLE_ALGORITHMS = "autofill_field_classification_available_algorithms";
+ field public static final java.lang.String RESOURCE_DEFAULT_ALGORITHM = "autofill_field_classification_default_algorithm";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
- field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
- field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
+ field public static final deprecated java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+ field public static final deprecated java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
}
}
@@ -4665,6 +4667,7 @@ package android.service.notification {
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final java.lang.String KEY_PEOPLE = "key_people";
field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
+ field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 462d22e2e9b3..1d08da5be957 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1044,6 +1044,7 @@ package android.service.notification {
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final java.lang.String KEY_PEOPLE = "key_people";
field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
+ field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blacklist.txt
index 3a9e2d1d500c..dca3b525c06a 100644
--- a/config/hiddenapi-force-blacklist.txt
+++ b/config/hiddenapi-force-blacklist.txt
@@ -1,38 +1,38 @@
Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V
Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup;
Ljava/lang/invoke/VarHandle;->acquireFence()V
-Ljava/lang/invoke/VarHandle;->compareAndExchange([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndSet([[Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndSet([Ljava/lang/Object;)Z
Ljava/lang/invoke/VarHandle;->fullFence()V
-Ljava/lang/invoke/VarHandle;->get([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAdd([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAddAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAddRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSet([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSetAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSetRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getOpaque([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getVolatile([[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->get([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAdd([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAddAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAddRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSet([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSetAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSetRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getOpaque([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getVolatile([Ljava/lang/Object;)Ljava/lang/Object;
Ljava/lang/invoke/VarHandle;->loadLoadFence()V
Ljava/lang/invoke/VarHandle;->releaseFence()V
-Ljava/lang/invoke/VarHandle;->set([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setOpaque([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setRelease([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setVolatile([[Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->set([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setOpaque([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setRelease([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setVolatile([Ljava/lang/Object;)V
Ljava/lang/invoke/VarHandle;->storeStoreFence()V
-Ljava/lang/invoke/VarHandle;->weakCompareAndSet([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([[Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSet([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([Ljava/lang/Object;)Z
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index b936fe73d805..0b94c27db533 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,6 +1,3 @@
-Landroid/accessibilityservice/AccessibilityService;->mInfo:Landroid/accessibilityservice/AccessibilityServiceInfo;
-Landroid/accessibilityservice/AccessibilityService;->mWindowToken:Landroid/os/IBinder;
-Landroid/accessibilityservice/AccessibilityServiceInfo;->setCapabilities(I)V
Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;-><init>()V
Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accessibilityservice/IAccessibilityServiceConnection;
Landroid/accounts/Account;->accessId:Ljava/lang/String;
@@ -51,13 +48,6 @@ Landroid/accounts/IAccountManagerResponse$Stub;-><init>()V
Landroid/accounts/IAccountManagerResponse$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManagerResponse;
Landroid/accounts/IAccountManagerResponse;->onError(ILjava/lang/String;)V
Landroid/accounts/IAccountManagerResponse;->onResult(Landroid/os/Bundle;)V
-Landroid/animation/Animator;->reverse()V
-Landroid/animation/ArgbEvaluator;->getInstance()Landroid/animation/ArgbEvaluator;
-Landroid/animation/LayoutTransition;->cancel()V
-Landroid/animation/LayoutTransition;->cancel(I)V
-Landroid/animation/ValueAnimator;->animateValue(F)V
-Landroid/animation/ValueAnimator;->mDuration:J
-Landroid/animation/ValueAnimator;->sDurationScale:F
Landroid/app/ActionBar;->collapseActionView()Z
Landroid/app/ActionBar;->DISPLAY_TITLE_MULTIPLE_LINES:I
Landroid/app/ActionBar;->setShowHideAnimationEnabled(Z)V
@@ -533,7 +523,7 @@ Landroid/app/DownloadManager$Query;->setOnlyIncludeVisibleInDownloadsUi(Z)Landro
Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
Landroid/app/DownloadManager;->getWhereArgsForIds([J)[Ljava/lang/String;
Landroid/app/DownloadManager;->getWhereClauseForIds([J)Ljava/lang/String;
-Landroid/app/DownloadManager;->restartDownload([[J)V
+Landroid/app/DownloadManager;->restartDownload([J)V
Landroid/app/DownloadManager;->setAccessAllDownloads(Z)V
Landroid/app/DownloadManager;->setAccessFilename(Z)V
Landroid/app/DownloadManager;->UNDERLYING_COLUMNS:[Ljava/lang/String;
@@ -1049,7 +1039,6 @@ Landroid/bluetooth/BluetoothClass;-><init>(I)V
Landroid/bluetooth/BluetoothClass;->doesClassMatch(I)Z
Landroid/bluetooth/BluetoothClass;->PROFILE_A2DP:I
Landroid/bluetooth/BluetoothClass;->PROFILE_HEADSET:I
-Landroid/bluetooth/BluetoothCodecConfig;
Landroid/bluetooth/BluetoothCodecConfig;-><init>(IIIIIJJJJ)V
Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_16:I
Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_24:I
@@ -1085,7 +1074,6 @@ Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_INVALID:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_LDAC:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_MAX:I
Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_SBC:I
-Landroid/bluetooth/BluetoothCodecStatus;
Landroid/bluetooth/BluetoothCodecStatus;->EXTRA_CODEC_STATUS:Ljava/lang/String;
Landroid/bluetooth/BluetoothCodecStatus;->getCodecConfig()Landroid/bluetooth/BluetoothCodecConfig;
Landroid/bluetooth/BluetoothCodecStatus;->getCodecsLocalCapabilities()[Landroid/bluetooth/BluetoothCodecConfig;
@@ -1453,10 +1441,15 @@ Landroid/content/pm/BaseParceledListSlice;->writeParcelableCreator(Ljava/lang/Ob
Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName;
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
+Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
+Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
@@ -1555,6 +1548,8 @@ Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;-><init>(Landroid/os/IBinde
Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
+Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
@@ -2025,8 +2020,6 @@ Landroid/database/sqlite/SQLiteStatement;-><init>(Landroid/database/sqlite/SQLit
Landroid/database/sqlite/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
Landroid/database/sqlite/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
Landroid/database/sqlite/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
-Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
-Landroid/ddm/DdmHandleAppName;->setAppName(Ljava/lang/String;I)V
Landroid/filterfw/core/Filter;-><init>(Ljava/lang/String;)V
Landroid/filterfw/core/Filter;->isAvailable(Ljava/lang/String;)Z
Landroid/filterfw/core/Filter;->setInputValue(Ljava/lang/String;Ljava/lang/Object;)V
@@ -2093,268 +2086,7 @@ Landroid/filterfw/geometry/Quad;->p3:Landroid/filterfw/geometry/Point;
Landroid/filterfw/GraphEnvironment;-><init>()V
Landroid/filterfw/GraphEnvironment;->getRunner(II)Landroid/filterfw/core/GraphRunner;
Landroid/filterfw/GraphEnvironment;->loadGraph(Landroid/content/Context;I)I
-Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
-Landroid/graphics/Bitmap$Config;->nativeInt:I
-Landroid/graphics/Bitmap$Config;->nativeToConfig(I)Landroid/graphics/Bitmap$Config;
-Landroid/graphics/Bitmap;-><init>(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;)V
-Landroid/graphics/Bitmap;->createAshmemBitmap()Landroid/graphics/Bitmap;
-Landroid/graphics/Bitmap;->createAshmemBitmap(Landroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
-Landroid/graphics/Bitmap;->getDefaultDensity()I
-Landroid/graphics/Bitmap;->mHeight:I
-Landroid/graphics/Bitmap;->mNativePtr:J
-Landroid/graphics/Bitmap;->mNinePatchChunk:[B
-Landroid/graphics/Bitmap;->mNinePatchInsets:Landroid/graphics/NinePatch$InsetStruct;
-Landroid/graphics/Bitmap;->mWidth:I
-Landroid/graphics/Bitmap;->nativeReconfigure(JIIIZ)V
-Landroid/graphics/Bitmap;->reinit(IIZ)V
-Landroid/graphics/Bitmap;->scaleFromDensity(III)I
-Landroid/graphics/Bitmap;->setDefaultDensity(I)V
-Landroid/graphics/Bitmap;->setNinePatchChunk([B)V
-Landroid/graphics/BitmapFactory;->nativeDecodeAsset(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapFactory;->nativeDecodeByteArray([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapFactory;->nativeDecodeFileDescriptor(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapFactory;->nativeDecodeStream(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapRegionDecoder;-><init>(J)V
-Landroid/graphics/BitmapRegionDecoder;->nativeNewInstance([BIIZ)Landroid/graphics/BitmapRegionDecoder;
-Landroid/graphics/BitmapShader;->mBitmap:Landroid/graphics/Bitmap;
-Landroid/graphics/BitmapShader;->mTileX:I
-Landroid/graphics/BitmapShader;->mTileY:I
-Landroid/graphics/Camera;->native_instance:J
-Landroid/graphics/Canvas;-><init>(J)V
-Landroid/graphics/Canvas;->freeCaches()V
-Landroid/graphics/Canvas;->freeTextLayoutCaches()V
-Landroid/graphics/Canvas;->getGL()Ljavax/microedition/khronos/opengles/GL;
-Landroid/graphics/Canvas;->getNativeCanvasWrapper()J
-Landroid/graphics/Canvas;->mBitmap:Landroid/graphics/Bitmap;
-Landroid/graphics/Canvas;->release()V
-Landroid/graphics/Canvas;->setScreenDensity(I)V
-Landroid/graphics/CanvasProperty;->createFloat(F)Landroid/graphics/CanvasProperty;
-Landroid/graphics/CanvasProperty;->createPaint(Landroid/graphics/Paint;)Landroid/graphics/CanvasProperty;
-Landroid/graphics/ColorMatrixColorFilter;->mMatrix:Landroid/graphics/ColorMatrix;
-Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
-Landroid/graphics/ColorMatrixColorFilter;->setColorMatrixArray([F)V
-Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
-Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesCount(I)V
-Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesDuration(I)V
-Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray;
-Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mTransitions:Landroid/util/LongSparseLongArray;
-Landroid/graphics/drawable/AnimatedStateListDrawable;->mState:Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;
-Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;->callOnFinished(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V
-Landroid/graphics/drawable/AnimatedVectorDrawable;->forceAnimationOnUI()V
-Landroid/graphics/drawable/AnimatedVectorDrawable;->mAnimatedVectorState:Landroid/graphics/drawable/AnimatedVectorDrawable$AnimatedVectorDrawableState;
-Landroid/graphics/drawable/AnimatedVectorDrawable;->mAnimatorSet:Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;
-Landroid/graphics/drawable/AnimationDrawable;->mCurFrame:I
-Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorStateList;
-Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
-Landroid/graphics/drawable/BitmapDrawable;->mBitmapState:Landroid/graphics/drawable/BitmapDrawable$BitmapState;
-Landroid/graphics/drawable/BitmapDrawable;->mTargetDensity:I
-Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
-Landroid/graphics/drawable/ClipDrawable;->mState:Landroid/graphics/drawable/ClipDrawable$ClipState;
-Landroid/graphics/drawable/ColorDrawable$ColorState;->mUseColor:I
-Landroid/graphics/drawable/ColorDrawable;->mPaint:Landroid/graphics/Paint;
-Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
-Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
-Landroid/graphics/drawable/Drawable;->mSrcDensityOverride:I
-Landroid/graphics/drawable/Drawable;->parseTintMode(ILandroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuff$Mode;
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;-><init>(Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;Landroid/graphics/drawable/DrawableContainer;Landroid/content/res/Resources;)V
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mDrawables:[Landroid/graphics/drawable/Drawable;
-Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mHasColorFilter:Z
-Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
-Landroid/graphics/drawable/DrawableContainer;->mLastDrawable:Landroid/graphics/drawable/Drawable;
-Landroid/graphics/drawable/DrawableInflater;->mClassLoader:Ljava/lang/ClassLoader;
-Landroid/graphics/drawable/DrawableWrapper;->mState:Landroid/graphics/drawable/DrawableWrapper$DrawableWrapperState;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mAngle:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradient:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradientColors:[I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mHeight:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mInnerRadius:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mInnerRadiusRatio:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mOrientation:Landroid/graphics/drawable/GradientDrawable$Orientation;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mPositions:[F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadius:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadiusArray:[F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mShape:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mSolidColors:Landroid/content/res/ColorStateList;
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashGap:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashWidth:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeWidth:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mThickness:I
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mThicknessRatio:F
-Landroid/graphics/drawable/GradientDrawable$GradientState;->mWidth:I
-Landroid/graphics/drawable/GradientDrawable;->mFillPaint:Landroid/graphics/Paint;
-Landroid/graphics/drawable/GradientDrawable;->mGradientState:Landroid/graphics/drawable/GradientDrawable$GradientState;
-Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect;
-Landroid/graphics/drawable/GradientDrawable;->mStrokePaint:Landroid/graphics/Paint;
-Landroid/graphics/drawable/Icon;->createWithResource(Landroid/content/res/Resources;I)Landroid/graphics/drawable/Icon;
-Landroid/graphics/drawable/Icon;->getBitmap()Landroid/graphics/Bitmap;
-Landroid/graphics/drawable/Icon;->getDataBytes()[B
-Landroid/graphics/drawable/Icon;->getDataLength()I
-Landroid/graphics/drawable/Icon;->getDataOffset()I
-Landroid/graphics/drawable/Icon;->getResources()Landroid/content/res/Resources;
-Landroid/graphics/drawable/Icon;->hasTint()Z
-Landroid/graphics/drawable/Icon;->mString1:Ljava/lang/String;
-Landroid/graphics/drawable/Icon;->mType:I
-Landroid/graphics/drawable/InsetDrawable;->mState:Landroid/graphics/drawable/InsetDrawable$InsetState;
-Landroid/graphics/drawable/LayerDrawable$ChildDrawable;->mDrawable:Landroid/graphics/drawable/Drawable;
-Landroid/graphics/drawable/LayerDrawable$LayerState;->mChildren:[Landroid/graphics/drawable/LayerDrawable$ChildDrawable;
-Landroid/graphics/drawable/LayerDrawable;->addLayer(Landroid/graphics/drawable/LayerDrawable$ChildDrawable;)I
-Landroid/graphics/drawable/LayerDrawable;->ensurePadding()V
-Landroid/graphics/drawable/LayerDrawable;->mLayerState:Landroid/graphics/drawable/LayerDrawable$LayerState;
-Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
-Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
-Landroid/graphics/drawable/RippleDrawable$RippleState;->mColor:Landroid/content/res/ColorStateList;
-Landroid/graphics/drawable/RippleDrawable;->getRipplePaint()Landroid/graphics/Paint;
-Landroid/graphics/drawable/RippleDrawable;->mDensity:I
-Landroid/graphics/drawable/RippleDrawable;->mState:Landroid/graphics/drawable/RippleDrawable$RippleState;
-Landroid/graphics/drawable/RippleDrawable;->setForceSoftware(Z)V
-Landroid/graphics/drawable/RotateDrawable;->mState:Landroid/graphics/drawable/RotateDrawable$RotateState;
-Landroid/graphics/drawable/ScaleDrawable;->mState:Landroid/graphics/drawable/ScaleDrawable$ScaleState;
-Landroid/graphics/drawable/StateListDrawable$StateListState;->addStateSet([ILandroid/graphics/drawable/Drawable;)I
-Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
-Landroid/graphics/drawable/StateListDrawable;->mStateListState:Landroid/graphics/drawable/StateListDrawable$StateListState;
-Landroid/graphics/drawable/StateListDrawable;->updateStateFromTypedArray(Landroid/content/res/TypedArray;)V
-Landroid/graphics/drawable/TransitionDrawable;->mAlpha:I
-Landroid/graphics/drawable/TransitionDrawable;->mCrossFade:Z
-Landroid/graphics/drawable/TransitionDrawable;->mTo:I
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setPivotX(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setPivotY(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setRotation(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setTranslateX(F)V
-Landroid/graphics/drawable/VectorDrawable$VGroup;->setTranslateY(F)V
-Landroid/graphics/drawable/VectorDrawable;->getTargetByName(Ljava/lang/String;)Ljava/lang/Object;
-Landroid/graphics/drawable/VectorDrawable;->mTintFilter:Landroid/graphics/PorterDuffColorFilter;
-Landroid/graphics/drawable/VectorDrawable;->setAllowCaching(Z)V
-Landroid/graphics/FontFamily;-><init>()V
-Landroid/graphics/FontFamily;-><init>([Ljava/lang/String;I)V
-Landroid/graphics/FontFamily;->abortCreation()V
-Landroid/graphics/FontFamily;->addFontFromAssetManager(Landroid/content/res/AssetManager;Ljava/lang/String;IZIII[Landroid/graphics/fonts/FontVariationAxis;)Z
-Landroid/graphics/FontFamily;->addFontFromBuffer(Ljava/nio/ByteBuffer;I[Landroid/graphics/fonts/FontVariationAxis;II)Z
-Landroid/graphics/FontFamily;->freeze()Z
-Landroid/graphics/FontFamily;->mNativePtr:J
-Landroid/graphics/FontListParser;->parse(Ljava/io/InputStream;)Landroid/text/FontConfig;
-Landroid/graphics/fonts/FontVariationAxis;->mStyleValue:F
-Landroid/graphics/fonts/FontVariationAxis;->mTag:I
-Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
-Landroid/graphics/GraphicBuffer;->createFromExisting(IIIIJ)Landroid/graphics/GraphicBuffer;
-Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/graphics/GraphicBuffer;->mNativeObject:J
-Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
-Landroid/graphics/ImageFormat;->Y8:I
-Landroid/graphics/LightingColorFilter;->setColorAdd(I)V
-Landroid/graphics/LightingColorFilter;->setColorMultiply(I)V
-Landroid/graphics/LinearGradient;->mColor0:I
-Landroid/graphics/LinearGradient;->mColor1:I
-Landroid/graphics/LinearGradient;->mColors:[I
-Landroid/graphics/LinearGradient;->mPositions:[F
-Landroid/graphics/LinearGradient;->mTileMode:Landroid/graphics/Shader$TileMode;
-Landroid/graphics/LinearGradient;->mX0:F
-Landroid/graphics/LinearGradient;->mX1:F
-Landroid/graphics/LinearGradient;->mY0:F
-Landroid/graphics/LinearGradient;->mY1:F
-Landroid/graphics/Matrix;->IDENTITY_MATRIX:Landroid/graphics/Matrix;
-Landroid/graphics/Matrix;->native_instance:J
-Landroid/graphics/Movie;-><init>(J)V
-Landroid/graphics/Movie;->mNativeMovie:J
-Landroid/graphics/NinePatch$InsetStruct;-><init>(IIIIIIIIFIF)V
-Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
-Landroid/graphics/NinePatch;->mNativeChunk:J
-Landroid/graphics/Outline;->mRect:Landroid/graphics/Rect;
-Landroid/graphics/Paint;->getNativeInstance()J
-Landroid/graphics/Paint;->getTextRunAdvances([CIIIIZ[FI)F
-Landroid/graphics/Paint;->getTextRunCursor([CIIIII)I
-Landroid/graphics/Paint;->mNativePaint:J
-Landroid/graphics/Paint;->mTypeface:Landroid/graphics/Typeface;
-Landroid/graphics/Paint;->setCompatibilityScaling(F)V
-Landroid/graphics/Paint;->setHyphenEdit(I)V
-Landroid/graphics/Path;->isSimplePath:Z
-Landroid/graphics/Path;->rects:Landroid/graphics/Region;
-Landroid/graphics/pdf/PdfRenderer;->doClose()V
-Landroid/graphics/pdf/PdfRenderer;->mCurrentPage:Landroid/graphics/pdf/PdfRenderer$Page;
-Landroid/graphics/Picture;->mNativePicture:J
-Landroid/graphics/PorterDuff$Mode;->nativeInt:I
-Landroid/graphics/PorterDuffColorFilter;->getColor()I
-Landroid/graphics/PorterDuffColorFilter;->getMode()Landroid/graphics/PorterDuff$Mode;
-Landroid/graphics/RadialGradient;->mCenterColor:I
-Landroid/graphics/RadialGradient;->mColors:[I
-Landroid/graphics/RadialGradient;->mEdgeColor:I
-Landroid/graphics/RadialGradient;->mPositions:[F
-Landroid/graphics/RadialGradient;->mRadius:F
-Landroid/graphics/RadialGradient;->mTileMode:Landroid/graphics/Shader$TileMode;
-Landroid/graphics/RadialGradient;->mX:F
-Landroid/graphics/RadialGradient;->mY:F
-Landroid/graphics/Rect;->printShortString(Ljava/io/PrintWriter;)V
-Landroid/graphics/Rect;->scale(F)V
-Landroid/graphics/Region$Op;->nativeInt:I
-Landroid/graphics/Region;-><init>(JI)V
-Landroid/graphics/Region;->mNativeRegion:J
-Landroid/graphics/Region;->recycle()V
-Landroid/graphics/Region;->scale(F)V
-Landroid/graphics/Shader$TileMode;->nativeInt:I
-Landroid/graphics/SurfaceTexture;->mFrameAvailableListener:J
-Landroid/graphics/SurfaceTexture;->mOnFrameAvailableHandler:Landroid/os/Handler;
-Landroid/graphics/SurfaceTexture;->mProducer:J
-Landroid/graphics/SurfaceTexture;->mSurfaceTexture:J
-Landroid/graphics/SurfaceTexture;->nativeDetachFromGLContext()I
-Landroid/graphics/SurfaceTexture;->postEventFromNative(Ljava/lang/ref/WeakReference;)V
-Landroid/graphics/SweepGradient;->mColor0:I
-Landroid/graphics/SweepGradient;->mColor1:I
-Landroid/graphics/SweepGradient;->mColors:[I
-Landroid/graphics/SweepGradient;->mCx:F
-Landroid/graphics/SweepGradient;->mCy:F
-Landroid/graphics/SweepGradient;->mPositions:[F
-Landroid/graphics/TableMaskFilter;->CreateClipTable(II)Landroid/graphics/TableMaskFilter;
-Landroid/graphics/TemporaryBuffer;->obtain(I)[C
-Landroid/graphics/TemporaryBuffer;->recycle([C)V
-Landroid/graphics/Typeface;-><init>(J)V
-Landroid/graphics/Typeface;->createFromFamilies([Landroid/graphics/FontFamily;)Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;II)Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->createFromFamiliesWithDefault([Landroid/graphics/FontFamily;Ljava/lang/String;II)Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->mStyle:I
-Landroid/graphics/Typeface;->nativeCreateFromArray([JII)J
-Landroid/graphics/Typeface;->nativeCreateWeightAlias(JI)J
-Landroid/graphics/Typeface;->native_instance:J
-Landroid/graphics/Typeface;->sDefaults:[Landroid/graphics/Typeface;
-Landroid/graphics/Typeface;->setDefault(Landroid/graphics/Typeface;)V
-Landroid/graphics/Typeface;->sSystemFallbackMap:Ljava/util/Map;
-Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
-Landroid/graphics/Xfermode;->porterDuffMode:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_GOOD:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_IMAGER_DIRTY:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_INSUFFICIENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_PARTIAL:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_TOO_FAST:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_TOO_SLOW:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_CANCELED:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_HW_NOT_PRESENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_HW_UNAVAILABLE:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_LOCKOUT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_LOCKOUT_PERMANENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_NO_BIOMETRICS:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_NO_SPACE:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_TIMEOUT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_UNABLE_TO_PROCESS:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_USER_CANCELED:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR:I
Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR_BASE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_GOOD:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_IMAGER_DIRTY:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_INSUFFICIENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_PARTIAL:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_TOO_FAST:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_TOO_SLOW:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_CANCELED:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_HW_NOT_PRESENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_HW_UNAVAILABLE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_LOCKOUT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_LOCKOUT_PERMANENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_NO_FINGERPRINTS:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_NO_SPACE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_TIMEOUT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_UNABLE_TO_PROCESS:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_USER_CANCELED:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR:I
Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR_BASE:I
Landroid/hardware/Camera$Parameters;->copyFrom(Landroid/hardware/Camera$Parameters;)V
Landroid/hardware/Camera$Parameters;->dump()V
@@ -2615,7 +2347,6 @@ Landroid/icu/text/Transliterator;->getInstance(Ljava/lang/String;I)Landroid/icu/
Landroid/icu/text/Transliterator;->transliterate(Landroid/icu/text/Replaceable;Landroid/icu/text/Transliterator$Position;Ljava/lang/String;)V
Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/UForwardCharacterIterator;->DONE:I
Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
Landroid/icu/util/PersianCalendar;-><init>(Ljava/util/Locale;)V
Landroid/icu/util/UResourceBundle;->getBundleInstance(Ljava/lang/String;Landroid/icu/util/ULocale;)Landroid/icu/util/UResourceBundle;
@@ -2624,26 +2355,6 @@ Landroid/icu/util/UResourceBundle;->getString()Ljava/lang/String;
Landroid/icu/util/UResourceBundle;->getType()I
Landroid/icu/util/UResourceBundleIterator;->hasNext()Z
Landroid/icu/util/UResourceBundleIterator;->next()Landroid/icu/util/UResourceBundle;
-Landroid/inputmethodservice/InputMethodService$SettingsObserver;->shouldShowImeWithHardKeyboard()Z
-Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
-Landroid/inputmethodservice/InputMethodService;->mExtractView:Landroid/view/View;
-Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
-Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
-Landroid/inputmethodservice/InputMethodService;->mTheme:I
-Landroid/inputmethodservice/InputMethodService;->mTmpInsets:Landroid/inputmethodservice/InputMethodService$Insets;
-Landroid/inputmethodservice/InputMethodService;->onExtractedDeleteText(II)V
-Landroid/inputmethodservice/InputMethodService;->onExtractedReplaceText(IILjava/lang/CharSequence;)V
-Landroid/inputmethodservice/InputMethodService;->onExtractedSetSpan(Ljava/lang/Object;III)V
-Landroid/inputmethodservice/Keyboard;->mModifierKeys:Ljava/util/List;
-Landroid/inputmethodservice/Keyboard;->mTotalHeight:I
-Landroid/inputmethodservice/Keyboard;->mTotalWidth:I
-Landroid/inputmethodservice/Keyboard;->resize(II)V
-Landroid/inputmethodservice/KeyboardView;->mKeyBackground:Landroid/graphics/drawable/Drawable;
-Landroid/inputmethodservice/KeyboardView;->mLabelTextSize:I
-Landroid/inputmethodservice/KeyboardView;->mPreviewText:Landroid/widget/TextView;
-Landroid/inputmethodservice/KeyboardView;->openPopupIfRequired(Landroid/view/MotionEvent;)Z
-Landroid/inputmethodservice/KeyboardView;->repeatKey()Z
-Landroid/inputmethodservice/KeyboardView;->showKey(I)V
Landroid/location/Country;-><init>(Ljava/lang/String;I)V
Landroid/location/Country;->getCountryIso()Ljava/lang/String;
Landroid/location/Country;->getSource()I
@@ -3143,81 +2854,6 @@ Landroid/media/ThumbnailUtils;->transform(Landroid/graphics/Matrix;Landroid/grap
Landroid/media/TimedText;->getObject(I)Ljava/lang/Object;
Landroid/media/ToneGenerator;->mNativeContext:J
Landroid/media/TtmlRenderer;-><init>(Landroid/content/Context;)V
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_16_9:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_1_1:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_2_3:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_3_2:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_4_3:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_AVAILABLE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_FREE_WITH_SUBSCRIPTION:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_PAID_CONTENT:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_AUTHOR:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_AVAILABILITY:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_BROWSABLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_CONTENT_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_DURATION_MILLIS:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTENT_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERACTION_COUNT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERACTION_TYPE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERNAL_PROVIDER_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_ITEM_COUNT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LAST_PLAYBACK_POSITION_MILLIS:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LIVE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LOGO_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_OFFER_PRICE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_POSTER_ART_ASPECT_RATIO:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_PREVIEW_VIDEO_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_RELEASE_DATE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_STARTING_PRICE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_THUMBNAIL_ASPECT_RATIO:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_TRANSIENT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_TYPE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_FANS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_FOLLOWERS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_LIKES:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_LISTENS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_THUMBS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_VIEWERS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_VIEWS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_ALBUM:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_ARTIST:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_CHANNEL:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_CLIP:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_EVENT:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_MOVIE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_PLAYLIST:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_STATION:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TRACK:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_EPISODE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_SEASON:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_SERIES:I
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_AUDIO_LANGUAGE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_CANONICAL_GENRE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_CONTENT_RATING:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_EPISODE_DISPLAY_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_EPISODE_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_DATA:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG1:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG2:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG3:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG4:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_LONG_DESCRIPTION:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_POSTER_ART_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_REVIEW_RATING:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_REVIEW_RATING_STYLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEARCHABLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_DISPLAY_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SERIES_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SHORT_DESCRIPTION:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_THUMBNAIL_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VERSION_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VIDEO_HEIGHT:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VIDEO_WIDTH:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_PERCENTAGE:I
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_STARS:I
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_THUMBS_UP_DOWN:I
Landroid/media/tv/TvInputInfo;->getComponent()Landroid/content/ComponentName;
Landroid/media/tv/TvInputService$Session;->mOverlayFrame:Landroid/graphics/Rect;
Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -3709,28 +3345,6 @@ Landroid/net/wifi/WifiSsid;->CREATOR:Landroid/os/Parcelable$Creator;
Landroid/net/wifi/WifiSsid;->getOctets()[B
Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String;
Landroid/net/wifi/WifiSsid;->octets:Ljava/io/ByteArrayOutputStream;
-Landroid/nfc/cardemulation/AidGroup;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Landroid/nfc/cardemulation/AidGroup;->aids:Ljava/util/List;
-Landroid/nfc/cardemulation/AidGroup;->category:Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->createFromXml(Lorg/xmlpull/v1/XmlPullParser;)Landroid/nfc/cardemulation/AidGroup;
-Landroid/nfc/cardemulation/AidGroup;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/nfc/cardemulation/AidGroup;->description:Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->getAids()Ljava/util/List;
-Landroid/nfc/cardemulation/AidGroup;->getCategory()Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->writeAsXml(Lorg/xmlpull/v1/XmlSerializer;)V
-Landroid/nfc/cardemulation/ApduServiceInfo;-><init>(Landroid/content/pm/PackageManager;Landroid/content/pm/ResolveInfo;Z)V
-Landroid/nfc/cardemulation/ApduServiceInfo;-><init>(Landroid/content/pm/ResolveInfo;ZLjava/lang/String;Ljava/util/ArrayList;Ljava/util/ArrayList;ZIILjava/lang/String;)V
-Landroid/nfc/cardemulation/ApduServiceInfo;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getDescription()Ljava/lang/String;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getSettingsActivityName()Ljava/lang/String;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getUid()I
-Landroid/nfc/cardemulation/ApduServiceInfo;->isOnHost()Z
-Landroid/nfc/cardemulation/ApduServiceInfo;->loadBanner(Landroid/content/pm/PackageManager;)Landroid/graphics/drawable/Drawable;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mDynamicAidGroups:Ljava/util/HashMap;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mService:Landroid/content/pm/ResolveInfo;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mStaticAidGroups:Ljava/util/HashMap;
-Landroid/nfc/cardemulation/ApduServiceInfo;->requiresUnlock()Z
-Landroid/nfc/ErrorCodes;->isError(I)Z
Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I
Landroid/nfc/INfcAdapterExtras;->authenticate(Ljava/lang/String;[B)V
Landroid/nfc/INfcAdapterExtras;->close(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
@@ -3739,21 +3353,6 @@ Landroid/nfc/INfcAdapterExtras;->getDriverName(Ljava/lang/String;)Ljava/lang/Str
Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
-Landroid/nfc/NdefRecord;->mId:[B
-Landroid/nfc/NfcActivityManager;->mAdapter:Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->attemptDeadServiceRecovery(Ljava/lang/Exception;)V
-Landroid/nfc/NfcAdapter;->getAdapterState()I
-Landroid/nfc/NfcAdapter;->getContext()Landroid/content/Context;
-Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->getNfcAdapter(Landroid/content/Context;)Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->getNfcAdapterExtrasInterface()Landroid/nfc/INfcAdapterExtras;
-Landroid/nfc/NfcAdapter;->getService()Landroid/nfc/INfcAdapter;
-Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
-Landroid/nfc/NfcAdapter;->sService:Landroid/nfc/INfcAdapter;
-Landroid/nfc/NfcManager;-><init>(Landroid/content/Context;)V
-Landroid/nfc/Tag;->getServiceHandle()I
-Landroid/nfc/Tag;->getTagService()Landroid/nfc/INfcTag;
-Landroid/nfc/Tag;->mId:[B
Landroid/opengl/EGL14;->eglGetDisplay(J)Landroid/opengl/EGLDisplay;
Landroid/opengl/GLES20;->glGetActiveAttrib(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V
Landroid/opengl/GLES20;->glGetActiveUniform(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V
@@ -4445,9 +4044,6 @@ Landroid/provider/CalendarContract$CalendarAlerts;->findNextAlarmTime(Landroid/c
Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V
Landroid/provider/CalendarContract$CalendarAlerts;->scheduleAlarm(Landroid/content/Context;Landroid/app/AlarmManager;J)V
Landroid/provider/CallLog$Calls;->addCall(Lcom/android/internal/telephony/CallerInfo;Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIILandroid/telecom/PhoneAccountHandle;JILjava/lang/Long;ZLandroid/os/UserHandle;Z)Landroid/net/Uri;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX:Ljava/lang/String;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX_COUNTS:Ljava/lang/String;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX_TITLES:Ljava/lang/String;
Landroid/provider/ContactsContract$Contacts$AggregationSuggestions;->builder()Landroid/provider/ContactsContract$Contacts$AggregationSuggestions$Builder;
Landroid/provider/ContactsContract$Contacts;->CORP_CONTENT_URI:Landroid/net/Uri;
Landroid/provider/ContactsContract$QuickContact;->composeQuickContactsIntent(Landroid/content/Context;Landroid/graphics/Rect;Landroid/net/Uri;I[Ljava/lang/String;)Landroid/content/Intent;
@@ -4971,7 +4567,7 @@ Landroid/renderscript/RSSurfaceView;-><init>(Landroid/content/Context;)V
Landroid/renderscript/RSSurfaceView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
Landroid/renderscript/Script$Builder;-><init>(Landroid/renderscript/RenderScript;)V
Landroid/renderscript/Script$Builder;->mRS:Landroid/renderscript/RenderScript;
-Landroid/security/Credentials;->convertToPem([[Ljava/security/cert/Certificate;)[B
+Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
Landroid/security/Credentials;->getInstance()Landroid/security/Credentials;
Landroid/security/Credentials;->install(Landroid/content/Context;Ljava/lang/String;[B)V
Landroid/security/Credentials;->install(Landroid/content/Context;Ljava/security/KeyPair;)V
@@ -5838,21 +5434,6 @@ Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V
Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;->mText:Ljava/lang/String;
Landroid/view/ActionProvider;->reset()V
Landroid/view/ActionProvider;->setSubUiVisibilityListener(Landroid/view/ActionProvider$SubUiVisibilityListener;)V
-Landroid/view/animation/Animation;->detach()V
-Landroid/view/animation/Animation;->getInvalidateRegion(IIIILandroid/graphics/RectF;Landroid/view/animation/Transformation;)V
-Landroid/view/animation/Animation;->initializeInvalidateRegion(IIII)V
-Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
-Landroid/view/animation/Animation;->mPreviousRegion:Landroid/graphics/RectF;
-Landroid/view/animation/Animation;->mPreviousTransformation:Landroid/view/animation/Transformation;
-Landroid/view/animation/Animation;->mRegion:Landroid/graphics/RectF;
-Landroid/view/animation/Animation;->mTransformation:Landroid/view/animation/Transformation;
-Landroid/view/animation/AnimationUtils;->createAnimationFromXml(Landroid/content/Context;Lorg/xmlpull/v1/XmlPullParser;Landroid/view/animation/AnimationSet;Landroid/util/AttributeSet;)Landroid/view/animation/Animation;
-Landroid/view/animation/Transformation;->printShortString(Ljava/io/PrintWriter;)V
-Landroid/view/animation/TranslateAnimation;->mFromXValue:F
-Landroid/view/animation/TranslateAnimation;->mFromYValue:F
-Landroid/view/animation/TranslateAnimation;->mToXValue:F
-Landroid/view/animation/TranslateAnimation;->mToYValue:F
-Landroid/view/animation/TranslateYAnimation;-><init>(IFIF)V
Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
Landroid/view/Choreographer$CallbackQueue;->addCallbackLocked(JLjava/lang/Object;Ljava/lang/Object;)V
@@ -6649,7 +6230,6 @@ Landroid/webkit/URLUtil;->verifyURLEncoding(Ljava/lang/String;)Z
Landroid/webkit/WebResourceResponse;->mImmutable:Z
Landroid/webkit/WebResourceResponse;->mStatusCode:I
Landroid/webkit/WebSettings$TextSize;->value:I
-Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
Landroid/webkit/WebSyncManager;->syncFromRamToFlash()V
Landroid/webkit/WebView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;IILjava/util/Map;Z)V
Landroid/webkit/WebView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;ILjava/util/Map;Z)V
@@ -7322,73 +6902,6 @@ Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyRequest(La
Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyResponse(ILandroid/telecom/VideoProfile;Landroid/telecom/VideoProfile;)V
Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
-Lcom/android/ims/internal/uce/common/CapInfo;-><init>()V
-Lcom/android/ims/internal/uce/common/CapInfo;->setCapTimestamp(J)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setCdViaPresenceSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setExts([Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtHttpSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtSnFSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtThumbSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFullSnFGroupChatSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPullFtSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPullSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPushSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setImSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIpVideoSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIpVoiceSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIsSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVideoCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVideoOnlyCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVoiceCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setSmSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setSpSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setVsDuringCSSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setVsSupported(Z)V
-Lcom/android/ims/internal/uce/common/StatusCode;-><init>()V
-Lcom/android/ims/internal/uce/common/StatusCode;->setStatusCode(I)V
-Lcom/android/ims/internal/uce/common/UceLong;->getUceLong()J
-Lcom/android/ims/internal/uce/common/UceLong;->setUceLong(J)V
-Lcom/android/ims/internal/uce/presence/PresCmdId;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresCmdId;->setCmdId(I)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/presence/PresCmdId;)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setUserData(I)V
-Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;->setPublishTrigeerType(I)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setDisplayName(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setInstanceInfo(Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setResUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setPresentityUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setReason(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setResId(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setResInstanceState(I)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setTupleInfo([Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setFullState(Z)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setListName(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setPresSubscriptionState(Lcom/android/ims/internal/uce/presence/PresSubscriptionState;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setSubscriptionExpireTime(I)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setSubscriptionTerminatedReason(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setVersion(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setCmdId(Lcom/android/ims/internal/uce/presence/PresCmdId;)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setReasonPhrase(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setRetryAfter(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setSipResponseCode(I)V
-Lcom/android/ims/internal/uce/presence/PresSubscriptionState;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresSubscriptionState;->setPresSubscriptionState(I)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setContactUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setFeatureTag(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setTimestamp(Ljava/lang/String;)V
Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
Lcom/android/internal/app/AlertController$AlertParams;-><init>(Landroid/content/Context;)V
Lcom/android/internal/app/AlertController$AlertParams;->apply(Lcom/android/internal/app/AlertController;)V
@@ -8351,6 +7864,7 @@ Lcom/android/internal/util/AsyncChannel;->sendMessageSynchronously(III)Landroid/
Lcom/android/internal/util/AsyncChannel;->sendMessageSynchronously(Landroid/os/Message;)Landroid/os/Message;
Lcom/android/internal/util/AsyncChannel;->STATUS_SUCCESSFUL:I
Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
+Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
Lcom/android/internal/util/XmlUtils;->convertValueToBoolean(Ljava/lang/CharSequence;Z)Z
Lcom/android/internal/util/XmlUtils;->convertValueToInt(Ljava/lang/CharSequence;I)I
Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
@@ -8440,6 +7954,7 @@ Lcom/android/internal/widget/IRemoteViewsFactory;->getViewTypeCount()I
Lcom/android/internal/widget/IRemoteViewsFactory;->hasStableIds()Z
Lcom/android/internal/widget/IRemoteViewsFactory;->isCreated()Z
Lcom/android/internal/widget/IRemoteViewsFactory;->onDataSetChanged()V
+Lcom/android/internal/widget/ScrollBarUtils;->getThumbLength(IIII)I
Lcom/android/internal/widget/ScrollingTabContainerView;-><init>(Landroid/content/Context;)V
Lcom/android/internal/widget/ScrollingTabContainerView;->addTab(Landroid/app/ActionBar$Tab;IZ)V
Lcom/android/internal/widget/ScrollingTabContainerView;->addTab(Landroid/app/ActionBar$Tab;Z)V
@@ -8511,6 +8026,8 @@ Lcom/android/org/conscrypt/AbstractConscryptSocket;->setHostname(Ljava/lang/Stri
Lcom/android/org/conscrypt/AbstractConscryptSocket;->setNpnProtocols([B)V
Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V
Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
+Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
+Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String;
Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String;
Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I
@@ -8980,46 +8497,6 @@ Ljava/util/zip/Inflater;->finished:Z
Ljava/util/zip/Inflater;->len:I
Ljava/util/zip/Inflater;->needDict:Z
Ljava/util/zip/Inflater;->off:I
-Ljava/util/zip/ZipConstants;->CENATT:I
-Ljava/util/zip/ZipConstants;->CENATX:I
-Ljava/util/zip/ZipConstants;->CENCOM:I
-Ljava/util/zip/ZipConstants;->CENCRC:I
-Ljava/util/zip/ZipConstants;->CENDSK:I
-Ljava/util/zip/ZipConstants;->CENEXT:I
-Ljava/util/zip/ZipConstants;->CENFLG:I
-Ljava/util/zip/ZipConstants;->CENHDR:I
-Ljava/util/zip/ZipConstants;->CENHOW:I
-Ljava/util/zip/ZipConstants;->CENLEN:I
-Ljava/util/zip/ZipConstants;->CENNAM:I
-Ljava/util/zip/ZipConstants;->CENOFF:I
-Ljava/util/zip/ZipConstants;->CENSIG:J
-Ljava/util/zip/ZipConstants;->CENSIZ:I
-Ljava/util/zip/ZipConstants;->CENTIM:I
-Ljava/util/zip/ZipConstants;->CENVEM:I
-Ljava/util/zip/ZipConstants;->CENVER:I
-Ljava/util/zip/ZipConstants;->ENDCOM:I
-Ljava/util/zip/ZipConstants;->ENDHDR:I
-Ljava/util/zip/ZipConstants;->ENDOFF:I
-Ljava/util/zip/ZipConstants;->ENDSIG:J
-Ljava/util/zip/ZipConstants;->ENDSIZ:I
-Ljava/util/zip/ZipConstants;->ENDSUB:I
-Ljava/util/zip/ZipConstants;->ENDTOT:I
-Ljava/util/zip/ZipConstants;->EXTCRC:I
-Ljava/util/zip/ZipConstants;->EXTHDR:I
-Ljava/util/zip/ZipConstants;->EXTLEN:I
-Ljava/util/zip/ZipConstants;->EXTSIG:J
-Ljava/util/zip/ZipConstants;->EXTSIZ:I
-Ljava/util/zip/ZipConstants;->LOCCRC:I
-Ljava/util/zip/ZipConstants;->LOCEXT:I
-Ljava/util/zip/ZipConstants;->LOCFLG:I
-Ljava/util/zip/ZipConstants;->LOCHDR:I
-Ljava/util/zip/ZipConstants;->LOCHOW:I
-Ljava/util/zip/ZipConstants;->LOCLEN:I
-Ljava/util/zip/ZipConstants;->LOCNAM:I
-Ljava/util/zip/ZipConstants;->LOCSIG:J
-Ljava/util/zip/ZipConstants;->LOCSIZ:I
-Ljava/util/zip/ZipConstants;->LOCTIM:I
-Ljava/util/zip/ZipConstants;->LOCVER:I
Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V
Ljava/util/zip/ZipEntry;->method:I
Ljava/util/zip/ZipFile;->close(J)V
@@ -9034,6 +8511,16 @@ Ljavax/microedition/khronos/egl/EGL10;->eglReleaseThread()Z
Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
+Llibcore/io/Memory;->peekByte(J)B
+Llibcore/io/Memory;->peekByteArray(J[BII)V
+Llibcore/io/Memory;->peekInt(JZ)I
+Llibcore/io/Memory;->peekLong(JZ)J
+Llibcore/io/Memory;->pokeByte(JB)V
+Llibcore/io/Memory;->pokeByteArray(J[BII)V
+Llibcore/io/Memory;->pokeInt(JIZ)V
+Llibcore/io/Memory;->pokeLong(JJZ)V
+Llibcore/io/Streams;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)I
Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
Llibcore/util/ZoneInfo;->mTransitions:[J
Lorg/apache/http/conn/ssl/AbstractVerifier;->BAD_COUNTRY_2LDS:[Ljava/lang/String;
@@ -9056,6 +8543,8 @@ Lorg/ccil/cowan/tagsoup/ElementType;->theName:Ljava/lang/String;
Lorg/ccil/cowan/tagsoup/ElementType;->theNamespace:Ljava/lang/String;
Lorg/ccil/cowan/tagsoup/ElementType;->theParent:Lorg/ccil/cowan/tagsoup/ElementType;
Lorg/ccil/cowan/tagsoup/ElementType;->theSchema:Lorg/ccil/cowan/tagsoup/Schema;
+Lorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+Lorg/ccil/cowan/tagsoup/Parser;-><init>()V
Lorg/ccil/cowan/tagsoup/Schema;->theElementTypes:Ljava/util/HashMap;
Lorg/ccil/cowan/tagsoup/Schema;->theEntities:Ljava/util/HashMap;
Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
@@ -9155,10 +8644,84 @@ Lorg/xml/sax/SAXParseException;->init(Ljava/lang/String;Ljava/lang/String;II)V
Lorg/xml/sax/SAXParseException;->lineNumber:I
Lorg/xml/sax/SAXParseException;->publicId:Ljava/lang/String;
Lorg/xml/sax/SAXParseException;->systemId:Ljava/lang/String;
+Lsun/misc/Cleaner;->clean()V
+Lsun/misc/Unsafe;->addressSize()I
+Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
+Lsun/misc/Unsafe;->allocateMemory(J)J
+Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->compareAndSwapInt(Ljava/lang/Object;JII)Z
+Lsun/misc/Unsafe;->compareAndSwapLong(Ljava/lang/Object;JJJ)Z
+Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
+Lsun/misc/Unsafe;->copyMemory(JJJ)V
+Lsun/misc/Unsafe;->copyMemoryFromPrimitiveArray(Ljava/lang/Object;JJJ)V
+Lsun/misc/Unsafe;->copyMemoryToPrimitiveArray(JLjava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->freeMemory(J)V
+Lsun/misc/Unsafe;->fullFence()V
+Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I
+Lsun/misc/Unsafe;->getAndAddLong(Ljava/lang/Object;JJ)J
+Lsun/misc/Unsafe;->getAndSetInt(Ljava/lang/Object;JI)I
+Lsun/misc/Unsafe;->getAndSetLong(Ljava/lang/Object;JJ)J
+Lsun/misc/Unsafe;->getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getArrayBaseOffsetForComponentType(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->getArrayIndexScaleForComponentType(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->getBoolean(Ljava/lang/Object;J)Z
+Lsun/misc/Unsafe;->getByte(J)B
+Lsun/misc/Unsafe;->getByte(Ljava/lang/Object;J)B
+Lsun/misc/Unsafe;->getChar(J)C
+Lsun/misc/Unsafe;->getChar(Ljava/lang/Object;J)C
+Lsun/misc/Unsafe;->getDouble(J)D
+Lsun/misc/Unsafe;->getDouble(Ljava/lang/Object;J)D
+Lsun/misc/Unsafe;->getFloat(J)F
+Lsun/misc/Unsafe;->getFloat(Ljava/lang/Object;J)F
+Lsun/misc/Unsafe;->getInt(J)I
+Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I
+Lsun/misc/Unsafe;->getIntVolatile(Ljava/lang/Object;J)I
+Lsun/misc/Unsafe;->getLong(J)J
+Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J
+Lsun/misc/Unsafe;->getLongVolatile(Ljava/lang/Object;J)J
+Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getShort(J)S
+Lsun/misc/Unsafe;->getShort(Ljava/lang/Object;J)S
+Lsun/misc/Unsafe;->getUnsafe()Lsun/misc/Unsafe;
+Lsun/misc/Unsafe;->INVALID_FIELD_OFFSET:I
+Lsun/misc/Unsafe;->loadFence()V
+Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J
+Lsun/misc/Unsafe;->pageSize()I
+Lsun/misc/Unsafe;->park(ZJ)V
+Lsun/misc/Unsafe;->putBoolean(Ljava/lang/Object;JZ)V
+Lsun/misc/Unsafe;->putByte(JB)V
+Lsun/misc/Unsafe;->putByte(Ljava/lang/Object;JB)V
+Lsun/misc/Unsafe;->putChar(JC)V
+Lsun/misc/Unsafe;->putChar(Ljava/lang/Object;JC)V
+Lsun/misc/Unsafe;->putDouble(JD)V
+Lsun/misc/Unsafe;->putDouble(Ljava/lang/Object;JD)V
+Lsun/misc/Unsafe;->putFloat(JF)V
+Lsun/misc/Unsafe;->putFloat(Ljava/lang/Object;JF)V
+Lsun/misc/Unsafe;->putInt(JI)V
+Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putIntVolatile(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putLong(JJ)V
+Lsun/misc/Unsafe;->putLong(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putLongVolatile(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putOrderedInt(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putOrderedLong(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putShort(JS)V
+Lsun/misc/Unsafe;->putShort(Ljava/lang/Object;JS)V
+Lsun/misc/Unsafe;->setMemory(JJB)V
+Lsun/misc/Unsafe;->storeFence()V
Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
Lsun/misc/Unsafe;->THE_ONE:Lsun/misc/Unsafe;
+Lsun/misc/Unsafe;->unpark(Ljava/lang/Object;)V
Lsun/misc/URLClassPath$JarLoader;->getJarFile()Ljava/util/jar/JarFile;
Lsun/misc/URLClassPath;->lmap:Ljava/util/HashMap;
Lsun/misc/URLClassPath;->loaders:Ljava/util/ArrayList;
Lsun/misc/URLClassPath;->urls:Ljava/util/Stack;
+Lsun/nio/ch/DirectBuffer;->cleaner()Lsun/misc/Cleaner;
+Lsun/security/x509/AlgorithmId;->get(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/AlgorithmId;->getName()Ljava/lang/String;
Lsun/security/x509/AVA;->hasRFC2253Keyword()Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index f1e700bf356b..b8c1468da793 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -93,10 +93,6 @@ Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landro
Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
Landroid/database/sqlite/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
Landroid/database/sqlite/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
-Landroid/graphics/Bitmap;->createGraphicBufferHandle()Landroid/graphics/GraphicBuffer;
-Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
-Landroid/graphics/drawable/Drawable;->isProjected()Z
-Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
Landroid/hardware/display/DisplayManagerGlobal;->getInstance()Landroid/hardware/display/DisplayManagerGlobal;
Landroid/hardware/display/DisplayManagerGlobal;->getRealDisplay(I)Landroid/view/Display;
@@ -609,31 +605,6 @@ Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/i
Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/common/CapInfo;->getCapTimestamp()J
-Lcom/android/ims/internal/uce/common/CapInfo;->isCdViaPresenceSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtHttpSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtSnFSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtThumbSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFullSnFGroupChatSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullFtSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPushSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isImSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIpVideoSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIpVoiceSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIsSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoOnlyCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVoiceCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isSmSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isSpSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isVsDuringCSSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isVsSupported()Z
-Lcom/android/ims/internal/uce/common/StatusCode;->getStatusCode()I
-Lcom/android/ims/internal/uce/common/UceLong;-><init>()V
-Lcom/android/ims/internal/uce/common/UceLong;->getClientId()I
-Lcom/android/ims/internal/uce/common/UceLong;->setClientId(I)V
Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
@@ -649,24 +620,6 @@ Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/androi
Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getSdp()Ljava/lang/String;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setSdp(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdId;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCmdId;->setCmdId(I)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setUserData(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setReasonPhrase(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRequestId(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRetryAfter(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setSipResponseCode(I)V
Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
@@ -685,18 +638,6 @@ Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/and
Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->getContactUri()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->mContactUri:Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getMediaType()I
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceDesc()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceId()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceVer()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getCmdId()Lcom/android/ims/internal/uce/presence/PresCmdId;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getReasonPhrase()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRequestId()I
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRetryAfter()I
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getSipResponseCode()I
Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 300a530970c9..5b73eaa9cd17 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -442,8 +443,10 @@ public abstract class AccessibilityService extends Service {
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
+ @UnsupportedAppUsage
private AccessibilityServiceInfo mInfo;
+ @UnsupportedAppUsage
private IBinder mWindowToken;
private WindowManager mWindowManager;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index ed684d7a5901..be2e2faf3739 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -19,6 +19,7 @@ package android.accessibilityservice;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import android.annotation.IntDef;
+import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -695,6 +696,7 @@ public class AccessibilityServiceInfo implements Parcelable {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setCapabilities(int capabilities) {
mCapabilities = capabilities;
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4ebcc446e5a2..17d54d2455fe 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -17,6 +17,7 @@
package android.animation;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ConstantState;
@@ -460,6 +461,7 @@ public abstract class Animator implements Cloneable {
/**
* @hide
*/
+ @UnsupportedAppUsage
public void reverse() {
throw new IllegalStateException("Reverse is not supported");
}
diff --git a/core/java/android/animation/ArgbEvaluator.java b/core/java/android/animation/ArgbEvaluator.java
index a96bee6ac203..5b69d18a8386 100644
--- a/core/java/android/animation/ArgbEvaluator.java
+++ b/core/java/android/animation/ArgbEvaluator.java
@@ -16,6 +16,8 @@
package android.animation;
+import android.annotation.UnsupportedAppUsage;
+
/**
* This evaluator can be used to perform type interpolation between integer
* values that represent ARGB colors.
@@ -31,6 +33,7 @@ public class ArgbEvaluator implements TypeEvaluator {
*
* @hide
*/
+ @UnsupportedAppUsage
public static ArgbEvaluator getInstance() {
return sInstance;
}
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5a23fddf1d51..5b3813d9c3af 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -16,6 +16,7 @@
package android.animation;
+import android.annotation.UnsupportedAppUsage;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
@@ -1070,6 +1071,7 @@ public class LayoutTransition {
*
* @hide
*/
+ @UnsupportedAppUsage
public void cancel() {
if (currentChangingAnimations.size() > 0) {
LinkedHashMap<View, Animator> currentAnimCopy =
@@ -1105,6 +1107,7 @@ public class LayoutTransition {
*
* @hide
*/
+ @UnsupportedAppUsage
public void cancel(int transitionType) {
switch (transitionType) {
case CHANGE_APPEARING:
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index cc95eb6f4ea2..a0464dfac1f4 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -19,6 +19,7 @@ package android.animation;
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
@@ -75,6 +76,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
/**
* Internal constants
*/
+ @UnsupportedAppUsage
private static float sDurationScale = 1.0f;
/**
@@ -200,6 +202,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
//
// How long the animation should last in ms
+ @UnsupportedAppUsage
private long mDuration = 300;
// The amount of time in ms to delay starting the animation after start() is called. Note
@@ -1534,6 +1537,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* @param fraction The elapsed fraction of the animation.
*/
@CallSuper
+ @UnsupportedAppUsage
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 3aeef148b4e7..4517446c3068 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -16,6 +16,7 @@
package android.app;
+import android.app.backup.IBackupCallback;
import android.app.backup.IBackupManager;
import android.os.ParcelFileDescriptor;
@@ -55,7 +56,7 @@ oneway interface IBackupAgent {
void doBackup(in ParcelFileDescriptor oldState,
in ParcelFileDescriptor data,
in ParcelFileDescriptor newState,
- long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags);
+ long quotaBytes, IBackupCallback callbackBinder, int transportFlags);
/**
* Restore an entire data snapshot to the application.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index ec2cf0cf0dae..097dd9c00aca 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -941,11 +941,13 @@ public abstract class BackupAgent extends ContextWrapper {
private static final String TAG = "BackupServiceBinder";
@Override
- public void doBackup(ParcelFileDescriptor oldState,
+ public void doBackup(
+ ParcelFileDescriptor oldState,
ParcelFileDescriptor data,
ParcelFileDescriptor newState,
- long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags)
- throws RemoteException {
+ long quotaBytes,
+ IBackupCallback callbackBinder,
+ int transportFlags) throws RemoteException {
// Ensure that we're running with the app's normal permission level
long ident = Binder.clearCallingIdentity();
@@ -969,7 +971,7 @@ public abstract class BackupAgent extends ContextWrapper {
Binder.restoreCallingIdentity(ident);
try {
- callbackBinder.opComplete(token, 0);
+ callbackBinder.operationComplete(0);
} catch (RemoteException e) {
// we'll time out anyway, so we're safe
}
diff --git a/core/java/android/app/backup/IBackupCallback.aidl b/core/java/android/app/backup/IBackupCallback.aidl
new file mode 100644
index 000000000000..9582a58fa594
--- /dev/null
+++ b/core/java/android/app/backup/IBackupCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+import android.app.backup.IBackupManager;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Callback interface made for responding to one-way calls from the system.
+ *
+ * @hide
+ */
+oneway interface IBackupCallback {
+ void operationComplete(long result);
+}
diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java
index 7e39e47cb952..956078772ca8 100644
--- a/core/java/android/ddm/DdmHandleAppName.java
+++ b/core/java/android/ddm/DdmHandleAppName.java
@@ -16,6 +16,7 @@
package android.ddm;
+import android.annotation.UnsupportedAppUsage;
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
@@ -69,6 +70,7 @@ public class DdmHandleAppName extends ChunkHandler {
* before or after DDMS connects. For the latter we need to send up
* an APNM message.
*/
+ @UnsupportedAppUsage
public static void setAppName(String name, int userId) {
if (name == null || name.length() == 0)
return;
@@ -79,6 +81,7 @@ public class DdmHandleAppName extends ChunkHandler {
sendAPNM(name, userId);
}
+ @UnsupportedAppUsage
public static String getAppName() {
return mAppName;
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 431c6511b1f3..46671b237203 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -28,6 +28,7 @@ import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
@@ -343,10 +344,12 @@ public class InputMethodService extends AbstractInputMethodService {
InputMethodManager mImm;
+ @UnsupportedAppUsage
int mTheme = 0;
LayoutInflater mInflater;
TypedArray mThemeAttrs;
+ @UnsupportedAppUsage
View mRootView;
SoftInputWindow mWindow;
boolean mInitialized;
@@ -378,8 +381,10 @@ public class InputMethodService extends AbstractInputMethodService {
boolean mFullscreenApplied;
boolean mIsFullscreen;
+ @UnsupportedAppUsage
View mExtractView;
boolean mExtractViewHidden;
+ @UnsupportedAppUsage
ExtractEditText mExtractEditText;
ViewGroup mExtractAccessories;
View mExtractAction;
@@ -402,6 +407,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
boolean mShouldClearInsetOfPreviousIme;
+ @UnsupportedAppUsage
final Insets mTmpInsets = new Insets();
final int[] mTmpLocation = new int[2];
@@ -811,6 +817,7 @@ public class InputMethodService extends AbstractInputMethodService {
mService.getContentResolver().unregisterContentObserver(this);
}
+ @UnsupportedAppUsage
private boolean shouldShowImeWithHardKeyboard() {
// Lazily initialize as needed.
if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
@@ -850,6 +857,7 @@ public class InputMethodService extends AbstractInputMethodService {
return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard + "}";
}
}
+ @UnsupportedAppUsage
private SettingsObserver mSettingsObserver;
/**
@@ -2492,6 +2500,7 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* @hide
*/
+ @UnsupportedAppUsage
public void onExtractedDeleteText(int start, int end) {
InputConnection conn = getCurrentInputConnection();
if (conn != null) {
@@ -2504,6 +2513,7 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* @hide
*/
+ @UnsupportedAppUsage
public void onExtractedReplaceText(int start, int end, CharSequence text) {
InputConnection conn = getCurrentInputConnection();
if (conn != null) {
@@ -2515,6 +2525,7 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* @hide
*/
+ @UnsupportedAppUsage
public void onExtractedSetSpan(Object span, int start, int end, int flags) {
InputConnection conn = getCurrentInputConnection();
if (conn != null) {
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index a5490eff0899..ec5f05067120 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -18,6 +18,7 @@ package android.inputmethodservice;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.UnsupportedAppUsage;
import android.annotation.XmlRes;
import android.content.Context;
import android.content.res.Resources;
@@ -110,18 +111,21 @@ public class Keyboard {
private int mKeyHeight;
/** Total height of the keyboard, including the padding and keys */
+ @UnsupportedAppUsage
private int mTotalHeight;
/**
* Total width of the keyboard, including left side gaps and keys, but not any gaps on the
* right side.
*/
+ @UnsupportedAppUsage
private int mTotalWidth;
/** List of keys in this keyboard */
private List<Key> mKeys;
/** List of modifier keys such as Shift & Alt, if any */
+ @UnsupportedAppUsage
private List<Key> mModifierKeys;
/** Width of the screen available to fit the keyboard */
@@ -623,6 +627,7 @@ public class Keyboard {
rows.add(row);
}
+ @UnsupportedAppUsage
final void resize(int newWidth, int newHeight) {
int numRows = rows.size();
for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 16c1f6d59ca3..9ca804975b7a 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -16,6 +16,7 @@
package android.inputmethodservice;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -133,6 +134,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private Keyboard mKeyboard;
private int mCurrentKeyIndex = NOT_A_KEY;
+ @UnsupportedAppUsage
private int mLabelTextSize;
private int mKeyTextSize;
private int mKeyTextColor;
@@ -140,6 +142,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private int mShadowColor;
private float mBackgroundDimAmount;
+ @UnsupportedAppUsage
private TextView mPreviewText;
private PopupWindow mPreviewPopup;
private int mPreviewTextSizeLarge;
@@ -217,6 +220,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private float mOldPointerX;
private float mOldPointerY;
+ @UnsupportedAppUsage
private Drawable mKeyBackground;
private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
@@ -910,6 +914,7 @@ public class KeyboardView extends View implements View.OnClickListener {
}
}
+ @UnsupportedAppUsage
private void showKey(final int keyIndex) {
final PopupWindow previewPopup = mPreviewPopup;
final Key[] keys = mKeys;
@@ -1052,6 +1057,7 @@ public class KeyboardView extends View implements View.OnClickListener {
key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
}
+ @UnsupportedAppUsage
private boolean openPopupIfRequired(MotionEvent me) {
// Check if we have a popup layout specified first.
if (mPopupLayout == 0) {
@@ -1357,6 +1363,7 @@ public class KeyboardView extends View implements View.OnClickListener {
return true;
}
+ @UnsupportedAppUsage
private boolean repeatKey() {
Key key = mKeys[mRepeatKeyIndex];
detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 3adcdc378b77..98e31ad53be3 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -16,6 +16,8 @@
package android.nfc;
+import android.annotation.UnsupportedAppUsage;
+
/**
* This class defines all the error codes that can be returned by the service
* and producing an exception on the application level. These are needed since
@@ -25,6 +27,7 @@ package android.nfc;
*/
public class ErrorCodes {
+ @UnsupportedAppUsage
public static boolean isError(int code) {
if (code < 0) {
return true;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 093a9b469cb8..b0090ca6bc32 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
@@ -279,6 +280,7 @@ public final class NdefRecord implements Parcelable {
private final short mTnf;
private final byte[] mType;
+ @UnsupportedAppUsage
private final byte[] mId;
private final byte[] mPayload;
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 958063ae0a9f..abfa133a2f39 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.Application;
import android.content.ContentProvider;
@@ -45,6 +46,7 @@ public final class NfcActivityManager extends IAppCallback.Stub
static final String TAG = NfcAdapter.TAG;
static final Boolean DBG = false;
+ @UnsupportedAppUsage
final NfcAdapter mAdapter;
// All objects in the lists are protected by this
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c3f23a1cca9f..21fed48189eb 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -20,6 +20,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.OnActivityPausedListener;
@@ -325,6 +326,7 @@ public final class NfcAdapter {
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
// recovery
+ @UnsupportedAppUsage
static INfcAdapter sService;
static INfcTag sTagService;
static INfcCardEmulation sCardEmulationService;
@@ -490,6 +492,7 @@ public final class NfcAdapter {
* or throws if NFC is not available.
* @hide
*/
+ @UnsupportedAppUsage
public static synchronized NfcAdapter getNfcAdapter(Context context) {
if (!sIsInitialized) {
sHasNfcFeature = hasNfcFeature();
@@ -593,6 +596,7 @@ public final class NfcAdapter {
* @hide
*/
@Deprecated
+ @UnsupportedAppUsage
public static NfcAdapter getDefaultAdapter() {
// introduced in API version 9 (GB 2.3)
// deprecated in API version 10 (GB 2.3.3)
@@ -615,6 +619,7 @@ public final class NfcAdapter {
/**
* @hide
*/
+ @UnsupportedAppUsage
public Context getContext() {
return mContext;
}
@@ -623,6 +628,7 @@ public final class NfcAdapter {
* Returns the binder interface to the service.
* @hide
*/
+ @UnsupportedAppUsage
public INfcAdapter getService() {
isEnabled(); // NOP call to recover sService if it is stale
return sService;
@@ -676,6 +682,7 @@ public final class NfcAdapter {
* NFC service dead - attempt best effort recovery
* @hide
*/
+ @UnsupportedAppUsage
public void attemptDeadServiceRecovery(Exception e) {
Log.e(TAG, "NFC service dead - attempting to recover", e);
INfcAdapter service = getServiceInterface();
@@ -746,6 +753,7 @@ public final class NfcAdapter {
*
* @hide
*/
+ @UnsupportedAppUsage
public int getAdapterState() {
try {
return sService.getState();
@@ -1227,6 +1235,7 @@ public final class NfcAdapter {
/**
* @hide
*/
+ @UnsupportedAppUsage
public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
int flags) {
if (activity == null) {
@@ -1862,6 +1871,7 @@ public final class NfcAdapter {
/**
* @hide
*/
+ @UnsupportedAppUsage
public INfcAdapterExtras getNfcAdapterExtrasInterface() {
if (mContext == null) {
throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 50d674570c14..71199c9c6257 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -17,6 +17,7 @@
package android.nfc;
import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
/**
@@ -44,6 +45,7 @@ public final class NfcManager {
/**
* @hide
*/
+ @UnsupportedAppUsage
public NfcManager(Context context) {
NfcAdapter adapter;
context = context.getApplicationContext();
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 154d5a11db9e..ce684cfea180 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.nfc.tech.IsoDep;
import android.nfc.tech.MifareClassic;
@@ -110,6 +111,7 @@ import java.util.HashMap;
* <p>
*/
public final class Tag implements Parcelable {
+ @UnsupportedAppUsage
final byte[] mId;
final int[] mTechList;
final String[] mTechStringList;
@@ -235,6 +237,7 @@ public final class Tag implements Parcelable {
* For use by NfcService only.
* @hide
*/
+ @UnsupportedAppUsage
public int getServiceHandle() {
return mServiceHandle;
}
@@ -355,6 +358,7 @@ public final class Tag implements Parcelable {
}
/** @hide */
+ @UnsupportedAppUsage
public INfcTag getTagService() {
return mTagService;
}
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 78a9401a2abd..63776c4f397a 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -45,8 +46,11 @@ public final class AidGroup implements Parcelable {
static final String TAG = "AidGroup";
+ @UnsupportedAppUsage
final List<String> aids;
+ @UnsupportedAppUsage
final String category;
+ @UnsupportedAppUsage
final String description;
/**
@@ -79,6 +83,7 @@ public final class AidGroup implements Parcelable {
this.description = null;
}
+ @UnsupportedAppUsage
AidGroup(String category, String description) {
this.aids = new ArrayList<String>();
this.category = category;
@@ -88,6 +93,7 @@ public final class AidGroup implements Parcelable {
/**
* @return the category of this AID group
*/
+ @UnsupportedAppUsage
public String getCategory() {
return category;
}
@@ -95,6 +101,7 @@ public final class AidGroup implements Parcelable {
/**
* @return the list of AIDs in this group
*/
+ @UnsupportedAppUsage
public List<String> getAids() {
return aids;
}
@@ -124,6 +131,7 @@ public final class AidGroup implements Parcelable {
}
}
+ @UnsupportedAppUsage
public static final Parcelable.Creator<AidGroup> CREATOR =
new Parcelable.Creator<AidGroup>() {
@@ -144,6 +152,7 @@ public final class AidGroup implements Parcelable {
}
};
+ @UnsupportedAppUsage
static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
String category = null;
ArrayList<String> aids = new ArrayList<String>();
@@ -185,6 +194,7 @@ public final class AidGroup implements Parcelable {
return group;
}
+ @UnsupportedAppUsage
public void writeAsXml(XmlSerializer out) throws IOException {
out.startTag(null, "aid-group");
out.attribute(null, "category", category);
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 218e4f223549..e8d801c525e9 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -16,6 +16,7 @@
package android.nfc.cardemulation;
+import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -54,6 +55,7 @@ public final class ApduServiceInfo implements Parcelable {
/**
* The service that implements this
*/
+ @UnsupportedAppUsage
final ResolveInfo mService;
/**
@@ -69,11 +71,13 @@ public final class ApduServiceInfo implements Parcelable {
/**
* Mapping from category to static AID group
*/
+ @UnsupportedAppUsage
final HashMap<String, AidGroup> mStaticAidGroups;
/**
* Mapping from category to dynamic AID group
*/
+ @UnsupportedAppUsage
final HashMap<String, AidGroup> mDynamicAidGroups;
/**
@@ -99,6 +103,7 @@ public final class ApduServiceInfo implements Parcelable {
/**
* @hide
*/
+ @UnsupportedAppUsage
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
@@ -120,6 +125,7 @@ public final class ApduServiceInfo implements Parcelable {
this.mSettingsActivityName = settingsActivityName;
}
+ @UnsupportedAppUsage
public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
XmlPullParserException, IOException {
ServiceInfo si = info.serviceInfo;
@@ -374,18 +380,22 @@ public final class ApduServiceInfo implements Parcelable {
return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
}
+ @UnsupportedAppUsage
public boolean isOnHost() {
return mOnHost;
}
+ @UnsupportedAppUsage
public boolean requiresUnlock() {
return mRequiresDeviceUnlock;
}
+ @UnsupportedAppUsage
public String getDescription() {
return mDescription;
}
+ @UnsupportedAppUsage
public int getUid() {
return mUid;
}
@@ -411,6 +421,7 @@ public final class ApduServiceInfo implements Parcelable {
return mService.loadIcon(pm);
}
+ @UnsupportedAppUsage
public Drawable loadBanner(PackageManager pm) {
Resources res;
try {
@@ -426,6 +437,7 @@ public final class ApduServiceInfo implements Parcelable {
}
}
+ @UnsupportedAppUsage
public String getSettingsActivityName() { return mSettingsActivityName; }
@Override
@@ -483,6 +495,7 @@ public final class ApduServiceInfo implements Parcelable {
dest.writeString(mSettingsActivityName);
};
+ @UnsupportedAppUsage
public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
new Parcelable.Creator<ApduServiceInfo>() {
@Override
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f4f43e327222..6cf67de2303c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9217,6 +9217,19 @@ public final class Settings {
/** {@hide} */
public static final String STORAGE_BENCHMARK_INTERVAL = "storage_benchmark_interval";
+ /**
+ * Whether or not Settings should enable psd API.
+ * {@hide}
+ */
+ public static final String SETTINGS_USE_PSD_API = "settings_use_psd_api";
+
+ /**
+ * Whether or not Settings should enable external provider API.
+ * {@hide}
+ */
+ public static final String SETTINGS_USE_EXTERNAL_PROVIDER_API =
+ "settings_use_external_provider_api";
+
/**
* Sample validity in seconds to configure for the system DNS resolver.
* {@hide}
@@ -9824,6 +9837,18 @@ public final class Settings {
"recommended_network_evaluator_cache_expiry_ms";
/**
+ * Whether wifi scan throttle is enabled or not.
+ * This is intended to be used via adb commands or a menu in developer option to turn off
+ * the default wifi scan throttling mechanism for apps.
+ *
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
+
+ private static final Validator WIFI_SCAN_THROTTLE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
* connectivity.
* @hide
@@ -12315,6 +12340,7 @@ public final class Settings {
VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
+ VALIDATORS.put(WIFI_SCAN_THROTTLE_ENABLED, WIFI_SCAN_THROTTLE_ENABLED_VALIDATOR);
VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR);
VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
VALIDATORS.put(CHARGING_VIBRATION_ENABLED, CHARGING_VIBRATION_ENABLED_VALIDATOR);
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1cd76d2e9ec9..e5e1c926fbcd 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -65,16 +65,36 @@ public abstract class AutofillFieldClassificationService extends Service {
/**
* Manifest metadata key for the resource string containing the name of the default field
* classification algorithm.
+ *
+ * @deprecated Use {@link #RESOURCE_DEFAULT_ALGORITHM} instead.
*/
+ @Deprecated
public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM =
"android.autofill.field_classification.default_algorithm";
+
/**
* Manifest metadata key for the resource string array containing the names of all field
* classification algorithms provided by the service.
+ *
+ * @deprecated Use {@link #RESOURCE_AVAILABLE_ALGORITHMS} instead.
*/
+ @Deprecated
public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
"android.autofill.field_classification.available_algorithms";
+ /**
+ * Name of the resource string containing the name of the default field
+ * classification algorithm.
+ */
+ public static final String RESOURCE_DEFAULT_ALGORITHM =
+ "autofill_field_classification_default_algorithm";
+
+ /**
+ * Name of the resource string array containing the names of all field
+ * classification algorithms provided by the service.
+ */
+ public static final String RESOURCE_AVAILABLE_ALGORITHMS =
+ "autofill_field_classification_available_algorithms";
/** {@hide} **/
public static final String EXTRA_SCORES = "scores";
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 0d94af4bc828..e0c354ac0f23 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -71,6 +71,12 @@ public final class Adjustment implements Parcelable {
public static final String KEY_SMART_ACTIONS = "key_smart_actions";
/**
+ * Data type: ArrayList of {@link CharSequence}.
+ * Used to suggest smart replies for a notification.
+ */
+ public static final String KEY_SMART_REPLIES = "key_smart_replies";
+
+ /**
* Create a notification adjustment.
*
* @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 09425a9c5d28..09eecd8e2868 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1427,6 +1427,7 @@ public abstract class NotificationListenerService extends Service {
private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
private boolean mHidden;
private ArrayList<Notification.Action> mSmartActions;
+ private ArrayList<CharSequence> mSmartReplies;
public Ranking() {}
@@ -1564,6 +1565,13 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * @hide
+ */
+ public List<CharSequence> getSmartReplies() {
+ return mSmartReplies;
+ }
+
+ /**
* Returns whether this notification can be displayed as a badge.
*
* @return true if the notification can be displayed as a badge, false otherwise.
@@ -1591,7 +1599,8 @@ public abstract class NotificationListenerService extends Service {
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
- int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions) {
+ int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions,
+ ArrayList<CharSequence> smartReplies) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1608,6 +1617,7 @@ public abstract class NotificationListenerService extends Service {
mUserSentiment = userSentiment;
mHidden = hidden;
mSmartActions = smartActions;
+ mSmartReplies = smartReplies;
}
/**
@@ -1658,6 +1668,7 @@ public abstract class NotificationListenerService extends Service {
private ArrayMap<String, Integer> mUserSentiment;
private ArrayMap<String, Boolean> mHidden;
private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
+ private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
@@ -1686,7 +1697,8 @@ public abstract class NotificationListenerService extends Service {
getVisibilityOverride(key), getSuppressedVisualEffects(key),
getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
- getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key));
+ getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key),
+ getSmartReplies(key));
return rank >= 0;
}
@@ -1833,6 +1845,15 @@ public abstract class NotificationListenerService extends Service {
return mSmartActions.get(key);
}
+ private ArrayList<CharSequence> getSmartReplies(String key) {
+ synchronized (this) {
+ if (mSmartReplies == null) {
+ buildSmartReplies();
+ }
+ }
+ return mSmartReplies.get(key);
+ }
+
// Locked by 'this'
private void buildRanksLocked() {
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1959,6 +1980,15 @@ public abstract class NotificationListenerService extends Service {
}
}
+ // Locked by 'this'
+ private void buildSmartReplies() {
+ Bundle smartReplies = mRankingUpdate.getSmartReplies();
+ mSmartReplies = new ArrayMap<>(smartReplies.size());
+ for (String key : smartReplies.keySet()) {
+ mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
+ }
+ }
+
// ----------- Parcelable
@Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index bed221494d4e..c67fad01d6cd 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -38,12 +38,14 @@ public class NotificationRankingUpdate implements Parcelable {
private final Bundle mUserSentiment;
private final Bundle mHidden;
private final Bundle mSmartActions;
+ private final Bundle mSmartReplies;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
Bundle visibilityOverrides, Bundle suppressedVisualEffects,
int[] importance, Bundle explanation, Bundle overrideGroupKeys,
Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
- Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions) {
+ Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
+ Bundle smartReplies) {
mKeys = keys;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
@@ -58,6 +60,7 @@ public class NotificationRankingUpdate implements Parcelable {
mUserSentiment = userSentiment;
mHidden = hidden;
mSmartActions = smartActions;
+ mSmartReplies = smartReplies;
}
public NotificationRankingUpdate(Parcel in) {
@@ -76,6 +79,7 @@ public class NotificationRankingUpdate implements Parcelable {
mUserSentiment = in.readBundle();
mHidden = in.readBundle();
mSmartActions = in.readBundle();
+ mSmartReplies = in.readBundle();
}
@Override
@@ -99,6 +103,7 @@ public class NotificationRankingUpdate implements Parcelable {
out.writeBundle(mUserSentiment);
out.writeBundle(mHidden);
out.writeBundle(mSmartActions);
+ out.writeBundle(mSmartReplies);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -167,4 +172,8 @@ public class NotificationRankingUpdate implements Parcelable {
public Bundle getSmartActions() {
return mSmartActions;
}
+
+ public Bundle getSmartReplies() {
+ return mSmartReplies;
+ }
}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 2499afb7e8f3..1b063e16ed59 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -380,8 +380,8 @@ public final class Log {
/** @hide */ public static final int LOG_ID_SYSTEM = 3;
/** @hide */ public static final int LOG_ID_CRASH = 4;
- /** @hide */ public static native int println_native(int bufID,
- int priority, String tag, String msg);
+ /** @hide */
+ public static native int println_native(int bufID, int priority, String tag, String msg);
/**
* Return the maximum payload the log daemon accepts without truncation.
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index b3400ef538b8..af18caa0e122 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -22,32 +22,34 @@ import com.android.internal.util.GrowingArrayUtils;
import libcore.util.EmptyArray;
/**
- * SparseArrays map integers to Objects. Unlike a normal array of Objects,
- * there can be gaps in the indices. It is intended to be more memory efficient
- * than using a HashMap to map Integers to Objects, both because it avoids
+ * <code>SparseArray</code> maps integers to Objects and, unlike a normal array of Objects,
+ * its indices can contain gaps. <code>SparseArray</code> is intended to be more memory-efficient
+ * than a
+ * <a href="/reference/java/util/HashMap"><code>HashMap</code></a>, because it avoids
* auto-boxing keys and its data structure doesn't rely on an extra entry object
* for each mapping.
*
* <p>Note that this container keeps its mappings in an array data structure,
- * using a binary search to find keys. The implementation is not intended to be appropriate for
+ * using a binary search to find keys. The implementation is not intended to be appropriate for
* data structures
- * that may contain large numbers of items. It is generally slower than a traditional
- * HashMap, since lookups require a binary search and adds and removes require inserting
- * and deleting entries in the array. For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.</p>
+ * that may contain large numbers of items. It is generally slower than a
+ * <code>HashMap</code> because lookups require a binary search,
+ * and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is less than 50%.
*
* <p>To help with performance, the container includes an optimization when removing
* keys: instead of compacting its array immediately, it leaves the removed entry marked
- * as deleted. The entry can then be re-used for the same key, or compacted later in
- * a single garbage collection step of all removed entries. This garbage collection will
- * need to be performed at any time the array needs to be grown or the the map size or
- * entry values are retrieved.</p>
+ * as deleted. The entry can then be re-used for the same key or compacted later in
+ * a single garbage collection of all removed entries. This garbage collection
+ * must be performed whenever the array needs to be grown, or when the map size or
+ * entry values are retrieved.
*
* <p>It is possible to iterate over the items in this container using
* {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
- * <code>keyAt(int)</code> with ascending values of the index will return the
- * keys in ascending order, or the values corresponding to the keys in ascending
- * order in the case of <code>valueAt(int)</code>.</p>
+ * <code>keyAt(int)</code> with ascending values of the index returns the
+ * keys in ascending order. In the case of <code>valueAt(int)</code>, the
+ * values corresponding to the keys are returned in ascending order.
*/
public class SparseArray<E> implements Cloneable {
private static final Object DELETED = new Object();
@@ -333,7 +335,7 @@ public class SparseArray<E> implements Cloneable {
/**
* Returns an index for which {@link #valueAt} would return the
- * specified key, or a negative number if no keys map to the
+ * specified value, or a negative number if no keys map to the
* specified value.
* <p>Beware that this is a linear search, unlike lookups by key,
* and that multiple keys can map to the same value and this will
@@ -357,7 +359,7 @@ public class SparseArray<E> implements Cloneable {
/**
* Returns an index for which {@link #valueAt} would return the
- * specified key, or a negative number if no keys map to the
+ * specified value, or a negative number if no keys map to the
* specified value.
* <p>Beware that this is a linear search, unlike lookups by key,
* and that multiple keys can map to the same value and this will
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index 74fa9e875076..33644832bdf1 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -536,9 +536,6 @@ public class RecordingCanvas extends Canvas {
@Nullable int[] colors, int colorOffset, @Nullable short[] indices, int indexOffset,
int indexCount, @NonNull Paint paint) {
checkRange(verts.length, vertOffset, vertexCount);
- if (isHardwareAccelerated()) {
- return;
- }
if (texs != null) {
checkRange(texs.length, texOffset, vertexCount);
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 64686dd977a2..87b7b057e668 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -19,6 +19,7 @@ package android.view.animation;
import android.annotation.AnimRes;
import android.annotation.ColorInt;
import android.annotation.InterpolatorRes;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.RectF;
@@ -183,6 +184,7 @@ public abstract class Animation implements Cloneable {
/**
* The animation listener to be notified when the animation starts, ends or repeats.
*/
+ @UnsupportedAppUsage
AnimationListener mListener;
/**
@@ -211,9 +213,13 @@ public abstract class Animation implements Cloneable {
private boolean mMore = true;
private boolean mOneMoreTime = true;
+ @UnsupportedAppUsage
RectF mPreviousRegion = new RectF();
+ @UnsupportedAppUsage
RectF mRegion = new RectF();
+ @UnsupportedAppUsage
Transformation mTransformation = new Transformation();
+ @UnsupportedAppUsage
Transformation mPreviousTransformation = new Transformation();
private final CloseGuard guard = CloseGuard.get();
@@ -322,6 +328,7 @@ public abstract class Animation implements Cloneable {
/**
* @hide
*/
+ @UnsupportedAppUsage
public void detach() {
if (mStarted && !mEnded) {
mEnded = true;
@@ -1046,6 +1053,7 @@ public abstract class Animation implements Cloneable {
*
* @hide
*/
+ @UnsupportedAppUsage
public void getInvalidateRegion(int left, int top, int right, int bottom,
RectF invalidate, Transformation transformation) {
@@ -1077,6 +1085,7 @@ public abstract class Animation implements Cloneable {
*
* @hide
*/
+ @UnsupportedAppUsage
public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
final RectF region = mPreviousRegion;
region.set(left, top, right, bottom);
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 29f8442b2b46..c877b9cec812 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -19,6 +19,7 @@ package android.view.animation;
import android.annotation.AnimRes;
import android.annotation.InterpolatorRes;
import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -157,6 +158,7 @@ public class AnimationUtils {
return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
}
+ @UnsupportedAppUsage
private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 8eb5b5cf5441..58da04d8d38f 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
package android.view.animation;
import android.annotation.FloatRange;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -238,6 +239,7 @@ public class Transformation {
* Print short string, to optimize dumping.
* @hide
*/
+ @UnsupportedAppUsage
public void printShortString(PrintWriter pw) {
pw.print("{alpha="); pw.print(mAlpha);
pw.print(" matrix=");
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
index 216022b24eb6..6c040d4c61aa 100644
--- a/core/java/android/view/animation/TranslateAnimation.java
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -16,6 +16,7 @@
package android.view.animation;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
@@ -34,13 +35,17 @@ public class TranslateAnimation extends Animation {
private int mToYType = ABSOLUTE;
/** @hide */
+ @UnsupportedAppUsage
protected float mFromXValue = 0.0f;
/** @hide */
+ @UnsupportedAppUsage
protected float mToXValue = 0.0f;
/** @hide */
+ @UnsupportedAppUsage
protected float mFromYValue = 0.0f;
/** @hide */
+ @UnsupportedAppUsage
protected float mToYValue = 0.0f;
/** @hide */
diff --git a/core/java/android/view/animation/TranslateYAnimation.java b/core/java/android/view/animation/TranslateYAnimation.java
index 714558dc717d..a6e0ccb18805 100644
--- a/core/java/android/view/animation/TranslateYAnimation.java
+++ b/core/java/android/view/animation/TranslateYAnimation.java
@@ -16,6 +16,7 @@
package android.view.animation;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
/**
@@ -38,6 +39,7 @@ public class TranslateYAnimation extends TranslateAnimation {
/**
* Constructor. Passes in 0 for the x parameters of TranslateAnimation
*/
+ @UnsupportedAppUsage
public TranslateYAnimation(int fromYType, float fromYValue, int toYType, float toYValue) {
super(ABSOLUTE, 0, ABSOLUTE, 0, fromYType, fromYValue, toYType, toYValue);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index b5c736480215..32b2f63f4e59 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1405,7 +1405,8 @@ public final class AutofillManager {
final SyncResultReceiver receiver = new SyncResultReceiver();
try {
mService.getAvailableFieldClassificationAlgorithms(receiver);
- final String[] algorithms = receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
+ final String[] algorithms = receiver
+ .getObjectResult(SyncResultReceiver.TYPE_STRING_ARRAY);
return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -2906,7 +2907,7 @@ public final class AutofillManager {
case TYPE_STRING:
return (T) mBundle.getString(EXTRA);
case TYPE_STRING_ARRAY:
- return (T) mBundle.getString(EXTRA);
+ return (T) mBundle.getStringArray(EXTRA);
case TYPE_PARCELABLE:
return (T) mBundle.getParcelable(EXTRA);
default:
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 9685f752285d..943c8bda7c91 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -25,6 +25,7 @@ import android.util.DebugUtils;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
@@ -62,8 +63,6 @@ import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
import java.io.PrintWriter;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
public final class ProcessState {
private static final String TAG = "ProcessStats";
@@ -127,6 +126,7 @@ public final class ProcessState {
private final long mVersion;
private final DurationsTable mDurations;
private final PssTable mPssTable;
+ private final long[] mTotalRunningPss = new long[ProcessStats.PSS_COUNT];
private ProcessState mCommonProcess;
private int mCurCombinedState = STATE_NOTHING;
@@ -135,6 +135,9 @@ public final class ProcessState {
private int mLastPssState = STATE_NOTHING;
private long mLastPssTime;
+ private long mTotalRunningStartTime;
+ private long mTotalRunningDuration;
+
private boolean mActive;
private int mNumActiveServices;
private int mNumStartedServices;
@@ -182,6 +185,9 @@ public final class ProcessState {
mVersion = vers;
mCurCombinedState = commonProcess.mCurCombinedState;
mStartTime = now;
+ if (mCurCombinedState != STATE_NOTHING) {
+ mTotalRunningStartTime = now;
+ }
mDurations = new DurationsTable(commonProcess.mStats.mTableData);
mPssTable = new PssTable(commonProcess.mStats.mTableData);
}
@@ -190,6 +196,8 @@ public final class ProcessState {
ProcessState pnew = new ProcessState(this, mPackage, mUid, mVersion, mName, now);
pnew.mDurations.addDurations(mDurations);
pnew.mPssTable.copyFrom(mPssTable, PSS_COUNT);
+ System.arraycopy(mTotalRunningPss, 0, pnew.mTotalRunningPss, 0, ProcessStats.PSS_COUNT);
+ pnew.mTotalRunningDuration = getTotalRunningDuration(now);
pnew.mNumExcessiveCpu = mNumExcessiveCpu;
pnew.mNumCachedKill = mNumCachedKill;
pnew.mMinCachedKillPss = mMinCachedKillPss;
@@ -235,7 +243,7 @@ public final class ProcessState {
public void setMultiPackage(boolean val) {
mMultiPackage = val;
}
-
+
public int getDurationsBucketCount() {
return mDurations.getKeyCount();
}
@@ -243,6 +251,10 @@ public final class ProcessState {
public void add(ProcessState other) {
mDurations.addDurations(other.mDurations);
mPssTable.mergeStats(other.mPssTable);
+ // Note that we don't touch mTotalRunningPss, because in current use
+ // 'other' is older stats that are being added in to these newer ones.
+ // So the newer ones keep track of the total running time, which is always
+ // the right thing over whatever was in older stats.
mNumExcessiveCpu += other.mNumExcessiveCpu;
if (other.mNumCachedKill > 0) {
addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
@@ -277,6 +289,10 @@ public final class ProcessState {
out.writeInt(mMultiPackage ? 1 : 0);
mDurations.writeToParcel(out);
mPssTable.writeToParcel(out);
+ for (int i = 0; i < ProcessStats.PSS_COUNT; i++) {
+ out.writeLong(mTotalRunningPss[i]);
+ }
+ out.writeLong(getTotalRunningDuration(now));
out.writeInt(0); // was mNumExcessiveWake
out.writeInt(mNumExcessiveCpu);
out.writeInt(mNumCachedKill);
@@ -300,6 +316,10 @@ public final class ProcessState {
if (!mPssTable.readFromParcel(in)) {
return false;
}
+ for (int i = 0; i < ProcessStats.PSS_COUNT; i++) {
+ mTotalRunningPss[i] = in.readLong();
+ }
+ mTotalRunningDuration = in.readLong();
in.readInt(); // was mNumExcessiveWake
mNumExcessiveCpu = in.readInt();
mNumCachedKill = in.readInt();
@@ -334,7 +354,8 @@ public final class ProcessState {
public boolean hasAnyData() {
return !(mDurations.getKeyCount() == 0
&& mCurCombinedState == STATE_NOTHING
- && mPssTable.getKeyCount() == 0);
+ && mPssTable.getKeyCount() == 0
+ && mTotalRunningPss[PSS_SAMPLE_COUNT] == 0);
}
/**
@@ -374,6 +395,19 @@ public final class ProcessState {
if (!mDead && (mCurCombinedState != state)) {
//Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
commitStateTime(now);
+ if (state == STATE_NOTHING) {
+ // We are transitioning to a no longer running state... stop counting run time.
+ mTotalRunningDuration += now - mTotalRunningStartTime;
+ mTotalRunningStartTime = 0;
+ } else if (mCurCombinedState == STATE_NOTHING) {
+ // We previously weren't running... now starting again, clear out total
+ // running info.
+ mTotalRunningDuration = 0;
+ mTotalRunningStartTime = now;
+ for (int i = ProcessStats.PSS_COUNT - 1; i >= 0; i--) {
+ mTotalRunningPss[i] = 0;
+ }
+ }
mCurCombinedState = state;
}
}
@@ -388,6 +422,8 @@ public final class ProcessState {
if (dur > 0) {
mDurations.addDuration(mCurCombinedState, dur);
}
+ mTotalRunningDuration += now - mTotalRunningStartTime;
+ mTotalRunningStartTime = now;
}
mStartTime = now;
}
@@ -496,6 +532,8 @@ public final class ProcessState {
// First update the common process.
mCommonProcess.mPssTable.mergeStats(mCurCombinedState, 1, pss, pss, pss, uss, uss, uss,
rss, rss, rss);
+ PssTable.mergeStats(mCommonProcess.mTotalRunningPss, 0, 1, pss, pss, pss, uss, uss, uss,
+ rss, rss, rss);
// If the common process is not multi-package, there is nothing else to do.
if (!mCommonProcess.mMultiPackage) {
@@ -504,7 +542,10 @@ public final class ProcessState {
if (pkgList != null) {
for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurCombinedState, 1,
+ ProcessState fixedProc = pullFixedProc(pkgList, ip);
+ fixedProc.mPssTable.mergeStats(mCurCombinedState, 1,
+ pss, pss, pss, uss, uss, uss, rss, rss, rss);
+ PssTable.mergeStats(fixedProc.mTotalRunningPss, 0, 1,
pss, pss, pss, uss, uss, uss, rss, rss, rss);
}
}
@@ -621,6 +662,11 @@ public final class ProcessState {
return proc;
}
+ public long getTotalRunningDuration(long now) {
+ return mTotalRunningDuration +
+ (mTotalRunningStartTime != 0 ? (now - mTotalRunningStartTime) : 0);
+ }
+
public long getDuration(int state, long now) {
long time = mDurations.getValueForId((byte)state);
if (mCurCombinedState == state) {
@@ -671,7 +717,7 @@ public final class ProcessState {
/**
* Sums up the PSS data and adds it to 'data'.
- *
+ *
* @param data The aggregate data is added here.
* @param now SystemClock.uptimeMillis()
*/
@@ -834,6 +880,7 @@ public final class ProcessState {
String running = "";
if (mCurCombinedState == bucket) {
running = " (running)";
+ time += now - mStartTime;
}
if (time != 0) {
pw.print(prefix);
@@ -870,7 +917,7 @@ public final class ProcessState {
}
public void dumpPss(PrintWriter pw, String prefix,
- int[] screenStates, int[] memStates, int[] procStates) {
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
boolean printedHeader = false;
int printedScreen = -1;
for (int is=0; is<screenStates.length; is++) {
@@ -880,52 +927,51 @@ public final class ProcessState {
final int iscreen = screenStates[is];
final int imem = memStates[im];
final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
- long count = getPssSampleCount(bucket);
- if (count > 0) {
- if (!printedHeader) {
- pw.print(prefix);
- pw.print("PSS/USS (");
- pw.print(mPssTable.getKeyCount());
- pw.println(" entries):");
- printedHeader = true;
- }
+ final int key = mPssTable.getKey((byte)bucket);
+ if (key == SparseMappingTable.INVALID_KEY) {
+ continue;
+ }
+ final long[] table = mPssTable.getArrayForKey(key);
+ final int tableOffset = SparseMappingTable.getIndexFromKey(key);
+ if (!printedHeader) {
pw.print(prefix);
- pw.print(" ");
- if (screenStates.length > 1) {
- DumpUtils.printScreenLabel(pw,
- printedScreen != iscreen ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- }
- if (memStates.length > 1) {
- DumpUtils.printMemLabel(pw,
- printedMem != imem ? imem : STATE_NOTHING, '/');
- printedMem = imem;
- }
- pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
- pw.print(count);
- pw.print(" samples ");
- DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, getPssAverage(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, getPssMaximum(bucket) * 1024);
- pw.print(" / ");
- DebugUtils.printSizeValue(pw, getPssUssMinimum(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, getPssUssAverage(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, getPssUssMaximum(bucket) * 1024);
- pw.print(" / ");
- DebugUtils.printSizeValue(pw, getPssRssMinimum(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, getPssRssAverage(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, getPssRssMaximum(bucket) * 1024);
- pw.println();
+ pw.print("PSS/USS (");
+ pw.print(mPssTable.getKeyCount());
+ pw.println(" entries):");
+ printedHeader = true;
+ }
+ pw.print(prefix);
+ pw.print(" ");
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw,
+ printedScreen != iscreen ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
}
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw,
+ printedMem != imem ? imem : STATE_NOTHING, '/');
+ printedMem = imem;
+ }
+ pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
+ dumpPssSamples(pw, table, tableOffset);
+ pw.println();
}
}
}
+ final long totalRunningDuration = getTotalRunningDuration(now);
+ if (totalRunningDuration != 0) {
+ pw.print(prefix);
+ pw.print("Cur time ");
+ TimeUtils.formatDuration(totalRunningDuration, pw);
+ if (mTotalRunningStartTime != 0) {
+ pw.print(" (running)");
+ }
+ if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+ pw.print(": ");
+ dumpPssSamples(pw, mTotalRunningPss, 0);
+ }
+ pw.println();
+ }
if (mNumExcessiveCpu != 0) {
pw.print(prefix); pw.print("Killed for excessive CPU use: ");
pw.print(mNumExcessiveCpu); pw.println(" times");
@@ -939,6 +985,28 @@ public final class ProcessState {
}
}
+ public static void dumpPssSamples(PrintWriter pw, long[] table, int offset) {
+ DebugUtils.printSizeValue(pw, table[offset + PSS_MINIMUM] * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_AVERAGE] * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_MAXIMUM] * 1024);
+ pw.print("/");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_USS_MINIMUM] * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_USS_AVERAGE] * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_USS_MAXIMUM] * 1024);
+ pw.print("/");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_RSS_MINIMUM] * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_RSS_AVERAGE] * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, table[offset + PSS_RSS_MAXIMUM] * 1024);
+ pw.print(" over ");
+ pw.print(table[offset + PSS_SAMPLE_COUNT]);
+ }
+
private void dumpProcessSummaryDetails(PrintWriter pw, String prefix,
String label, int[] screenStates, int[] memStates, int[] procStates,
long now, long totalTime, boolean full) {
@@ -1116,6 +1184,21 @@ public final class ProcessState {
dumpAllPssCheckin(pw);
pw.println();
}
+ if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+ pw.print("pkgrun,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(DumpUtils.collapseString(pkgName, itemName));
+ pw.print(",");
+ pw.print(getTotalRunningDuration(now));
+ pw.print(",");
+ dumpPssSamplesCheckin(pw, mTotalRunningPss, 0);
+ pw.println();
+ }
if (mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
pw.print("pkgkills,");
pw.print(pkgName);
@@ -1158,6 +1241,17 @@ public final class ProcessState {
dumpAllPssCheckin(pw);
pw.println();
}
+ if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+ pw.print("procrun,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(getTotalRunningDuration(now));
+ pw.print(",");
+ dumpPssSamplesCheckin(pw, mTotalRunningPss, 0);
+ pw.println();
+ }
if (mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
pw.print("kills,");
pw.print(procName);
@@ -1204,26 +1298,31 @@ public final class ProcessState {
pw.print(',');
DumpUtils.printProcStateTag(pw, type);
pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_SAMPLE_COUNT));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_MINIMUM));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_AVERAGE));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_MAXIMUM));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_USS_MINIMUM));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_USS_AVERAGE));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_USS_MAXIMUM));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_RSS_MINIMUM));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_RSS_AVERAGE));
- pw.print(':');
- pw.print(mPssTable.getValue(key, PSS_RSS_MAXIMUM));
- }
+ dumpPssSamplesCheckin(pw, mPssTable.getArrayForKey(key),
+ SparseMappingTable.getIndexFromKey(key));
+ }
+ }
+
+ public static void dumpPssSamplesCheckin(PrintWriter pw, long[] table, int offset) {
+ pw.print(table[offset + PSS_SAMPLE_COUNT]);
+ pw.print(':');
+ pw.print(table[offset + PSS_MINIMUM]);
+ pw.print(':');
+ pw.print(table[offset + PSS_AVERAGE]);
+ pw.print(':');
+ pw.print(table[offset + PSS_MAXIMUM]);
+ pw.print(':');
+ pw.print(table[offset + PSS_USS_MINIMUM]);
+ pw.print(':');
+ pw.print(table[offset + PSS_USS_AVERAGE]);
+ pw.print(':');
+ pw.print(table[offset + PSS_USS_MAXIMUM]);
+ pw.print(':');
+ pw.print(table[offset + PSS_RSS_MINIMUM]);
+ pw.print(':');
+ pw.print(table[offset + PSS_RSS_AVERAGE]);
+ pw.print(':');
+ pw.print(table[offset + PSS_RSS_MAXIMUM]);
}
@Override
@@ -1253,7 +1352,7 @@ public final class ProcessState {
}
// Group proc stats by type (screen state + mem state + process state)
- Map<Integer, Long> durationByState = new HashMap<>();
+ SparseLongArray durationByState = new SparseLongArray();
boolean didCurState = false;
for (int i=0; i<mDurations.getKeyCount(); i++) {
final int key = mDurations.getKeyAt(i);
@@ -1272,7 +1371,7 @@ public final class ProcessState {
for (int i=0; i<mPssTable.getKeyCount(); i++) {
final int key = mPssTable.getKeyAt(i);
final int type = SparseMappingTable.getIdFromKey(key);
- if (!durationByState.containsKey(type)) {
+ if (durationByState.indexOfKey(type) < 0) {
// state without duration should not have stats!
continue;
}
@@ -1284,36 +1383,35 @@ public final class ProcessState {
type);
long duration = durationByState.get(type);
- durationByState.remove(type); // remove the key since it is already being dumped.
+ durationByState.delete(type); // remove the key since it is already being dumped.
proto.write(ProcessStatsProto.State.DURATION_MS, duration);
- proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT));
- ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
- mPssTable.getValue(key, PSS_MINIMUM),
- mPssTable.getValue(key, PSS_AVERAGE),
- mPssTable.getValue(key, PSS_MAXIMUM));
- ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
- mPssTable.getValue(key, PSS_USS_MINIMUM),
- mPssTable.getValue(key, PSS_USS_AVERAGE),
- mPssTable.getValue(key, PSS_USS_MAXIMUM));
- ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.RSS,
- mPssTable.getValue(key, PSS_RSS_MINIMUM),
- mPssTable.getValue(key, PSS_RSS_AVERAGE),
- mPssTable.getValue(key, PSS_RSS_MAXIMUM));
+ mPssTable.writeStatsToProtoForKey(proto, key);
proto.end(stateToken);
}
- for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) {
+ for (int i = 0; i < durationByState.size(); i++) {
final long stateToken = proto.start(ProcessStatsProto.STATES);
DumpUtils.printProcStateTagProto(proto,
ProcessStatsProto.State.SCREEN_STATE,
ProcessStatsProto.State.MEMORY_STATE,
ProcessStatsProto.State.PROCESS_STATE,
- entry.getKey());
- proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue());
+ durationByState.keyAt(i));
+ proto.write(ProcessStatsProto.State.DURATION_MS, durationByState.valueAt(i));
+ proto.end(stateToken);
+ }
+
+ final long totalRunningDuration = getTotalRunningDuration(now);
+ if (totalRunningDuration > 0) {
+ final long stateToken = proto.start(ProcessStatsProto.TOTAL_RUNNING_STATE);
+ proto.write(ProcessStatsProto.State.DURATION_MS, totalRunningDuration);
+ if (mTotalRunningPss[PSS_SAMPLE_COUNT] != 0) {
+ PssTable.writeStatsToProto(proto, mTotalRunningPss, 0);
+ }
proto.end(stateToken);
}
+
proto.end(token);
}
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 12b16d040594..d088354e059d 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -158,7 +158,7 @@ public final class ProcessStats implements Parcelable {
};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 33;
+ private static final int PARCEL_VERSION = 34;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -1483,7 +1483,7 @@ public final class ProcessStats implements Parcelable {
proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
ALL_PROC_STATES, now);
proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES);
+ ALL_PROC_STATES, now);
proc.dumpInternalLocked(pw, " ", dumpAll);
}
} else {
@@ -1558,7 +1558,7 @@ public final class ProcessStats implements Parcelable {
int uid = uids.keyAt(iu);
numTotalProcs++;
final ProcessState proc = uids.valueAt(iu);
- if (proc.hasAnyData()) {
+ if (!proc.hasAnyData()) {
continue;
}
if (!proc.isMultiPackage()) {
@@ -1587,7 +1587,7 @@ public final class ProcessStats implements Parcelable {
pw.print(" entries)"); pw.println(":");
proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
ALL_PROC_STATES, now);
- proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES);
+ proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now);
proc.dumpInternalLocked(pw, " ", dumpAll);
}
}
diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java
index 1e7c566e23a7..f858e552bada 100644
--- a/core/java/com/android/internal/app/procstats/PssTable.java
+++ b/core/java/com/android/internal/app/procstats/PssTable.java
@@ -28,6 +28,10 @@ import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+import android.service.procstats.ProcessStatsProto;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
/**
* Class to accumulate PSS data.
*/
@@ -46,18 +50,17 @@ public class PssTable extends SparseMappingTable.Table {
public void mergeStats(PssTable that) {
final int N = that.getKeyCount();
for (int i=0; i<N; i++) {
- final int key = that.getKeyAt(i);
- final int state = SparseMappingTable.getIdFromKey(key);
- mergeStats(state, (int)that.getValue(key, PSS_SAMPLE_COUNT),
- that.getValue(key, PSS_MINIMUM),
- that.getValue(key, PSS_AVERAGE),
- that.getValue(key, PSS_MAXIMUM),
- that.getValue(key, PSS_USS_MINIMUM),
- that.getValue(key, PSS_USS_AVERAGE),
- that.getValue(key, PSS_USS_MAXIMUM),
- that.getValue(key, PSS_RSS_MINIMUM),
- that.getValue(key, PSS_RSS_AVERAGE),
- that.getValue(key, PSS_RSS_MAXIMUM));
+ final int thatKey = that.getKeyAt(i);
+ final int state = SparseMappingTable.getIdFromKey(thatKey);
+
+ final int key = getOrAddKey((byte)state, PSS_COUNT);
+ final long[] stats = getArrayForKey(key);
+ final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+
+ final long[] thatStats = that.getArrayForKey(thatKey);
+ final int thatStatsIndex = SparseMappingTable.getIndexFromKey(thatKey);
+
+ mergeStats(stats, statsIndex, thatStats, thatStatsIndex);
}
}
@@ -68,64 +71,100 @@ public class PssTable extends SparseMappingTable.Table {
public void mergeStats(int state, int inCount, long minPss, long avgPss, long maxPss,
long minUss, long avgUss, long maxUss, long minRss, long avgRss, long maxRss) {
final int key = getOrAddKey((byte)state, PSS_COUNT);
- final long count = getValue(key, PSS_SAMPLE_COUNT);
+ final long[] stats = getArrayForKey(key);
+ final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+ mergeStats(stats, statsIndex, inCount, minPss, avgPss, maxPss, minUss, avgUss, maxUss,
+ minRss, avgRss, maxRss);
+ }
+
+ public static void mergeStats(final long[] stats, final int statsIndex,
+ final long[] thatStats, int thatStatsIndex) {
+ mergeStats(stats, statsIndex, (int)thatStats[thatStatsIndex + PSS_SAMPLE_COUNT],
+ thatStats[thatStatsIndex + PSS_MINIMUM],
+ thatStats[thatStatsIndex + PSS_AVERAGE],
+ thatStats[thatStatsIndex + PSS_MAXIMUM],
+ thatStats[thatStatsIndex + PSS_USS_MINIMUM],
+ thatStats[thatStatsIndex + PSS_USS_AVERAGE],
+ thatStats[thatStatsIndex + PSS_USS_MAXIMUM],
+ thatStats[thatStatsIndex + PSS_RSS_MINIMUM],
+ thatStats[thatStatsIndex + PSS_RSS_AVERAGE],
+ thatStats[thatStatsIndex + PSS_RSS_MAXIMUM]);
+ }
+
+ public static void mergeStats(final long[] stats, final int statsIndex, final int inCount,
+ final long minPss, final long avgPss, final long maxPss,
+ final long minUss, final long avgUss, final long maxUss,
+ final long minRss, final long avgRss, final long maxRss) {
+ final long count = stats[statsIndex + PSS_SAMPLE_COUNT];
if (count == 0) {
- setValue(key, PSS_SAMPLE_COUNT, inCount);
- setValue(key, PSS_MINIMUM, minPss);
- setValue(key, PSS_AVERAGE, avgPss);
- setValue(key, PSS_MAXIMUM, maxPss);
- setValue(key, PSS_USS_MINIMUM, minUss);
- setValue(key, PSS_USS_AVERAGE, avgUss);
- setValue(key, PSS_USS_MAXIMUM, maxUss);
- setValue(key, PSS_RSS_MINIMUM, minRss);
- setValue(key, PSS_RSS_AVERAGE, avgRss);
- setValue(key, PSS_RSS_MAXIMUM, maxRss);
+ stats[statsIndex + PSS_SAMPLE_COUNT] = inCount;
+ stats[statsIndex + PSS_MINIMUM] = minPss;
+ stats[statsIndex + PSS_AVERAGE] = avgPss;
+ stats[statsIndex + PSS_MAXIMUM] = maxPss;
+ stats[statsIndex + PSS_USS_MINIMUM] = minUss;
+ stats[statsIndex + PSS_USS_AVERAGE] = avgUss;
+ stats[statsIndex + PSS_USS_MAXIMUM] = maxUss;
+ stats[statsIndex + PSS_RSS_MINIMUM] = minRss;
+ stats[statsIndex + PSS_RSS_AVERAGE] = avgRss;
+ stats[statsIndex + PSS_RSS_MAXIMUM] = maxRss;
} else {
- setValue(key, PSS_SAMPLE_COUNT, count + inCount);
-
- long val;
+ stats[statsIndex + PSS_SAMPLE_COUNT] = count + inCount;
- val = getValue(key, PSS_MINIMUM);
- if (val > minPss) {
- setValue(key, PSS_MINIMUM, minPss);
+ if (stats[statsIndex + PSS_MINIMUM] > minPss) {
+ stats[statsIndex + PSS_MINIMUM] = minPss;
}
- val = getValue(key, PSS_AVERAGE);
- setValue(key, PSS_AVERAGE,
- (long)(((val*(double)count)+(avgPss*(double)inCount)) / (count+inCount)));
+ stats[statsIndex + PSS_AVERAGE] = (long)(((stats[statsIndex + PSS_AVERAGE]
+ * (double)count) + (avgPss * (double)inCount)) / (count + inCount));
- val = getValue(key, PSS_MAXIMUM);
- if (val < maxPss) {
- setValue(key, PSS_MAXIMUM, maxPss);
+ if (stats[statsIndex + PSS_MAXIMUM] < maxPss) {
+ stats[statsIndex + PSS_MAXIMUM] = maxPss;
}
- val = getValue(key, PSS_USS_MINIMUM);
- if (val > minUss) {
- setValue(key, PSS_USS_MINIMUM, minUss);
+ if (stats[statsIndex + PSS_USS_MINIMUM] > minUss) {
+ stats[statsIndex + PSS_USS_MINIMUM] = minUss;
}
- val = getValue(key, PSS_USS_AVERAGE);
- setValue(key, PSS_USS_AVERAGE,
- (long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount)));
+ stats[statsIndex + PSS_USS_AVERAGE] = (long)(((stats[statsIndex + PSS_USS_AVERAGE]
+ * (double)count) + (avgUss * (double)inCount)) / (count + inCount));
- val = getValue(key, PSS_USS_MAXIMUM);
- if (val < maxUss) {
- setValue(key, PSS_USS_MAXIMUM, maxUss);
+ if (stats[statsIndex + PSS_USS_MAXIMUM] < maxUss) {
+ stats[statsIndex + PSS_USS_MAXIMUM] = maxUss;
}
- val = getValue(key, PSS_RSS_MINIMUM);
- if (val > minUss) {
- setValue(key, PSS_RSS_MINIMUM, minUss);
+ if (stats[statsIndex + PSS_RSS_MINIMUM] > minRss) {
+ stats[statsIndex + PSS_RSS_MINIMUM] = minRss;
}
- val = getValue(key, PSS_RSS_AVERAGE);
- setValue(key, PSS_RSS_AVERAGE,
- (long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount)));
+ stats[statsIndex + PSS_RSS_AVERAGE] = (long)(((stats[statsIndex + PSS_RSS_AVERAGE]
+ * (double)count) + (avgRss * (double)inCount)) / (count + inCount));
- val = getValue(key, PSS_RSS_MAXIMUM);
- if (val < maxUss) {
- setValue(key, PSS_RSS_MAXIMUM, maxUss);
+ if (stats[statsIndex + PSS_RSS_MAXIMUM] < maxRss) {
+ stats[statsIndex + PSS_RSS_MAXIMUM] = maxRss;
}
}
}
+
+ public void writeStatsToProtoForKey(ProtoOutputStream proto, int key) {
+ final long[] stats = getArrayForKey(key);
+ final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+ writeStatsToProto(proto, stats, statsIndex);
+ }
+
+ public static void writeStatsToProto(ProtoOutputStream proto, final long[] stats,
+ final int statsIndex) {
+ proto.write(ProcessStatsProto.State.SAMPLE_SIZE, stats[statsIndex + PSS_SAMPLE_COUNT]);
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS,
+ stats[statsIndex + PSS_MINIMUM],
+ stats[statsIndex + PSS_AVERAGE],
+ stats[statsIndex + PSS_MAXIMUM]);
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS,
+ stats[statsIndex + PSS_USS_MINIMUM],
+ stats[statsIndex + PSS_USS_AVERAGE],
+ stats[statsIndex + PSS_USS_MAXIMUM]);
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.RSS,
+ stats[statsIndex + PSS_RSS_MINIMUM],
+ stats[statsIndex + PSS_RSS_AVERAGE],
+ stats[statsIndex + PSS_RSS_MAXIMUM]);
+ }
}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index baf6a4da3f84..eb369e1bef14 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -17,12 +17,19 @@
package com.android.internal.os;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.Binder;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -31,6 +38,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BinderInternal.CallSession;
import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
@@ -74,12 +82,99 @@ public class BinderCallsStats implements BinderInternal.Observer {
private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
- public BinderCallsStats(Random random) {
- this.mRandom = random;
+ // State updated by the broadcast receiver below.
+ private boolean mScreenInteractive;
+ private boolean mCharging;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_CHANGED:
+ mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ mScreenInteractive = true;
+ break;
+ case Intent.ACTION_SCREEN_OFF:
+ mScreenInteractive = false;
+ break;
+ }
+ }
+ };
+
+ /** Injector for {@link BinderCallsStats}. */
+ public static class Injector {
+ public Random getRandomGenerator() {
+ return new Random();
+ }
+ }
+
+ public BinderCallsStats(Injector injector) {
+ this.mRandom = injector.getRandomGenerator();
+ }
+
+ public void systemReady(Context context) {
+ registerBroadcastReceiver(context);
+ setInitialState(queryScreenInteractive(context), queryIsCharging());
+ }
+
+ /**
+ * Listens for screen/battery state changes.
+ */
+ @VisibleForTesting
+ public void registerBroadcastReceiver(Context context) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiver(mBroadcastReceiver, filter);
+ }
+
+ /**
+ * Sets the battery/screen initial state.
+ *
+ * This has to be updated *after* the broadcast receiver is installed.
+ */
+ @VisibleForTesting
+ public void setInitialState(boolean isScreenInteractive, boolean isCharging) {
+ this.mScreenInteractive = isScreenInteractive;
+ this.mCharging = isCharging;
+ // Data collected previously was not accurate since the battery/screen state was not set.
+ reset();
+ }
+
+ private boolean queryIsCharging() {
+ final BatteryManagerInternal batteryManager =
+ LocalServices.getService(BatteryManagerInternal.class);
+ if (batteryManager == null) {
+ Slog.wtf(TAG, "BatteryManager null while starting BinderCallsStatsService");
+ // Default to true to not collect any data.
+ return true;
+ } else {
+ return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
+ }
+ }
+
+ private boolean queryScreenInteractive(Context context) {
+ final PowerManager powerManager = context.getSystemService(PowerManager.class);
+ final boolean screenInteractive;
+ if (powerManager == null) {
+ Slog.wtf(TAG, "PowerManager null while starting BinderCallsStatsService",
+ new Throwable());
+ return true;
+ } else {
+ return powerManager.isInteractive();
+ }
}
@Override
+ @Nullable
public CallSession callStarted(Binder binder, int code) {
+ if (mCharging) {
+ return null;
+ }
+
final CallSession s = obtainCallSession();
s.binderClass = binder.getClass();
s.transactionCode = code;
@@ -126,9 +221,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
final int callingUid = getCallingUid();
synchronized (mLock) {
+ // This was already checked in #callStart but check again while synchronized.
+ if (mCharging) {
+ return;
+ }
+
final UidEntry uidEntry = getUidEntry(callingUid);
uidEntry.callCount++;
- final CallStat callStat = uidEntry.getOrCreate(s.binderClass, s.transactionCode);
+ final CallStat callStat = uidEntry.getOrCreate(
+ s.binderClass, s.transactionCode, mScreenInteractive);
callStat.callCount++;
if (recordCall) {
@@ -178,7 +279,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
} catch (RuntimeException e) {
// Do not propagate the exception. We do not want to swallow original exception.
- Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
+ Slog.wtf(TAG, "Unexpected exception while updating mExceptionCounts");
}
}
@@ -225,6 +326,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
exported.className = stat.binderClass.getName();
exported.binderClass = stat.binderClass;
exported.transactionCode = stat.transactionCode;
+ exported.screenInteractive = stat.screenInteractive;
exported.cpuTimeMicros = stat.cpuTimeMicros;
exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
exported.latencyMicros = stat.latencyMicros;
@@ -308,7 +410,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
final List<UidEntry> topEntries = verbose ? entries
: getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
pw.println("Per-UID raw data " + datasetSizeDesc
- + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
+ + "(package/uid, call_desc, screen_interactive, "
+ + "cpu_time_micros, max_cpu_time_micros, "
+ "latency_time_micros, max_latency_time_micros, exception_count, "
+ "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
+ "call_count):");
@@ -320,6 +423,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
.append(uidToString(e.uid, appIdToPkgNameMap))
.append(',').append(e.className)
.append('#').append(e.methodName)
+ .append(',').append(e.screenInteractive)
.append(',').append(e.cpuTimeMicros)
.append(',').append(e.maxCpuTimeMicros)
.append(',').append(e.latencyMicros)
@@ -441,6 +545,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
public static class CallStat {
public Class<? extends Binder> binderClass;
public int transactionCode;
+ // True if the screen was interactive when the call ended.
+ public boolean screenInteractive;
// Number of calls for which we collected data for. We do not record data for all the calls
// when sampling is on.
public long recordedCallCount;
@@ -461,9 +567,11 @@ public class BinderCallsStats implements BinderInternal.Observer {
public long maxReplySizeBytes;
public long exceptionCount;
- CallStat(Class<? extends Binder> binderClass, int transactionCode) {
+ CallStat(Class<? extends Binder> binderClass, int transactionCode,
+ boolean screenInteractive) {
this.binderClass = binderClass;
this.transactionCode = transactionCode;
+ this.screenInteractive = screenInteractive;
}
}
@@ -471,6 +579,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public static class CallStatKey {
public Class<? extends Binder> binderClass;
public int transactionCode;
+ private boolean screenInteractive;
@Override
public boolean equals(Object o) {
@@ -480,6 +589,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
final CallStatKey key = (CallStatKey) o;
return transactionCode == key.transactionCode
+ && screenInteractive == key.screenInteractive
&& (binderClass.equals(key.binderClass));
}
@@ -487,6 +597,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public int hashCode() {
int result = binderClass.hashCode();
result = 31 * result + transactionCode;
+ result = 31 * result + (screenInteractive ? 1231 : 1237);
return result;
}
}
@@ -513,17 +624,20 @@ public class BinderCallsStats implements BinderInternal.Observer {
private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
private CallStatKey mTempKey = new CallStatKey();
- CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode) {
+ CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode,
+ boolean screenInteractive) {
// Use a global temporary key to avoid creating new objects for every lookup.
mTempKey.binderClass = binderClass;
mTempKey.transactionCode = transactionCode;
+ mTempKey.screenInteractive = screenInteractive;
CallStat mapCallStat = mCallStats.get(mTempKey);
// Only create CallStat if it's a new entry, otherwise update existing instance
if (mapCallStat == null) {
- mapCallStat = new CallStat(binderClass, transactionCode);
+ mapCallStat = new CallStat(binderClass, transactionCode, screenInteractive);
CallStatKey key = new CallStatKey();
key.binderClass = binderClass;
key.transactionCode = transactionCode;
+ key.screenInteractive = screenInteractive;
mCallStats.put(key, mapCallStat);
}
return mapCallStat;
@@ -592,6 +706,11 @@ public class BinderCallsStats implements BinderInternal.Observer {
return result;
}
+ @VisibleForTesting
+ public BroadcastReceiver getBroadcastReceiver() {
+ return mBroadcastReceiver;
+ }
+
private static int compareByCpuDesc(
ExportedCallStat a, ExportedCallStat b) {
return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 2790324e860e..616520f4e2b1 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -140,14 +140,14 @@ oneway interface IStatusBar
void showShutdownUi(boolean isReboot, String reason);
- // Used to show the dialog when FingerprintService starts authentication
- void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
- // Used to hide the dialog when a finger is authenticated
- void onFingerprintAuthenticated();
+ // Used to show the dialog when BiometricService starts authentication
+ void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
+ // Used to hide the dialog when a biometric is authenticated
+ void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
- void onFingerprintHelp(String message);
+ void onBiometricHelp(String message);
// Used to set a message - the dialog will dismiss after a certain amount of time
- void onFingerprintError(String error);
- // Used to hide the fingerprint dialog when the authenticationclient is stopped
- void hideFingerprintDialog();
+ void onBiometricError(String error);
+ // Used to hide the biometric dialog when the AuthenticationClient is stopped
+ void hideBiometricDialog();
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 159d49bc0009..a79e15a0b7e7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -90,14 +90,14 @@ interface IStatusBarService
void showPinningEnterExitToast(boolean entering);
void showPinningEscapeToast();
- // Used to show the dialog when FingerprintService starts authentication
- void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
- // Used to hide the dialog when a finger is authenticated
- void onFingerprintAuthenticated();
+ // Used to show the dialog when BiometricService starts authentication
+ void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
+ // Used to hide the dialog when a biometric is authenticated
+ void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
- void onFingerprintHelp(String message);
+ void onBiometricHelp(String message);
// Used to set a message - the dialog will dismiss after a certain amount of time
- void onFingerprintError(String error);
- // Used to hide the fingerprint dialog when the authenticationclient is stopped
- void hideFingerprintDialog();
+ void onBiometricError(String error);
+ // Used to hide the biometric dialog when the AuthenticationClient is stopped
+ void hideBiometricDialog();
}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 3b024b442060..db3bfe6e8157 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -320,9 +320,12 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
jintArray jcolors, jint colorIndex,
jshortArray jindices, jint indexIndex,
jint indexCount, jlong paintHandle) {
+
+ const int vertexCount = floatCount >> 1; // 2 floats per SkPoint
+
AutoJavaFloatArray vertA(env, jverts, vertIndex + floatCount);
AutoJavaFloatArray texA(env, jtexs, texIndex + floatCount);
- AutoJavaIntArray colorA(env, jcolors, colorIndex + floatCount);
+ AutoJavaIntArray colorA(env, jcolors, colorIndex + vertexCount);
AutoJavaShortArray indexA(env, jindices, indexIndex + indexCount);
const float* verts = vertA.ptr() + vertIndex;
@@ -337,7 +340,6 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
indices = (const uint16_t*)(indexA.ptr() + indexIndex);
}
- int vertexCount = floatCount >> 1; // 2 floats per SkPoint
SkVertices::VertexMode mode = static_cast<SkVertices::VertexMode>(modeHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
get_canvas(canvasHandle)->drawVertices(SkVertices::MakeCopy(mode, vertexCount,
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index 15ede0c5e192..b2a88b70905b 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -174,4 +174,8 @@ message ProcessStatsProto {
optional android.util.AggStats rss = 8;
}
repeated State states = 5;
+
+ // Total time process has been running... screen_state, memory_state, and process_state
+ // will not be set.
+ optional State total_running_state = 6;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2ff9c9d662b..6a3487c16572 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -606,6 +606,14 @@
<protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
<protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
+ <!-- Added in Q -->
+
+ <!-- For CarIdlenessTracker -->
+ <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_OFF" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.FORCE_IDLE" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.UNFORCE_IDLE" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -3500,6 +3508,10 @@
<permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
android:protectionLevel="signature" />
+ <!-- @hide Internal permission to allows an application to access card content provider. -->
+ <permission android:name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1a87fa2bd34e..5542f003fa7e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -383,6 +383,8 @@ public class SettingsBackupTest {
Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL,
Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST,
Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL,
+ Settings.Global.SETTINGS_USE_EXTERNAL_PROVIDER_API,
+ Settings.Global.SETTINGS_USE_PSD_API,
Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
Settings.Global.SHOW_FIRST_CRASH_DIALOG,
Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
@@ -484,6 +486,7 @@ public class SettingsBackupTest {
Settings.Global.WIFI_SAVED_STATE,
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
+ Settings.Global.WIFI_SCAN_THROTTLE_ENABLED,
Settings.Global.WIFI_SCORE_PARAMS,
Settings.Global.WIFI_SLEEP_POLICY,
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
@@ -704,3 +707,4 @@ public class SettingsBackupTest {
}
}
+
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 5599ee6284da..2f831908f5f9 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -16,7 +16,11 @@
package com.android.internal.os;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
import android.os.Binder;
+import android.os.OsProtoEnums;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -392,6 +396,113 @@ public class BinderCallsStatsTest {
}
@Test
+ public void testDataResetWhenInitialStateSet() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ bcs.setInitialState(true, true);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(0, uidEntries.size());
+ }
+
+ @Test
+ public void testScreenAndChargerInitialStates() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ bcs.setInitialState(true /** screen iteractive */, false);
+
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.CallStat> callStatsList =
+ new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+ assertEquals(true, callStatsList.get(0).screenInteractive);
+ }
+
+ @Test
+ public void testNoDataCollectedOnCharger() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+ .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+ bcs.getBroadcastReceiver().onReceive(null, intent);
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ assertEquals(0, bcs.getUidEntries().size());
+ }
+
+ @Test
+ public void testScreenOff() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_OFF));
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(1, uidEntries.size());
+ BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+ Assert.assertNotNull(uidEntry);
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+ assertEquals(false, callStatsList.get(0).screenInteractive);
+ }
+
+ @Test
+ public void testScreenOn() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(1, uidEntries.size());
+ BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+ Assert.assertNotNull(uidEntry);
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+ assertEquals(true, callStatsList.get(0).screenInteractive);
+ }
+
+ @Test
+ public void testOnCharger() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+ .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+ bcs.getBroadcastReceiver().onReceive(null, intent);
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ assertEquals(0, bcs.getExportedCallStats().size());
+ }
+
+ @Test
+ public void testOnBattery() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+ .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
+ bcs.getBroadcastReceiver().onReceive(null, intent);
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ assertEquals(1, bcs.getExportedCallStats().size());
+ }
+
+ @Test
public void testDumpDoesNotThrowException() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
@@ -419,6 +530,8 @@ public class BinderCallsStatsTest {
public void testGetExportedStatsWhenDetailedTrackingEnabled() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
+ bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
@@ -430,6 +543,7 @@ public class BinderCallsStatsTest {
assertEquals(TEST_UID, stat.uid);
assertEquals("android.os.Binder", stat.className);
assertEquals("1", stat.methodName);
+ assertEquals(true, stat.screenInteractive);
assertEquals(10, stat.cpuTimeMicros);
assertEquals(10, stat.maxCpuTimeMicros);
assertEquals(20, stat.latencyMicros);
@@ -462,11 +576,15 @@ public class BinderCallsStatsTest {
TestBinderCallsStats() {
// Make random generator not random.
- super(new Random() {
- int mCallCount = 0;
-
- public int nextInt() {
- return mCallCount++;
+ super(new Injector() {
+ public Random getRandomGenerator() {
+ return new Random() {
+ int mCallCount = 0;
+
+ public int nextInt() {
+ return mCallCount++;
+ }
+ };
}
});
}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index ffe6abc13aae..53e9826998f3 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -20,6 +20,7 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.Canvas.VertexMode;
import android.text.GraphicsOperations;
import android.text.MeasuredParagraph;
@@ -44,6 +45,7 @@ public abstract class BaseCanvas {
* freed by NativeAllocation.
* @hide
*/
+ @UnsupportedAppUsage
protected long mNativeCanvasWrapper;
/**
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index c6f841505e03..cea6c1c5d125 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,6 +21,7 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.content.res.ResourcesImpl;
import android.os.Parcel;
@@ -59,6 +60,7 @@ public final class Bitmap implements Parcelable {
private static final long NATIVE_ALLOCATION_SIZE = 32;
// Convenience for JNI access
+ @UnsupportedAppUsage
private final long mNativePtr;
/**
@@ -75,9 +77,13 @@ public final class Bitmap implements Parcelable {
*/
private boolean mRequestPremultiplied;
+ @UnsupportedAppUsage
private byte[] mNinePatchChunk; // may be null
+ @UnsupportedAppUsage
private NinePatch.InsetStruct mNinePatchInsets; // may be null
+ @UnsupportedAppUsage
private int mWidth;
+ @UnsupportedAppUsage
private int mHeight;
private boolean mRecycled;
@@ -99,11 +105,13 @@ public final class Bitmap implements Parcelable {
* density when running old apps.
* @hide
*/
+ @UnsupportedAppUsage
public static void setDefaultDensity(int density) {
sDefaultDensity = density;
}
@SuppressWarnings("deprecation")
+ @UnsupportedAppUsage
static int getDefaultDensity() {
if (sDefaultDensity >= 0) {
return sDefaultDensity;
@@ -117,6 +125,7 @@ public final class Bitmap implements Parcelable {
* int (pointer).
*/
// called from JNI
+ @UnsupportedAppUsage
Bitmap(long nativeBitmap, int width, int height, int density, boolean requestPremultiplied,
byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
if (nativeBitmap == 0) {
@@ -158,6 +167,7 @@ public final class Bitmap implements Parcelable {
* width/height values
*/
@SuppressWarnings("unused") // called from JNI
+ @UnsupportedAppUsage
void reinit(int width, int height, boolean requestPremultiplied) {
mWidth = width;
mHeight = height;
@@ -328,6 +338,7 @@ public final class Bitmap implements Parcelable {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setNinePatchChunk(byte[] chunk) {
mNinePatchChunk = chunk;
}
@@ -523,6 +534,7 @@ public final class Bitmap implements Parcelable {
*/
HARDWARE (7);
+ @UnsupportedAppUsage
final int nativeInt;
private static Config sConfigs[] = {
@@ -533,6 +545,7 @@ public final class Bitmap implements Parcelable {
this.nativeInt = ni;
}
+ @UnsupportedAppUsage
static Config nativeToConfig(int ni) {
return sConfigs[ni];
}
@@ -667,6 +680,7 @@ public final class Bitmap implements Parcelable {
*
* @hide
*/
+ @UnsupportedAppUsage
public Bitmap createAshmemBitmap() {
checkRecycled("Can't copy a recycled bitmap");
noteHardwareBitmapSlowCall();
@@ -685,6 +699,7 @@ public final class Bitmap implements Parcelable {
*
* @hide
*/
+ @UnsupportedAppUsage
public Bitmap createAshmemBitmap(Config config) {
checkRecycled("Can't copy a recycled bitmap");
noteHardwareBitmapSlowCall();
@@ -703,6 +718,7 @@ public final class Bitmap implements Parcelable {
* currently PIXEL_FORMAT_RGBA_8888 is the only supported format
* @hide
*/
+ @UnsupportedAppUsage
public static Bitmap createHardwareBitmap(@NonNull GraphicBuffer graphicBuffer) {
return nativeCreateHardwareBitmap(graphicBuffer);
}
@@ -1500,6 +1516,7 @@ public final class Bitmap implements Parcelable {
/**
* @hide
*/
+ @UnsupportedAppUsage
static public int scaleFromDensity(int size, int sdensity, int tdensity) {
if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) {
return size;
@@ -2032,6 +2049,7 @@ public final class Bitmap implements Parcelable {
* @return {@link GraphicBuffer} which is internally used by hardware bitmap
* @hide
*/
+ @UnsupportedAppUsage
public GraphicBuffer createGraphicBufferHandle() {
return nativeCreateGraphicBufferHandle(mNativePtr);
}
@@ -2049,6 +2067,7 @@ public final class Bitmap implements Parcelable {
private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
private static native long nativeGetNativeFinalizer();
private static native void nativeRecycle(long nativeBitmap);
+ @UnsupportedAppUsage
private static native void nativeReconfigure(long nativeBitmap, int width, int height,
int config, boolean isPremultiplied);
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 7ea35e73619a..adab1a9c5c51 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -20,6 +20,7 @@ import static android.graphics.BitmapFactory.Options.validate;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
@@ -831,11 +832,15 @@ public class BitmapFactory {
return decodeFileDescriptor(fd, null, null);
}
+ @UnsupportedAppUsage
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
+ @UnsupportedAppUsage
private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
Rect padding, Options opts);
+ @UnsupportedAppUsage
private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
+ @UnsupportedAppUsage
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
private static native boolean nativeIsSeekable(FileDescriptor fd);
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 2da27c7dfdbf..9b5027d71ad6 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -15,6 +15,7 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import java.io.FileDescriptor;
@@ -165,6 +166,7 @@ public final class BitmapRegionDecoder {
This can be called from JNI code.
*/
+ @UnsupportedAppUsage
private BitmapRegionDecoder(long decoder) {
mNativeBitmapRegionDecoder = decoder;
mRecycled = false;
@@ -267,6 +269,7 @@ public final class BitmapRegionDecoder {
private static native int nativeGetHeight(long lbm);
private static native void nativeClean(long lbm);
+ @UnsupportedAppUsage
private static native BitmapRegionDecoder nativeNewInstance(
byte[] data, int offset, int length, boolean isShareable);
private static native BitmapRegionDecoder nativeNewInstance(
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 5577f53ee28b..bcf72298ba30 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
/**
* Shader used to draw a bitmap as a texture. The bitmap can be repeated or
@@ -28,9 +29,12 @@ public class BitmapShader extends Shader {
* @hide
*/
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ @UnsupportedAppUsage
public Bitmap mBitmap;
+ @UnsupportedAppUsage
private int mTileX;
+ @UnsupportedAppUsage
private int mTileY;
/**
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index 60588d07196c..33889410a54b 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
+
/**
* A camera instance can be used to compute 3D transformations and
* generate a matrix that can be applied, for instance, on a
@@ -174,5 +176,6 @@ public class Camera {
private native void nativeGetMatrix(long native_matrix);
private native void nativeApplyToCanvas(long native_canvas);
+ @UnsupportedAppUsage
long native_instance;
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a465eeafa287..ef58c8f2bd16 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import dalvik.annotation.optimization.CriticalNative;
@@ -54,6 +55,7 @@ public class Canvas extends BaseCanvas {
public static boolean sCompatibilitySetBitmap = false;
/** @hide */
+ @UnsupportedAppUsage
public long getNativeCanvasWrapper() {
return mNativeCanvasWrapper;
}
@@ -62,6 +64,7 @@ public class Canvas extends BaseCanvas {
public boolean isRecordingFor(Object o) { return false; }
// may be null
+ @UnsupportedAppUsage
private Bitmap mBitmap;
// optional field set by the caller
@@ -123,6 +126,7 @@ public class Canvas extends BaseCanvas {
}
/** @hide */
+ @UnsupportedAppUsage
public Canvas(long nativeCanvas) {
if (nativeCanvas == 0) {
throw new IllegalStateException();
@@ -141,6 +145,7 @@ public class Canvas extends BaseCanvas {
* @hide
*/
@Deprecated
+ @UnsupportedAppUsage
protected GL getGL() {
return null;
}
@@ -269,6 +274,7 @@ public class Canvas extends BaseCanvas {
}
/** @hide */
+ @UnsupportedAppUsage
public void setScreenDensity(int density) {
mScreenDensity = density;
}
@@ -1263,6 +1269,7 @@ public class Canvas extends BaseCanvas {
*
* @hide
*/
+ @UnsupportedAppUsage
public void release() {
mNativeCanvasWrapper = 0;
if (mFinalizer != null) {
@@ -1276,6 +1283,7 @@ public class Canvas extends BaseCanvas {
*
* @hide
*/
+ @UnsupportedAppUsage
public static void freeCaches() {
nFreeCaches();
}
@@ -1285,6 +1293,7 @@ public class Canvas extends BaseCanvas {
*
* @hide
*/
+ @UnsupportedAppUsage
public static void freeTextLayoutCaches() {
nFreeTextLayoutCaches();
}
@@ -1591,9 +1600,9 @@ public class Canvas extends BaseCanvas {
* Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be
* drawn. The circle will be filled or framed based on the Style in the paint.
*
- * @param cx The x-coordinate of the center of the cirle to be drawn
- * @param cy The y-coordinate of the center of the cirle to be drawn
- * @param radius The radius of the cirle to be drawn
+ * @param cx The x-coordinate of the center of the circle to be drawn
+ * @param cy The y-coordinate of the center of the circle to be drawn
+ * @param radius The radius of the circle to be drawn
* @param paint The paint used to draw the circle
*/
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index ea3886cde274..1275e0827580 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import com.android.internal.util.VirtualRefBasePtr;
/**
@@ -26,10 +27,12 @@ public final class CanvasProperty<T> {
private VirtualRefBasePtr mProperty;
+ @UnsupportedAppUsage
public static CanvasProperty<Float> createFloat(float initialValue) {
return new CanvasProperty<Float>(nCreateFloat(initialValue));
}
+ @UnsupportedAppUsage
public static CanvasProperty<Paint> createPaint(Paint initialValue) {
return new CanvasProperty<Paint>(nCreatePaint(initialValue.getNativeInstance()));
}
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 9201a2e2310e..0f7980cc32e4 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -18,6 +18,7 @@ package android.graphics;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
/**
* A color filter that transforms colors through a 4x5 color matrix. This filter
@@ -26,6 +27,7 @@ import android.annotation.Nullable;
* @see ColorMatrix
*/
public class ColorMatrixColorFilter extends ColorFilter {
+ @UnsupportedAppUsage
private final ColorMatrix mMatrix = new ColorMatrix();
/**
@@ -76,6 +78,7 @@ public class ColorMatrixColorFilter extends ColorFilter {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setColorMatrix(@Nullable ColorMatrix matrix) {
discardNativeInstance();
if (matrix == null) {
@@ -104,6 +107,7 @@ public class ColorMatrixColorFilter extends ColorFilter {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setColorMatrixArray(@Nullable float[] array) {
// called '...Array' so that passing null isn't ambiguous
discardNativeInstance();
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index c69eb32148c0..229923ca8202 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
import android.text.TextUtils;
@@ -51,16 +52,19 @@ public class FontFamily {
/**
* @hide
*/
+ @UnsupportedAppUsage
public long mNativePtr;
// Points native font family builder. Must be zero after freezing this family.
private long mBuilderPtr;
+ @UnsupportedAppUsage
public FontFamily() {
mBuilderPtr = nInitBuilder(null, 0);
mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
}
+ @UnsupportedAppUsage
public FontFamily(@Nullable String[] langs, int variant) {
final String langsString;
if (langs == null || langs.length == 0) {
@@ -80,6 +84,7 @@ public class FontFamily {
* @return boolean returns false if some error happens in native code, e.g. broken font file is
* passed, etc.
*/
+ @UnsupportedAppUsage
public boolean freeze() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen");
@@ -93,6 +98,7 @@ public class FontFamily {
return mNativePtr != 0;
}
+ @UnsupportedAppUsage
public void abortCreation() {
if (mBuilderPtr == 0) {
throw new IllegalStateException("This FontFamily is already frozen or abandoned");
@@ -122,6 +128,7 @@ public class FontFamily {
}
}
+ @UnsupportedAppUsage
public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
int weight, int italic) {
if (mBuilderPtr == 0) {
@@ -147,6 +154,7 @@ public class FontFamily {
* using the OS/2 table in the font.
* @return
*/
+ @UnsupportedAppUsage
public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
boolean isAsset, int ttcIndex, int weight, int isItalic,
FontVariationAxis[] axes) {
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 431d0e0eb7b4..e3e8380716f3 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
import android.text.FontConfig;
import android.util.Xml;
@@ -37,6 +38,7 @@ import java.util.regex.Pattern;
public class FontListParser {
/* Parse fallback list (no names) */
+ @UnsupportedAppUsage
public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
try {
XmlPullParser parser = Xml.newPullParser();
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index 53d21776eed7..74086830fae2 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -53,6 +54,7 @@ public class GraphicBuffer implements Parcelable {
private final int mFormat;
private final int mUsage;
// Note: do not rename, this field is used by native code
+ @UnsupportedAppUsage
private final long mNativeObject;
// These two fields are only used by lock/unlockCanvas()
@@ -84,6 +86,7 @@ public class GraphicBuffer implements Parcelable {
/**
* Private use only. See {@link #create(int, int, int, int)}.
*/
+ @UnsupportedAppUsage
private GraphicBuffer(int width, int height, int format, int usage, long nativeObject) {
mWidth = width;
mHeight = height;
@@ -96,6 +99,7 @@ public class GraphicBuffer implements Parcelable {
* For SurfaceControl JNI.
* @hide
*/
+ @UnsupportedAppUsage
public static GraphicBuffer createFromExisting(int width, int height,
int format, int usage, long unwrappedNativeObject) {
long nativeObject = nWrapGraphicBuffer(unwrappedNativeObject);
@@ -274,6 +278,7 @@ public class GraphicBuffer implements Parcelable {
nWriteGraphicBufferToParcel(mNativeObject, dest);
}
+ @UnsupportedAppUsage
public static final Parcelable.Creator<GraphicBuffer> CREATOR =
new Parcelable.Creator<GraphicBuffer>() {
public GraphicBuffer createFromParcel(Parcel in) {
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 098f10003b8a..7bed1ac68fa0 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -28,6 +28,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
@@ -1856,6 +1857,7 @@ public final class ImageDecoder implements AutoCloseable {
* Private method called by JNI.
*/
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
private int postProcessAndRelease(@NonNull Canvas canvas) {
try {
return mPostProcessor.onPostProcess(canvas);
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 43fd2708ee3e..9546a4aec330 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
+
public class ImageFormat {
/*
* these constants are chosen to be binary compatible with their previous
@@ -103,6 +105,7 @@ public class ImageFormat {
*
* @hide
*/
+ @UnsupportedAppUsage
public static final int Y8 = 0x20203859;
/**
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 1578ffb873f0..62a890ff4f0b 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -22,6 +22,7 @@
package android.graphics;
import android.annotation.ColorInt;
+import android.annotation.UnsupportedAppUsage;
/**
* A color filter that can be used to simulate simple lighting effects.
@@ -72,6 +73,7 @@ public class LightingColorFilter extends ColorFilter {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setColorMultiply(@ColorInt int mul) {
if (mMul != mul) {
mMul = mul;
@@ -97,6 +99,7 @@ public class LightingColorFilter extends ColorFilter {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setColorAdd(@ColorInt int add) {
if (mAdd != add) {
mAdd = add;
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 7139efec9337..7e6fc353cf1e 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
public class LinearGradient extends Shader {
@@ -31,15 +32,24 @@ public class LinearGradient extends Shader {
*/
private int mType;
+ @UnsupportedAppUsage
private float mX0;
+ @UnsupportedAppUsage
private float mY0;
+ @UnsupportedAppUsage
private float mX1;
+ @UnsupportedAppUsage
private float mY1;
+ @UnsupportedAppUsage
private int[] mColors;
+ @UnsupportedAppUsage
private float[] mPositions;
+ @UnsupportedAppUsage
private int mColor0;
+ @UnsupportedAppUsage
private int mColor1;
+ @UnsupportedAppUsage
private TileMode mTileMode;
/**
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 486070c99e3f..f8cb366c7b92 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -21,6 +21,7 @@ import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
+import android.annotation.UnsupportedAppUsage;
import java.io.PrintWriter;
/**
@@ -39,6 +40,7 @@ public class Matrix {
public static final int MPERSP_2 = 8; //!< use with getValues/setValues
/** @hide */
+ @UnsupportedAppUsage
public final static Matrix IDENTITY_MATRIX = new Matrix() {
void oops() {
throw new IllegalStateException("Matrix can not be modified");
@@ -231,6 +233,7 @@ public class Matrix {
/**
* @hide
*/
+ @UnsupportedAppUsage
public final long native_instance;
/**
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 83857bece930..4c953b5397e0 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import java.io.FileInputStream;
@@ -25,8 +26,10 @@ import java.io.InputStream;
* @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
*/
public class Movie {
+ @UnsupportedAppUsage
private long mNativeMovie;
+ @UnsupportedAppUsage
private Movie(long nativeMovie) {
if (nativeMovie == 0) {
throw new RuntimeException("native movie creation failed");
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index b6a209f25df9..800247af4927 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
+
/**
* The NinePatch class permits drawing a bitmap in nine or more sections.
* Essentially, it allows the creation of custom graphics that will scale the
@@ -41,6 +43,7 @@ public class NinePatch {
*/
public static class InsetStruct {
@SuppressWarnings({"UnusedDeclaration"}) // called from JNI
+ @UnsupportedAppUsage
InsetStruct(int opticalLeft, int opticalTop, int opticalRight, int opticalBottom,
int outlineLeft, int outlineTop, int outlineRight, int outlineBottom,
float outlineRadius, int outlineAlpha, float decodeScale) {
@@ -77,6 +80,7 @@ public class NinePatch {
}
}
+ @UnsupportedAppUsage
private final Bitmap mBitmap;
/**
@@ -84,6 +88,7 @@ public class NinePatch {
*
* @hide
*/
+ @UnsupportedAppUsage
public long mNativeChunk;
private Paint mPaint;
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 1c85df0de590..98c990a71601 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Drawable;
import java.lang.annotation.Retention;
@@ -66,6 +67,7 @@ public final class Outline {
public Path mPath;
/** @hide */
+ @UnsupportedAppUsage
public final Rect mRect = new Rect();
/** @hide */
public float mRadius = RADIUS_UNDEFINED;
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42dac38affba..9dab53680aa9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
import android.os.LocaleList;
import android.text.GraphicsOperations;
@@ -44,6 +45,7 @@ import java.util.Locale;
*/
public class Paint {
+ @UnsupportedAppUsage
private long mNativePaint;
private long mNativeShader;
private long mNativeColorFilter;
@@ -61,6 +63,7 @@ public class Paint {
private MaskFilter mMaskFilter;
private PathEffect mPathEffect;
private Shader mShader;
+ @UnsupportedAppUsage
private Typeface mTypeface;
private Xfermode mXfermode;
@@ -618,6 +621,7 @@ public class Paint {
}
/** @hide */
+ @UnsupportedAppUsage
public void setCompatibilityScaling(float factor) {
if (factor == 1.0) {
mHasCompatScaling = false;
@@ -635,6 +639,7 @@ public class Paint {
*
* @hide
*/
+ @UnsupportedAppUsage
public long getNativeInstance() {
long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
if (newNativeShader != mNativeShader) {
@@ -1718,6 +1723,7 @@ public class Paint {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setHyphenEdit(int hyphen) {
nSetHyphenEdit(mNativePaint, hyphen);
}
@@ -2261,6 +2267,7 @@ public class Paint {
* @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int)
* @hide
*/
+ @UnsupportedAppUsage
public float getTextRunAdvances(char[] chars, int index, int count,
int contextIndex, int contextCount, boolean isRtl, float[] advances,
int advancesIndex) {
@@ -2451,6 +2458,7 @@ public class Paint {
* @return the offset of the next position, or -1
* @hide
*/
+ @UnsupportedAppUsage
public int getTextRunCursor(char[] text, int contextStart, int contextLength,
int dir, int offset, int cursorOpt) {
int contextEnd = contextStart + contextLength;
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 1652f6459f0d..405ab0bbcb34 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,6 +20,7 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UnsupportedAppUsage;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -46,10 +47,12 @@ public class Path {
/**
* @hide
*/
+ @UnsupportedAppUsage
public boolean isSimplePath = true;
/**
* @hide
*/
+ @UnsupportedAppUsage
public Region rects;
private Direction mLastDirection = null;
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index f2d0227645a9..f7acb11c8811 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import java.io.InputStream;
import java.io.OutputStream;
@@ -32,6 +33,7 @@ import java.io.OutputStream;
*/
public class Picture {
private PictureCanvas mRecordingCanvas;
+ @UnsupportedAppUsage
private long mNativePicture;
private boolean mRequiresHwAcceleration;
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index d7d3049b0efa..fba50430f2ef 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
+
/**
* <p>This class contains the list of alpha compositing and blending modes
* that can be passed to {@link PorterDuffXfermode}, a specialized implementation
@@ -364,6 +366,7 @@ public class PorterDuff {
/**
* @hide
*/
+ @UnsupportedAppUsage
public final int nativeInt;
}
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 88a63223ea40..6665220c293c 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -18,6 +18,7 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
/**
* A color filter that can be used to tint the source pixels using a single
@@ -50,6 +51,7 @@ public class PorterDuffColorFilter extends ColorFilter {
* @hide
*/
@ColorInt
+ @UnsupportedAppUsage
public int getColor() {
return mColor;
}
@@ -62,6 +64,7 @@ public class PorterDuffColorFilter extends ColorFilter {
*
* @hide
*/
+ @UnsupportedAppUsage
public PorterDuff.Mode getMode() {
return mMode;
}
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index f4b11917a415..41d26281b723 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.ColorInt;
+import android.annotation.UnsupportedAppUsage;
public class RadialGradient extends Shader {
@@ -31,14 +32,22 @@ public class RadialGradient extends Shader {
*/
private int mType;
+ @UnsupportedAppUsage
private float mX;
+ @UnsupportedAppUsage
private float mY;
+ @UnsupportedAppUsage
private float mRadius;
+ @UnsupportedAppUsage
private int[] mColors;
+ @UnsupportedAppUsage
private float[] mPositions;
+ @UnsupportedAppUsage
private int mCenterColor;
+ @UnsupportedAppUsage
private int mEdgeColor;
+ @UnsupportedAppUsage
private TileMode mTileMode;
/**
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 56a6820f4749..4fec33f19d92 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -206,6 +207,7 @@ public final class Rect implements Parcelable {
* Print short representation to given writer.
* @hide
*/
+ @UnsupportedAppUsage
public void printShortString(@NonNull PrintWriter pw) {
pw.print('['); pw.print(left); pw.print(',');
pw.print(top); pw.print("]["); pw.print(right);
@@ -694,6 +696,7 @@ public final class Rect implements Parcelable {
* Scales up the rect by the given scale.
* @hide
*/
+ @UnsupportedAppUsage
public void scale(float scale) {
if (scale != 1.0f) {
left = (int) (left * scale + 0.5f);
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index b27faddea4c1..29d936778d44 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
@@ -31,6 +32,7 @@ public class Region implements Parcelable {
/**
* @hide
*/
+ @UnsupportedAppUsage
public long mNativeRegion;
// the native values for these must match up with the enum in SkRegion.h
@@ -49,6 +51,7 @@ public class Region implements Parcelable {
/**
* @hide
*/
+ @UnsupportedAppUsage
public final int nativeInt;
}
@@ -239,6 +242,7 @@ public class Region implements Parcelable {
*
* @hide
*/
+ @UnsupportedAppUsage
public void scale(float scale) {
scale(scale, null);
}
@@ -333,6 +337,7 @@ public class Region implements Parcelable {
*
* @hide
*/
+ @UnsupportedAppUsage
public void recycle() {
setEmpty();
sPool.release(this);
@@ -406,6 +411,7 @@ public class Region implements Parcelable {
/* add dummy parameter so constructor can be called from jni without
triggering 'not cloneable' exception */
+ @UnsupportedAppUsage
private Region(long ni, int dummy) {
this(ni);
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 40288f5ec8af..40bcc9e26a3d 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -18,6 +18,7 @@ package android.graphics;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import libcore.util.NativeAllocationRegistry;
@@ -72,6 +73,7 @@ public class Shader {
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
}
+ @UnsupportedAppUsage
final int nativeInt;
}
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 1eebd2647753..99f440d599cb 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -68,13 +69,17 @@ import java.lang.ref.WeakReference;
*/
public class SurfaceTexture {
private final Looper mCreatorLooper;
+ @UnsupportedAppUsage
private Handler mOnFrameAvailableHandler;
/**
* These fields are used by native code, do not access or modify.
*/
+ @UnsupportedAppUsage
private long mSurfaceTexture;
+ @UnsupportedAppUsage
private long mProducer;
+ @UnsupportedAppUsage
private long mFrameAvailableListener;
private boolean mIsSingleBuffered;
@@ -378,6 +383,7 @@ public class SurfaceTexture {
* This method is invoked from native code only.
*/
@SuppressWarnings({"UnusedDeclaration"})
+ @UnsupportedAppUsage
private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) {
SurfaceTexture st = weakSelf.get();
if (st != null) {
@@ -405,6 +411,7 @@ public class SurfaceTexture {
private native void nativeSetDefaultBufferSize(int width, int height);
private native void nativeUpdateTexImage();
private native void nativeReleaseTexImage();
+ @UnsupportedAppUsage
private native int nativeDetachFromGLContext();
private native int nativeAttachToGLContext(int texName);
private native void nativeRelease();
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index b6b80b4f57dc..f944d85d3cf2 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
public class SweepGradient extends Shader {
@@ -31,11 +32,17 @@ public class SweepGradient extends Shader {
*/
private int mType;
+ @UnsupportedAppUsage
private float mCx;
+ @UnsupportedAppUsage
private float mCy;
+ @UnsupportedAppUsage
private int[] mColors;
+ @UnsupportedAppUsage
private float[] mPositions;
+ @UnsupportedAppUsage
private int mColor0;
+ @UnsupportedAppUsage
private int mColor1;
/**
diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java
index d0c1438285a8..d81c491e07e0 100644
--- a/graphics/java/android/graphics/TableMaskFilter.java
+++ b/graphics/java/android/graphics/TableMaskFilter.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
+
/**
* @hide
*/
@@ -32,6 +34,7 @@ public class TableMaskFilter extends MaskFilter {
native_instance = ni;
}
+ @UnsupportedAppUsage
public static TableMaskFilter CreateClipTable(int min, int max) {
return new TableMaskFilter(nativeNewClip(min, max));
}
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index 36a2275738c2..0ae2c703c21c 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -16,12 +16,14 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
/**
* @hide
*/
public class TemporaryBuffer {
+ @UnsupportedAppUsage
public static char[] obtain(int len) {
char[] buf;
@@ -37,6 +39,7 @@ public class TemporaryBuffer {
return buf;
}
+ @UnsupportedAppUsage
public static void recycle(char[] temp) {
if (temp.length > 1000) return;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 18dd97f8acef..522d7a51e69b 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,6 +25,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
import android.net.Uri;
@@ -93,6 +94,7 @@ public class Typeface {
/** The NORMAL style of the default monospace typeface. */
public static final Typeface MONOSPACE;
+ @UnsupportedAppUsage
static Typeface[] sDefaults;
/**
@@ -119,12 +121,15 @@ public class Typeface {
private static final Object sDynamicCacheLock = new Object();
static Typeface sDefaultTypeface;
+ @UnsupportedAppUsage
static final Map<String, Typeface> sSystemFontMap;
+ @UnsupportedAppUsage
static final Map<String, FontFamily[]> sSystemFallbackMap;
/**
* @hide
*/
+ @UnsupportedAppUsage
public long native_instance;
/** @hide */
@@ -139,6 +144,7 @@ public class Typeface {
public static final int BOLD_ITALIC = 3;
/** @hide */ public static final int STYLE_MASK = 0x03;
+ @UnsupportedAppUsage
private @Style int mStyle = 0;
/**
@@ -162,6 +168,7 @@ public class Typeface {
private int[] mSupportedAxes;
private static final int[] EMPTY_AXES = {};
+ @UnsupportedAppUsage
private static void setDefault(Typeface t) {
sDefaultTypeface = t;
nativeSetDefault(t.native_instance);
@@ -891,6 +898,7 @@ public class Typeface {
*
* @param families array of font families
*/
+ @UnsupportedAppUsage
private static Typeface createFromFamilies(FontFamily[] families) {
long[] ptrArray = new long[families.length];
for (int i = 0; i < families.length; i++) {
@@ -904,6 +912,7 @@ public class Typeface {
* This method is used by supportlib-v27.
* TODO: Remove private API use in supportlib: http://b/72665240
*/
+ @UnsupportedAppUsage
private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
int italic) {
return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
@@ -922,6 +931,7 @@ public class Typeface {
* closest to the regular weight and upright font is used.
* @param families array of font families
*/
+ @UnsupportedAppUsage
private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
String fallbackName, int weight, int italic) {
FontFamily[] fallback = sSystemFallbackMap.get(fallbackName);
@@ -939,6 +949,7 @@ public class Typeface {
}
// don't allow clients to call this directly
+ @UnsupportedAppUsage
private Typeface(long ni) {
if (ni == 0) {
throw new RuntimeException("native typeface cannot be made");
@@ -1197,7 +1208,9 @@ public class Typeface {
// TODO: clean up: change List<FontVariationAxis> to FontVariationAxis[]
private static native long nativeCreateFromTypefaceWithVariation(
long native_instance, List<FontVariationAxis> axes);
+ @UnsupportedAppUsage
private static native long nativeCreateWeightAlias(long native_instance, int weight);
+ @UnsupportedAppUsage
private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
private static native int[] nativeGetSupportedAxes(long native_instance);
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index a5da5d09ebaf..6f4adfde7ff9 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -21,6 +21,8 @@
package android.graphics;
+import android.annotation.UnsupportedAppUsage;
+
/**
* Xfermode is the base class for objects that are called to implement custom
* "transfer-modes" in the drawing pipeline. The static function Create(Modes)
@@ -30,5 +32,6 @@ package android.graphics;
*/
public class Xfermode {
static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
+ @UnsupportedAppUsage
int porterDuffMode = DEFAULT;
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 4f467d9aabae..3aaec3123d7d 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -19,6 +19,7 @@ package android.graphics.drawable;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -562,6 +563,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
* callback, so no need to post.
*/
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
private void onAnimationEnd() {
if (mAnimationCallbacks != null) {
for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index d714ca830976..b29fd4db5803 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.content.res.Resources;
@@ -202,11 +203,13 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
}
+ @UnsupportedAppUsage
public void setFramesCount(int framesCount) {
mState.mFramesCount = framesCount;
mIncrement = 360.0f / mState.mFramesCount;
}
+ @UnsupportedAppUsage
public void setFramesDuration(int framesDuration) {
mState.mFrameDuration = framesDuration;
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 3ed6a788b640..00380c5d0d95 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -20,6 +20,7 @@ import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -66,6 +67,7 @@ public class AnimatedStateListDrawable extends StateListDrawable {
private static final String ELEMENT_TRANSITION = "transition";
private static final String ELEMENT_ITEM = "item";
+ @UnsupportedAppUsage
private AnimatedStateListState mState;
/** The currently running transition, if any. */
@@ -558,7 +560,9 @@ public class AnimatedStateListDrawable extends StateListDrawable {
int[] mAnimThemeAttrs;
+ @UnsupportedAppUsage
LongSparseLongArray mTransitions;
+ @UnsupportedAppUsage
SparseIntArray mStateIds;
AnimatedStateListState(@Nullable AnimatedStateListState orig,
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 8e9862c39af0..76f2cfb33824 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -25,6 +25,7 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.Application;
import android.content.pm.ActivityInfo.Config;
@@ -305,6 +306,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
/** Local, mutable animator set. */
+ @UnsupportedAppUsage
private VectorDrawableAnimator mAnimatorSet;
/**
@@ -313,6 +315,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
*/
private Resources mRes;
+ @UnsupportedAppUsage
private AnimatedVectorDrawableState mAnimatedVectorState;
/** The animator set that is parsed from the xml. */
@@ -641,6 +644,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
* Force to animate on UI thread.
* @hide
*/
+ @UnsupportedAppUsage
public void forceAnimationOnUI() {
if (mAnimatorSet instanceof VectorDrawableAnimatorRT) {
VectorDrawableAnimatorRT animator = (VectorDrawableAnimatorRT) mAnimatorSet;
@@ -1771,6 +1775,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
}
// onFinished: should be called from native
+ @UnsupportedAppUsage
private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
set.onAnimationEnd(id);
}
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 0fd1741610ec..57764c2cb693 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -88,6 +89,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
private AnimationState mAnimationState;
/** The current frame, ranging from 0 to {@link #mAnimationState#getChildCount() - 1} */
+ @UnsupportedAppUsage
private int mCurFrame = 0;
/** Whether the drawable has an animation callback posted. */
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 99bed60afbfe..976190109e7c 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -87,9 +88,11 @@ public class BitmapDrawable extends Drawable {
private final Rect mDstRect = new Rect(); // #updateDstRectAndInsetsIfDirty() sets this
+ @UnsupportedAppUsage
private BitmapState mBitmapState;
private PorterDuffColorFilter mTintFilter;
+ @UnsupportedAppUsage
private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
private boolean mDstRectAndInsetsDirty = true;
@@ -237,6 +240,7 @@ public class BitmapDrawable extends Drawable {
}
/** @hide */
+ @UnsupportedAppUsage
public void setBitmap(Bitmap bitmap) {
if (mBitmapState.mBitmap != bitmap) {
mBitmapState.mBitmap = bitmap;
@@ -692,6 +696,7 @@ public class BitmapDrawable extends Drawable {
/**
* @hide only needed by a hack within ProgressBar
*/
+ @UnsupportedAppUsage
public ColorStateList getTint() {
return mBitmapState.mTint;
}
@@ -699,6 +704,7 @@ public class BitmapDrawable extends Drawable {
/**
* @hide only needed by a hack within ProgressBar
*/
+ @UnsupportedAppUsage
public Mode getTintMode() {
return mBitmapState.mTintMode;
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index d925b6b95c66..31fdb025bbc5 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -58,6 +59,7 @@ public class ClipDrawable extends DrawableWrapper {
private final Rect mTmpRect = new Rect();
+ @UnsupportedAppUsage
private ClipState mState;
ClipDrawable() {
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index a601d6d6feba..18b41fa22065 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -20,6 +20,7 @@ import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -52,6 +53,7 @@ import java.io.IOException;
* @attr ref android.R.styleable#ColorDrawable_color
*/
public class ColorDrawable extends Drawable {
+ @UnsupportedAppUsage
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
@@ -336,6 +338,7 @@ public class ColorDrawable extends Drawable {
int[] mThemeAttrs;
int mBaseColor; // base color, independent of setAlpha()
@ViewDebug.ExportedProperty
+ @UnsupportedAppUsage
int mUseColor; // basecolor modulated by setAlpha()
@Config int mChangingConfigurations;
ColorStateList mTint = null;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 986d0c1c1da7..e1f726320839 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -22,6 +22,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -186,6 +187,7 @@ public abstract class Drawable {
private int mLevel = 0;
private @Config int mChangingConfigurations = 0;
private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect()
+ @UnsupportedAppUsage
private WeakReference<Callback> mCallback = null;
private boolean mVisible = true;
@@ -204,6 +206,7 @@ public abstract class Drawable {
*
* @hide
*/
+ @UnsupportedAppUsage
protected int mSrcDensityOverride = 0;
/**
@@ -714,6 +717,7 @@ public abstract class Drawable {
*
* @hide magic!
*/
+ @UnsupportedAppUsage
public boolean isProjected() {
return false;
}
@@ -1389,6 +1393,7 @@ public abstract class Drawable {
* @throws XmlPullParserException
* @throws IOException
*/
+ @UnsupportedAppUsage
void inflateWithAttributes(@NonNull @SuppressWarnings("unused") Resources r,
@NonNull @SuppressWarnings("unused") XmlPullParser parser, @NonNull TypedArray attrs,
@AttrRes int visibleAttr) throws XmlPullParserException, IOException {
@@ -1508,6 +1513,7 @@ public abstract class Drawable {
* Ensures the tint filter is consistent with the current tint color and
* mode.
*/
+ @UnsupportedAppUsage
@Nullable PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,
@Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode) {
if (tint == null || tintMode == null) {
@@ -1613,6 +1619,7 @@ public abstract class Drawable {
*
* @hide
*/
+ @UnsupportedAppUsage
public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
switch (value) {
case 3: return Mode.SRC_OVER;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index e7b383a2f9c8..10f6fae3cf6d 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -54,9 +55,11 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
* to improve the quality at negligible cost.
*/
private static final boolean DEFAULT_DITHER = true;
+ @UnsupportedAppUsage
private DrawableContainerState mDrawableContainerState;
private Rect mHotspotBounds;
private Drawable mCurrDrawable;
+ @UnsupportedAppUsage
private Drawable mLastDrawable;
private int mAlpha = 0xFF;
@@ -686,11 +689,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
@Config int mChildrenChangingConfigurations;
SparseArray<ConstantState> mDrawableFutures;
+ @UnsupportedAppUsage
Drawable[] mDrawables;
int mNumChildren;
boolean mVariablePadding = false;
boolean mCheckedPadding;
+ @UnsupportedAppUsage
Rect mConstantPadding;
boolean mConstantSize = false;
@@ -720,6 +725,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
boolean mAutoMirrored;
ColorFilter mColorFilter;
+ @UnsupportedAppUsage
boolean mHasColorFilter;
ColorStateList mTintList;
@@ -730,6 +736,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
/**
* @hide
*/
+ @UnsupportedAppUsage
protected DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java
index 0ee9071f4d06..bad3791a9c24 100644
--- a/graphics/java/android/graphics/drawable/DrawableInflater.java
+++ b/graphics/java/android/graphics/drawable/DrawableInflater.java
@@ -22,6 +22,7 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -49,6 +50,7 @@ public final class DrawableInflater {
new HashMap<>();
private final Resources mRes;
+ @UnsupportedAppUsage
private final ClassLoader mClassLoader;
/**
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index a907ca5928f5..4ee45bfd8bc8 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -46,6 +47,7 @@ import java.io.IOException;
* Drawable container with only one child element.
*/
public abstract class DrawableWrapper extends Drawable implements Drawable.Callback {
+ @UnsupportedAppUsage
private DrawableWrapperState mState;
private Drawable mDrawable;
private boolean mMutated;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 5629389aaa42..87402342a3c9 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -20,6 +20,7 @@ import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -155,10 +156,14 @@ public class GradientDrawable extends Drawable {
private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f;
private static final float DEFAULT_THICKNESS_RATIO = 9.0f;
+ @UnsupportedAppUsage
private GradientState mGradientState;
+ @UnsupportedAppUsage
private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ @UnsupportedAppUsage
private Rect mPadding;
+ @UnsupportedAppUsage
private Paint mStrokePaint; // optional, set by the caller
private ColorFilter mColorFilter; // optional, set by the caller
private PorterDuffColorFilter mTintFilter;
@@ -1792,27 +1797,46 @@ public class GradientDrawable extends Drawable {
final static class GradientState extends ConstantState {
public @Config int mChangingConfigurations;
+ @UnsupportedAppUsage
public @Shape int mShape = RECTANGLE;
+ @UnsupportedAppUsage
public @GradientType int mGradient = LINEAR_GRADIENT;
+ @UnsupportedAppUsage
public int mAngle = 0;
+ @UnsupportedAppUsage
public Orientation mOrientation;
+ @UnsupportedAppUsage
public ColorStateList mSolidColors;
public ColorStateList mStrokeColors;
+ @UnsupportedAppUsage
public @ColorInt int[] mGradientColors;
public @ColorInt int[] mTempColors; // no need to copy
public float[] mTempPositions; // no need to copy
+ @UnsupportedAppUsage
public float[] mPositions;
+ @UnsupportedAppUsage
public int mStrokeWidth = -1; // if >= 0 use stroking.
+ @UnsupportedAppUsage
public float mStrokeDashWidth = 0.0f;
+ @UnsupportedAppUsage
public float mStrokeDashGap = 0.0f;
+ @UnsupportedAppUsage
public float mRadius = 0.0f; // use this if mRadiusArray is null
+ @UnsupportedAppUsage
public float[] mRadiusArray = null;
+ @UnsupportedAppUsage
public Rect mPadding = null;
+ @UnsupportedAppUsage
public int mWidth = -1;
+ @UnsupportedAppUsage
public int mHeight = -1;
+ @UnsupportedAppUsage
public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO;
+ @UnsupportedAppUsage
public float mThicknessRatio = DEFAULT_THICKNESS_RATIO;
+ @UnsupportedAppUsage
public int mInnerRadius = -1;
+ @UnsupportedAppUsage
public int mThickness = -1;
public boolean mDither = false;
public Insets mOpticalInsets = Insets.NONE;
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 361fe0bffbbc..accc08157ac5 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -21,6 +21,7 @@ import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -99,6 +100,7 @@ public final class Icon implements Parcelable {
private static final int VERSION_STREAM_SERIALIZER = 1;
+ @UnsupportedAppUsage
private final int mType;
private ColorStateList mTintList;
@@ -115,6 +117,7 @@ public final class Icon implements Parcelable {
// TYPE_RESOURCE: package name
// TYPE_URI: uri string
+ @UnsupportedAppUsage
private String mString1;
// TYPE_RESOURCE: resId
@@ -139,6 +142,7 @@ public final class Icon implements Parcelable {
* @return The {@link android.graphics.Bitmap} held by this {@link #TYPE_BITMAP} Icon.
* @hide
*/
+ @UnsupportedAppUsage
public Bitmap getBitmap() {
if (mType != TYPE_BITMAP && mType != TYPE_ADAPTIVE_BITMAP) {
throw new IllegalStateException("called getBitmap() on " + this);
@@ -154,6 +158,7 @@ public final class Icon implements Parcelable {
* @return The length of the compressed bitmap byte array held by this {@link #TYPE_DATA} Icon.
* @hide
*/
+ @UnsupportedAppUsage
public int getDataLength() {
if (mType != TYPE_DATA) {
throw new IllegalStateException("called getDataLength() on " + this);
@@ -168,6 +173,7 @@ public final class Icon implements Parcelable {
* valid compressed bitmap data is found.
* @hide
*/
+ @UnsupportedAppUsage
public int getDataOffset() {
if (mType != TYPE_DATA) {
throw new IllegalStateException("called getDataOffset() on " + this);
@@ -182,6 +188,7 @@ public final class Icon implements Parcelable {
* bitmap data.
* @hide
*/
+ @UnsupportedAppUsage
public byte[] getDataBytes() {
if (mType != TYPE_DATA) {
throw new IllegalStateException("called getDataBytes() on " + this);
@@ -195,6 +202,7 @@ public final class Icon implements Parcelable {
* @return The {@link android.content.res.Resources} for this {@link #TYPE_RESOURCE} Icon.
* @hide
*/
+ @UnsupportedAppUsage
public Resources getResources() {
if (mType != TYPE_RESOURCE) {
throw new IllegalStateException("called getResources() on " + this);
@@ -560,6 +568,7 @@ public final class Icon implements Parcelable {
* Version of createWithResource that takes Resources. Do not use.
* @hide
*/
+ @UnsupportedAppUsage
public static Icon createWithResource(Resources res, @DrawableRes int resId) {
if (res == null) {
throw new IllegalArgumentException("Resource must not be null.");
@@ -692,6 +701,7 @@ public final class Icon implements Parcelable {
}
/** @hide */
+ @UnsupportedAppUsage
public boolean hasTint() {
return (mTintList != null) || (mTintMode != DEFAULT_TINT_MODE);
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index ade42949cdaf..bc8a4cbd7e9d 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -57,6 +58,7 @@ public class InsetDrawable extends DrawableWrapper {
private final Rect mTmpRect = new Rect();
private final Rect mTmpInsetRect = new Rect();
+ @UnsupportedAppUsage
private InsetState mState;
/**
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 4725c2c4c0e5..b4392c8d023f 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -93,6 +94,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
public static final int INSET_UNDEFINED = Integer.MIN_VALUE;
@NonNull
+ @UnsupportedAppUsage
LayerState mLayerState;
private int[] mPaddingL;
@@ -428,6 +430,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
* @param layer The layer to add.
* @return The index of the layer.
*/
+ @UnsupportedAppUsage
int addLayer(@NonNull ChildDrawable layer) {
final LayerState st = mLayerState;
final int N = st.mChildren != null ? st.mChildren.length : 0;
@@ -1739,6 +1742,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
/**
* Ensures the child padding caches are large enough.
*/
+ @UnsupportedAppUsage
void ensurePadding() {
final int N = mLayerState.mNumChildren;
if (mPaddingL != null && mPaddingL.length >= N) {
@@ -1820,6 +1824,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
static class ChildDrawable {
+ @UnsupportedAppUsage
public Drawable mDrawable;
public int[] mThemeAttrs;
public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
@@ -1922,6 +1927,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
private int[] mThemeAttrs;
int mNumChildren;
+ @UnsupportedAppUsage
ChildDrawable[] mChildren;
int mDensity;
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 7f23cea6a806..b53477137331 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -70,6 +71,7 @@ public class NinePatchDrawable extends Drawable {
/** Temporary rect used for density scaling. */
private Rect mTempRect;
+ @UnsupportedAppUsage
private NinePatchState mNinePatchState;
private PorterDuffColorFilter mTintFilter;
private Rect mPadding;
@@ -588,6 +590,7 @@ public class NinePatchDrawable extends Drawable {
@Config int mChangingConfigurations;
// Values loaded during inflation.
+ @UnsupportedAppUsage
NinePatch mNinePatch = null;
ColorStateList mTint = null;
Mode mTintMode = DEFAULT_TINT_MODE;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 266a6ac87d53..1540cc22e295 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -120,6 +121,7 @@ public class RippleDrawable extends LayerDrawable {
private final Rect mDirtyBounds = new Rect();
/** Mirrors mLayerState with some extra information. */
+ @UnsupportedAppUsage
private RippleState mState;
/** The masking layer, e.g. the layer with id R.id.mask. */
@@ -157,6 +159,7 @@ public class RippleDrawable extends LayerDrawable {
private Paint mRipplePaint;
/** Target density of the display into which ripples are drawn. */
+ @UnsupportedAppUsage
private int mDensity;
/** Whether bounds are being overridden. */
@@ -857,6 +860,7 @@ public class RippleDrawable extends LayerDrawable {
mMask.draw(canvas);
}
+ @UnsupportedAppUsage
Paint getRipplePaint() {
if (mRipplePaint == null) {
mRipplePaint = new Paint();
@@ -945,6 +949,7 @@ public class RippleDrawable extends LayerDrawable {
* @param forceSoftware true if RenderThread animations should be disabled, false otherwise
* @hide
*/
+ @UnsupportedAppUsage
public void setForceSoftware(boolean forceSoftware) {
mForceSoftware = forceSoftware;
}
@@ -975,6 +980,7 @@ public class RippleDrawable extends LayerDrawable {
static class RippleState extends LayerState {
int[] mTouchThemeAttrs;
+ @UnsupportedAppUsage
ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
int mMaxRadius = RADIUS_AUTO;
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index c0dfe77cf4f3..db5f082bd853 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.content.res.Resources;
@@ -54,6 +55,7 @@ import java.io.IOException;
public class RotateDrawable extends DrawableWrapper {
private static final int MAX_LEVEL = 10000;
+ @UnsupportedAppUsage
private RotateState mState;
/**
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 51e143baeac4..91ed061e511d 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -66,6 +67,7 @@ public class ScaleDrawable extends DrawableWrapper {
private final Rect mTmpRect = new Rect();
+ @UnsupportedAppUsage
private ScaleState mState;
ScaleDrawable() {
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 67c341287095..8de8f813283f 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -18,6 +18,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -63,6 +64,7 @@ public class StateListDrawable extends DrawableContainer {
private static final boolean DEBUG = false;
+ @UnsupportedAppUsage
private StateListState mStateListState;
private boolean mMutated;
@@ -129,6 +131,7 @@ public class StateListDrawable extends DrawableContainer {
/**
* Updates the constant state from the values in the typed array.
*/
+ @UnsupportedAppUsage
private void updateStateFromTypedArray(TypedArray a) {
final StateListState state = mStateListState;
@@ -206,6 +209,7 @@ public class StateListDrawable extends DrawableContainer {
* @param attrs The attribute set.
* @return An array of state_ attributes.
*/
+ @UnsupportedAppUsage
int[] extractStateSet(AttributeSet attrs) {
int j = 0;
final int numAttrs = attrs.getAttributeCount();
@@ -329,6 +333,7 @@ public class StateListDrawable extends DrawableContainer {
mStateSets = stateSets;
}
+ @UnsupportedAppUsage
int addStateSet(int[] stateSet, Drawable drawable) {
final int pos = addChild(drawable);
mStateSets[pos] = stateSet;
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 3dfd68018a6b..276f3662189b 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -16,6 +16,7 @@
package android.graphics.drawable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -65,10 +66,13 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
private boolean mReverse;
private long mStartTimeMillis;
private int mFrom;
+ @UnsupportedAppUsage
private int mTo;
private int mDuration;
private int mOriginalDuration;
+ @UnsupportedAppUsage
private int mAlpha = 0;
+ @UnsupportedAppUsage
private boolean mCrossFade;
/**
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index b5bd97f74d84..040601c3472a 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -16,6 +16,7 @@ package android.graphics.drawable;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.ComplexColor;
@@ -321,6 +322,7 @@ public class VectorDrawable extends Drawable {
private VectorDrawableState mVectorState;
+ @UnsupportedAppUsage
private PorterDuffColorFilter mTintFilter;
private ColorFilter mColorFilter;
@@ -389,6 +391,7 @@ public class VectorDrawable extends Drawable {
mMutated = false;
}
+ @UnsupportedAppUsage
Object getTargetByName(String name) {
return mVectorState.mVGTargetsMap.get(name);
}
@@ -867,6 +870,7 @@ public class VectorDrawable extends Drawable {
return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
}
+ @UnsupportedAppUsage
void setAllowCaching(boolean allowCaching) {
nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
}
@@ -1498,6 +1502,7 @@ public class VectorDrawable extends Drawable {
}
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
public void setRotation(float rotation) {
if (isTreeValid()) {
nSetRotation(mNativePtr, rotation);
@@ -1510,6 +1515,7 @@ public class VectorDrawable extends Drawable {
}
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
public void setPivotX(float pivotX) {
if (isTreeValid()) {
nSetPivotX(mNativePtr, pivotX);
@@ -1522,6 +1528,7 @@ public class VectorDrawable extends Drawable {
}
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
public void setPivotY(float pivotY) {
if (isTreeValid()) {
nSetPivotY(mNativePtr, pivotY);
@@ -1558,6 +1565,7 @@ public class VectorDrawable extends Drawable {
}
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
public void setTranslateX(float translateX) {
if (isTreeValid()) {
nSetTranslateX(mNativePtr, translateX);
@@ -1570,6 +1578,7 @@ public class VectorDrawable extends Drawable {
}
@SuppressWarnings("unused")
+ @UnsupportedAppUsage
public void setTranslateY(float translateY) {
if (isTreeValid()) {
nSetTranslateY(mNativePtr, translateY);
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 1b7408a03294..2a902c5a6b66 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -18,6 +18,7 @@ package android.graphics.fonts;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -27,8 +28,10 @@ import java.util.regex.Pattern;
* Class that holds information about single font variation axis.
*/
public final class FontVariationAxis {
+ @UnsupportedAppUsage
private final int mTag;
private final String mTagString;
+ @UnsupportedAppUsage
private final float mStyleValue;
/**
diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java
index 4a91705239c1..1836f009f86b 100644
--- a/graphics/java/android/graphics/pdf/PdfRenderer.java
+++ b/graphics/java/android/graphics/pdf/PdfRenderer.java
@@ -19,6 +19,7 @@ package android.graphics.pdf;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Matrix;
@@ -118,6 +119,7 @@ public final class PdfRenderer implements AutoCloseable {
private ParcelFileDescriptor mInput;
+ @UnsupportedAppUsage
private Page mCurrentPage;
/** @hide */
@@ -242,6 +244,7 @@ public final class PdfRenderer implements AutoCloseable {
}
}
+ @UnsupportedAppUsage
private void doClose() {
if (mCurrentPage != null) {
mCurrentPage.close();
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 850230943458..4f4ca3f47009 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -128,7 +128,11 @@ public class KeyStore {
public static final int FLAG_STRONGBOX = 1 << 4;
// States
- public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
+ public enum State {
+ UNLOCKED,
+ LOCKED,
+ UNINITIALIZED
+ };
private int mError = NO_ERROR;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 61c412dac185..bcff5bfa8428 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1794,18 +1794,17 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener
public @interface MediaError {}
/* Do not change these values without updating their counterparts
- * in include/media/mediaplayer2.h!
+ * in include/media/MediaPlayer2Types.h!
*/
/** Unspecified media player info.
* @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNKNOWN = 1;
- /** The player switched to this datas source because it is the
- * next-to-be-played in the playlist.
+ /** The player just started the playback of this datas source.
* @see android.media.MediaPlayer2.EventCallback#onInfo
*/
- public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
+ public static final int MEDIA_INFO_DATA_SOURCE_START = 2;
/** The player just pushed the very first video frame for rendering.
* @see android.media.MediaPlayer2.EventCallback#onInfo
@@ -1820,12 +1819,13 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener
/** The player just completed the playback of this data source.
* @see android.media.MediaPlayer2.EventCallback#onInfo
*/
- public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5;
+ public static final int MEDIA_INFO_DATA_SOURCE_END = 5;
- /** The player just completed the playback of the full playlist.
+ /** The player just completed the playback of all data sources set by {@link #setDataSource},
+ * {@link #setNextDataSource} and {@link #setNextDataSources}.
* @see android.media.MediaPlayer2.EventCallback#onInfo
*/
- public static final int MEDIA_INFO_PLAYLIST_END = 6;
+ public static final int MEDIA_INFO_DATA_SOURCE_LIST_END = 6;
/** The player just prepared a data source.
* @see android.media.MediaPlayer2.EventCallback#onInfo
@@ -1927,11 +1927,11 @@ public abstract class MediaPlayer2 implements SubtitleController.Listener
*/
@IntDef(flag = false, prefix = "MEDIA_INFO", value = {
MEDIA_INFO_UNKNOWN,
- MEDIA_INFO_STARTED_AS_NEXT,
+ MEDIA_INFO_DATA_SOURCE_START,
MEDIA_INFO_VIDEO_RENDERING_START,
MEDIA_INFO_AUDIO_RENDERING_START,
- MEDIA_INFO_PLAYBACK_COMPLETE,
- MEDIA_INFO_PLAYLIST_END,
+ MEDIA_INFO_DATA_SOURCE_END,
+ MEDIA_INFO_DATA_SOURCE_LIST_END,
MEDIA_INFO_PREPARED,
MEDIA_INFO_VIDEO_TRACK_LAGGING,
MEDIA_INFO_BUFFERING_START,
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index d5dd46caca7b..95ba00fa617b 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -210,6 +210,22 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
@Override
void process() {
stayAwake(true);
+
+ // TODO: remove this block when native code sends MEDIA_INFO_DATA_SOURCE_START
+ // when pipeline is created.
+ if (getState() == PLAYER_STATE_PREPARED) {
+ final DataSourceDesc dsd;
+ synchronized (mSrcLock) {
+ dsd = mCurrentDSD;
+ }
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ MediaPlayer2Impl.this, dsd, MEDIA_INFO_DATA_SOURCE_START, 0));
+ }
+ }
+ }
+
_start();
}
});
@@ -246,6 +262,22 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
@Override
void process() {
stayAwake(false);
+
+ // TODO: remove this block when native code allows prepared -> pause
+ // and sends MEDIA_INFO_DATA_SOURCE_START when pipeline is created.
+ if (getState() == PLAYER_STATE_PREPARED) {
+ final DataSourceDesc dsd;
+ synchronized (mSrcLock) {
+ dsd = mCurrentDSD;
+ }
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ MediaPlayer2Impl.this, dsd, MEDIA_INFO_DATA_SOURCE_START, 0));
+ }
+ }
+ }
+
_pause();
}
});
@@ -888,23 +920,30 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
private native void nativeHandleDataSourceCallback(
boolean isCurrent, long srcId, Media2DataSource dataSource);
+ /**
+ * @return true if there is a next data source, false otherwise.
+ */
// This function should be always called on |mHandlerThread|.
- private void prepareNextDataSource() {
+ private boolean prepareNextDataSource() {
if (Looper.myLooper() != mHandlerThread.getLooper()) {
Log.e(TAG, "prepareNextDataSource: called on wrong looper");
}
+ boolean hasNextDSD;
+ synchronized (mSrcLock) {
+ hasNextDSD = (mNextDSDs != null && !mNextDSDs.isEmpty());
+ }
+
int state = getState();
if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
// Current source has not been prepared yet.
- return;
+ return hasNextDSD;
}
synchronized (mSrcLock) {
- if (mNextDSDs == null || mNextDSDs.isEmpty()
- || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
+ if (!hasNextDSD || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
// There is no next source or it's in preparing or prepared state.
- return;
+ return hasNextDSD;
}
try {
@@ -919,9 +958,10 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
// make a new SrcId to obsolete notification for previous one.
mNextSrcId = mSrcIdGenerator++;
mNextSourceState = NEXT_SOURCE_STATE_INIT;
- prepareNextDataSource();
+ return prepareNextDataSource();
}
}
+ return hasNextDSD;
}
// This function should be always called on |mHandlerThread|.
@@ -930,40 +970,48 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
Log.e(TAG, "playNextDataSource: called on wrong looper");
}
+ boolean hasNextDSD = false;
synchronized (mSrcLock) {
- if (mNextDSDs == null || mNextDSDs.isEmpty()) {
- return;
- }
+ if (mNextDSDs != null && !mNextDSDs.isEmpty()) {
+ hasNextDSD = true;
+ if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
+ // Switch to next source only when it has been prepared.
+ mCurrentDSD = mNextDSDs.get(0);
+ mCurrentSrcId = mNextSrcId;
+ mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
+ mNextDSDs.remove(0);
+ mNextSrcId = mSrcIdGenerator++; // make it different from |mCurrentSrcId|
+ mBufferedPercentageNext.set(0);
+ mNextSourceState = NEXT_SOURCE_STATE_INIT;
- if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
- // Switch to next source only when it has been prepared.
- mCurrentDSD = mNextDSDs.get(0);
- mCurrentSrcId = mNextSrcId;
- mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
- mNextDSDs.remove(0);
- mNextSrcId = mSrcIdGenerator++; // make it different from |mCurrentSrcId|
- mBufferedPercentageNext.set(0);
- mNextSourceState = NEXT_SOURCE_STATE_INIT;
+ long srcId = mCurrentSrcId;
+ try {
+ nativePlayNextDataSource(srcId);
+ } catch (Exception e) {
+ Message msg2 = mTaskHandler.obtainMessage(
+ MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+ mTaskHandler.handleMessage(msg2, srcId);
+ // Keep |mNextSourcePlayPending|
+ hasNextDSD = prepareNextDataSource();
+ }
+ if (hasNextDSD) {
+ stayAwake(true);
- long srcId = mCurrentSrcId;
- try {
- nativePlayNextDataSource(srcId);
- } catch (Exception e) {
- Message msg2 = mTaskHandler.obtainMessage(
- MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
- mTaskHandler.handleMessage(msg2, srcId);
- // Keep |mNextSourcePlayPending|
- prepareNextDataSource();
- return;
+ // Now a new current src is playing.
+ // Wait for MEDIA_INFO_DATA_SOURCE_START to prepare next source.
+ mNextSourcePlayPending = false;
+ }
+ } else if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
+ hasNextDSD = prepareNextDataSource();
}
- stayAwake(true);
+ }
+ }
- // Now a new current src is playing.
- // Wait for MEDIA2_INFO_STARTED_AS_NEXT to prepare next source.
- mNextSourcePlayPending = false;
- } else {
- if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
- prepareNextDataSource();
+ if (!hasNextDSD) {
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ MediaPlayer2Impl.this, null, MEDIA_INFO_DATA_SOURCE_LIST_END, 0));
}
}
}
@@ -2767,7 +2815,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0));
}
}
stayAwake(false);
@@ -2869,7 +2917,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
cb.first.execute(() -> cb.second.onError(
mMediaPlayer, dsd, what, extra));
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0));
}
}
stayAwake(false);
@@ -2878,8 +2926,15 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
case MEDIA_INFO:
{
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ mMediaPlayer, dsd, what, extra));
+ }
+ }
+
switch (msg.arg1) {
- case MEDIA_INFO_STARTED_AS_NEXT:
+ case MEDIA_INFO_DATA_SOURCE_START:
if (isCurrentSrcId) {
prepareNextDataSource();
}
@@ -2916,13 +2971,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
}
break;
}
-
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, what, extra));
- }
- }
// No real default action so far.
return;
}
@@ -3034,19 +3082,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
}
switch (what) {
- case MEDIA_INFO:
- if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- // this acquires the wakelock if needed, and sets the client side state
- mp.play();
- }
- }).start();
- Thread.yield();
- }
- break;
-
case MEDIA_DRM_INFO:
// We need to derive mDrmInfoImpl before prepare() returns so processing it here
// before the notification is sent to TaskHandler below. TaskHandler runs in the
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index ff70e9712bcc..a7217ecabc35 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -55,12 +55,6 @@
<intent-filter>
<action android:name="android.service.autofill.AutofillFieldClassificationService" />
</intent-filter>
- <meta-data
- android:name="android.autofill.field_classification.default_algorithm"
- android:resource="@string/autofill_field_classification_default_algorithm" />
- <meta-data
- android:name="android.autofill.field_classification.available_algorithms"
- android:resource="@array/autofill_field_classification_available_algorithms" />
</service>
<library android:name="android.ext.services"/>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index a539b1f2ba60..9d19898dd902 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -17,15 +17,16 @@
package android.ext.services.notification;
import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEGATIVE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
-import android.ext.services.R;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -193,23 +194,27 @@ public class Assistant extends NotificationAssistantService {
if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
ArrayList<Notification.Action> actions =
mSmartActionsHelper.suggestActions(this, sbn);
- if (actions.isEmpty()) {
- return null;
- }
- return createEnqueuedNotificationAdjustment(sbn, actions);
+ ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(this, sbn);
+ return createEnqueuedNotificationAdjustment(sbn, actions, replies);
}
/** A convenience helper for creating an adjustment for an SBN. */
+ @Nullable
private Adjustment createEnqueuedNotificationAdjustment(
@NonNull StatusBarNotification statusBarNotification,
- @NonNull ArrayList<Notification.Action> smartActions) {
+ @NonNull ArrayList<Notification.Action> smartActions,
+ @NonNull ArrayList<CharSequence> smartReplies) {
+ if (smartActions.isEmpty() && smartReplies.isEmpty()) {
+ return null;
+ }
Bundle signals = new Bundle();
signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
+ signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
return new Adjustment(
statusBarNotification.getPackageName(),
statusBarNotification.getKey(),
signals,
- "smart action" /* explanation */,
+ "smart action, reply" /* explanation */,
statusBarNotification.getUserId());
}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index ed5cbabe1ca2..9d33bd9972f1 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
+import android.os.SystemProperties;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -32,13 +33,13 @@ import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
public class SmartActionsHelper {
- private static final ArrayList<Notification.Action> EMPTY_LIST = new ArrayList<>();
+ private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
+ private static final ArrayList<CharSequence> EMPTY_REPLY_LIST = new ArrayList<>();
// If a notification has any of these flags set, it's inelgibile for actions being added.
private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
@@ -49,6 +50,10 @@ public class SmartActionsHelper {
private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
private static final int MAX_ACTIONS_PER_LINK = 1;
private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+ // Allow us to test out smart reply with dumb suggestions, it is disabled by default.
+ // TODO: Removed this once we have the model.
+ private static final String SYS_PROP_SMART_REPLIES_EXPERIMENT =
+ "persist.sys.smart_replies_experiment";
SmartActionsHelper() {}
@@ -62,14 +67,14 @@ public class SmartActionsHelper {
ArrayList<Notification.Action> suggestActions(
@Nullable Context context, @NonNull StatusBarNotification sbn) {
if (!isEligibleForActionAdjustment(sbn)) {
- return EMPTY_LIST;
+ return EMPTY_ACTION_LIST;
}
if (context == null) {
- return EMPTY_LIST;
+ return EMPTY_ACTION_LIST;
}
TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
if (tcm == null) {
- return EMPTY_LIST;
+ return EMPTY_ACTION_LIST;
}
Notification.Action[] actions = sbn.getNotification().actions;
int numOfExistingActions = actions == null ? 0: actions.length;
@@ -79,6 +84,18 @@ public class SmartActionsHelper {
getMostSalientActionText(sbn.getNotification()), maxSmartActions);
}
+ ArrayList<CharSequence> suggestReplies(
+ @Nullable Context context, @NonNull StatusBarNotification sbn) {
+ if (!isEligibleForReplyAdjustment(sbn)) {
+ return EMPTY_REPLY_LIST;
+ }
+ if (context == null) {
+ return EMPTY_REPLY_LIST;
+ }
+ // TODO: replaced this with our model when it is ready.
+ return new ArrayList<>(Arrays.asList("Yes, please", "No, thanks"));
+ }
+
/**
* Returns whether a notification is eligible for action adjustments.
*
@@ -108,6 +125,17 @@ public class SmartActionsHelper {
|| hasInlineReply(notification);
}
+ private boolean isEligibleForReplyAdjustment(@NonNull StatusBarNotification sbn) {
+ if (!SystemProperties.getBoolean(SYS_PROP_SMART_REPLIES_EXPERIMENT, false)) {
+ return false;
+ }
+ Notification notification = sbn.getNotification();
+ if (notification.actions == null) {
+ return false;
+ }
+ return hasInlineReply(sbn.getNotification());
+ }
+
private boolean hasInlineReply(Notification notification) {
Notification.Action[] actions = notification.actions;
if (actions == null) {
@@ -151,7 +179,7 @@ public class SmartActionsHelper {
@NonNull TextClassificationManager tcm, @Nullable CharSequence text,
int maxSmartActions) {
if (TextUtils.isEmpty(text)) {
- return EMPTY_LIST;
+ return EMPTY_ACTION_LIST;
}
TextClassifier textClassifier = tcm.getTextClassifier();
diff --git a/packages/PackageInstaller/res/layout/uninstall_confirm.xml b/packages/PackageInstaller/res/layout/uninstall_confirm.xml
deleted file mode 100644
index 4c8177167464..000000000000
--- a/packages/PackageInstaller/res/layout/uninstall_confirm.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-
-<!--
-
- Defines the layout of the confirmation screen that gets displayed when an
- application is about to be uninstalled. Includes ok and cancel buttons
- to let the uinstallation continue or abort.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:orientation="vertical"
- android:paddingBottom="8dip">
-
- <!-- If an activity was specified, explains what package it's in. -->
- <TextView
- android:id="@+id/activity_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:visibility="gone" />
-
- <!-- The snippet (title & icon) about the application being uninstalled. -->
- <include
- layout="@layout/app_details"
- android:id="@+id/uninstall_activity_snippet" />
-
- <FrameLayout
- android:id="@+id/top_divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="4dip"
- android:paddingStart="16dip"
- android:paddingEnd="16dip" >
- <ProgressBar
- android:id="@+id/progress_bar"
- style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
-
- <!-- uninstall application confirmation text -->
- <TextView
- android:id="@+id/uninstall_confirm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:paddingStart="24dip"
- android:paddingEnd="24dip" />
-
- </LinearLayout>
-
- <!-- OK confirm and cancel buttons. -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:divider="?android:attr/dividerHorizontal"
- android:showDividers="beginning"
- android:paddingTop="16dip">
-
- <LinearLayout
- style="?android:attr/buttonBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:measureWithLargestChild="true">
-
- <LinearLayout android:id="@+id/leftSpacer"
- android:layout_weight="0.25"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone" />
-
- <Button android:id="@+id/cancel_button"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:layout_weight="1"
- android:text="@string/cancel"
- android:maxLines="2"
- style="?android:attr/buttonBarButtonStyle" />
-
- <Button android:id="@+id/ok_button"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_weight="1"
- android:text="@string/ok"
- android:maxLines="2"
- style="?android:attr/buttonBarButtonStyle" />
-
- <LinearLayout android:id="@+id/rightSpacer"
- android:layout_width="0dip"
- android:layout_weight="0.25"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone" />
-
- </LinearLayout>
- </LinearLayout>
-</LinearLayout>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 50d9c17bec23..d30be58d45ce 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -189,9 +189,9 @@
<string name="vpn_settings_not_available" msgid="956841430176985598">"La configuració de la VPN no està disponible per a aquest usuari."</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"La configuració de compartició de xarxa no està disponible per a aquest usuari."</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"La configuració del nom del punt d\'accés no està disponible per a aquest usuari."</string>
- <string name="enable_adb" msgid="7982306934419797485">"Depuració USB"</string>
+ <string name="enable_adb" msgid="7982306934419797485">"Depuració per USB"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"Activa el mode de depuració quan el dispositiu estigui connectat per USB"</string>
- <string name="clear_adb_keys" msgid="4038889221503122743">"Revoca autoritzacions de depuració USB"</string>
+ <string name="clear_adb_keys" msgid="4038889221503122743">"Revoca autoritzacions de depuració per USB"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Drecera per a informe d\'errors"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostra un botó al menú d\'engegada per crear un informe d\'errors"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Pantalla sempre activa"</string>
@@ -251,9 +251,9 @@
<string name="debug_view_attributes" msgid="6485448367803310384">"Inspecció d\'atributs de visualització"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén les dades mòbils sempre actives, fins i tot quan la Wi‑Fi està activada (per canviar de xarxa ràpidament)."</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Fes servir l\'acceleració per maquinari per compartir la xarxa, si està disponible"</string>
- <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració USB?"</string>
- <string name="adb_warning_message" msgid="7316799925425402244">"La depuració USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
- <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració d\'USB dels ordinadors que has autoritzat anteriorment?"</string>
+ <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració per USB?"</string>
+ <string name="adb_warning_message" msgid="7316799925425402244">"La depuració per USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
+ <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració per USB dels ordinadors que has autoritzat anteriorment?"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"Vols permetre la conf. de desenvolupament?"</string>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"Aquesta configuració només està prevista per a usos de desenvolupament. Pot fer que el dispositiu i que les aplicacions s\'interrompin o tinguin un comportament inadequat."</string>
<string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Verifica aplicacions per USB"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 1579d8f9a77a..a90231abf842 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -189,9 +189,9 @@
<string name="vpn_settings_not_available" msgid="956841430176985598">"Nastavení sítě VPN pro tohoto uživatele není dostupné."</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"Nastavení sdíleného připojení pro tohoto uživatele není dostupné."</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"Nastavení přístupového bodu pro tohoto uživatele není dostupné."</string>
- <string name="enable_adb" msgid="7982306934419797485">"Ladění USB"</string>
+ <string name="enable_adb" msgid="7982306934419797485">"Ladění přes USB"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"Povolit režim ladění s připojeným zařízením USB"</string>
- <string name="clear_adb_keys" msgid="4038889221503122743">"Zrušit autorizace k ladění USB"</string>
+ <string name="clear_adb_keys" msgid="4038889221503122743">"Zrušit autorizace k ladění přes USB"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Zástupce hlášení chyb"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Zobrazit v hlavní nabídce tlačítko k vygenerování chybového hlášení"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Nevypínat obrazovku"</string>
@@ -251,9 +251,9 @@
<string name="debug_view_attributes" msgid="6485448367803310384">"Kontrola atributu zobrazení"</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobilní data budou vždy ponechána aktivní, i když bude aktivní Wi-Fi (za účelem rychlého přepínání sítí)."</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Pokud je k dispozici hardwarová akceleraci tetheringu, použít ji"</string>
- <string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění USB?"</string>
+ <string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění přes USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Ladění prostřednictvím rozhraní USB je určeno pouze pro účely vývoje. Použijte je ke kopírování dat mezi počítačem a zařízením, instalaci aplikací do zařízení bez upozornění a čtení dat protokolů."</string>
- <string name="adb_keys_warning_message" msgid="5659849457135841625">"Zrušit přístup k ladění USB ze všech počítačů, které jste v minulosti autorizovali?"</string>
+ <string name="adb_keys_warning_message" msgid="5659849457135841625">"Zrušit přístup k ladění přes USB ze všech počítačů, které jste v minulosti autorizovali?"</string>
<string name="dev_settings_warning_title" msgid="7244607768088540165">"Povolit nastavení pro vývojáře?"</string>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"Tato nastavení jsou určena pouze pro vývojáře. Mohou způsobit rozbití nebo nesprávné fungování zařízení a nainstalovaných aplikací."</string>
<string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Ověřit aplikace z USB"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index ec7bf9884327..04109771ee61 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -165,7 +165,7 @@
<string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> motorraren ezarpenak"</string>
<string name="tts_engine_settings_button" msgid="1030512042040722285">"Abiarazi motorraren ezarpenak"</string>
<string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor hobetsia"</string>
- <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorra"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorrak"</string>
<string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Berrezarri ahots-tonua"</string>
<string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Berrezarri testua esateko ahots-tonu lehenetsia."</string>
<string-array name="tts_rate_entries">
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 5eac8fadfd02..e94dd7383ee3 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -371,10 +371,10 @@
<string name="power_remaining_duration_only_enhanced" msgid="4189311599812296592">"Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"Temps restant en fonction de votre utilisation (<xliff:g id="LEVEL">%2$s</xliff:g>) : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_short" msgid="3463575350656389957">"Temps restant : <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
- <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de l\'utilisation (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de l\'utilisation"</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index d80e4ffab25e..33c5551605c0 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -162,7 +162,7 @@ Shows the global actions dialog (long-press power).
Draws decorations about the screen in software (e.g. rounded corners, cutouts).
-### [com.android.systemui.fingerprint.FingerprintDialogImpl](/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java)
+### [com.android.systemui.biometrics.BiometricDialogImpl](/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java)
Fingerprint UI.
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index e6452e717d5e..fa4c8b51dc45 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -39,6 +39,6 @@
-keep class ** extends androidx.preference.PreferenceFragment
-keep class com.android.systemui.tuner.*
-keep class com.android.systemui.plugins.** {
- public protected *;
+ *;
}
-keep class androidx.core.app.CoreComponentFactory
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
index 8379dbb4826d..ee8d357a0b80 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -28,7 +28,7 @@
<ImageView android:id="@+id/user_avatar"
android:layout_width="@dimen/car_user_switcher_image_avatar_size"
android:layout_height="@dimen/car_user_switcher_image_avatar_size"
- android:background="@drawable/car_button_ripple_background_inverse"
+ android:background="@drawable/car_button_ripple_background_light"
android:gravity="center"/>
<TextView android:id="@+id/user_name"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index aecf494d65a1..19492a08e7a2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -354,7 +354,7 @@
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
- <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+ <item>com.android.systemui.biometrics.BiometricDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
</string-array>
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index a81043e22fe5..6e62b0d575a2 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.fingerprint;
+package com.android.systemui.biometrics;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -31,25 +31,28 @@ import com.android.internal.os.SomeArgs;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.CommandQueue;
-public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+/**
+ * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
+ * FingerprintDialogView).
+ */
+public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
private static final String TAG = "FingerprintDialogImpl";
private static final boolean DEBUG = true;
- protected static final int MSG_SHOW_DIALOG = 1;
- protected static final int MSG_FINGERPRINT_AUTHENTICATED = 2;
- protected static final int MSG_FINGERPRINT_HELP = 3;
- protected static final int MSG_FINGERPRINT_ERROR = 4;
- protected static final int MSG_HIDE_DIALOG = 5;
- protected static final int MSG_BUTTON_NEGATIVE = 6;
- protected static final int MSG_USER_CANCELED = 7;
- protected static final int MSG_BUTTON_POSITIVE = 8;
- protected static final int MSG_CLEAR_MESSAGE = 9;
-
+ private static final int MSG_SHOW_DIALOG = 1;
+ private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
+ private static final int MSG_BIOMETRIC_HELP = 3;
+ private static final int MSG_BIOMETRIC_ERROR = 4;
+ private static final int MSG_HIDE_DIALOG = 5;
+ private static final int MSG_BUTTON_NEGATIVE = 6;
+ private static final int MSG_USER_CANCELED = 7;
+ private static final int MSG_BUTTON_POSITIVE = 8;
private FingerprintDialogView mDialogView;
private WindowManager mWindowManager;
private IBiometricPromptReceiver mReceiver;
private boolean mDialogShowing;
+ private Callback mCallback = new Callback();
private Handler mHandler = new Handler() {
@Override
@@ -58,14 +61,14 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call
case MSG_SHOW_DIALOG:
handleShowDialog((SomeArgs) msg.obj);
break;
- case MSG_FINGERPRINT_AUTHENTICATED:
- handleFingerprintAuthenticated();
+ case MSG_BIOMETRIC_AUTHENTICATED:
+ handleBiometricAuthenticated();
break;
- case MSG_FINGERPRINT_HELP:
- handleFingerprintHelp((String) msg.obj);
+ case MSG_BIOMETRIC_HELP:
+ handleBiometricHelp((String) msg.obj);
break;
- case MSG_FINGERPRINT_ERROR:
- handleFingerprintError((String) msg.obj);
+ case MSG_BIOMETRIC_ERROR:
+ handleBiometricError((String) msg.obj);
break;
case MSG_HIDE_DIALOG:
handleHideDialog((Boolean) msg.obj);
@@ -79,13 +82,33 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call
case MSG_BUTTON_POSITIVE:
handleButtonPositive();
break;
- case MSG_CLEAR_MESSAGE:
- handleClearMessage();
- break;
}
}
};
+ private class Callback implements DialogViewCallback {
+ @Override
+ public void onUserCanceled() {
+ mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+ }
+
+ @Override
+ public void onErrorShown() {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
+ false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+
+ @Override
+ public void onNegativePressed() {
+ mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+ }
+
+ @Override
+ public void onPositivePressed() {
+ mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+ }
+ }
+
@Override
public void start() {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
@@ -93,16 +116,16 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call
}
getComponent(CommandQueue.class).addCallbacks(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mDialogView = new FingerprintDialogView(mContext, mHandler);
+ mDialogView = new FingerprintDialogView(mContext, mCallback);
}
@Override
- public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
- if (DEBUG) Log.d(TAG, "showFingerprintDialog");
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+ if (DEBUG) Log.d(TAG, "showBiometricDialog");
// Remove these messages as they are part of the previous client
- mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
- mHandler.removeMessages(MSG_FINGERPRINT_HELP);
- mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+ mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
+ mHandler.removeMessages(MSG_BIOMETRIC_HELP);
+ mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
@@ -110,26 +133,26 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call
}
@Override
- public void onFingerprintAuthenticated() {
- if (DEBUG) Log.d(TAG, "onFingerprintAuthenticated");
- mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+ public void onBiometricAuthenticated() {
+ if (DEBUG) Log.d(TAG, "onBiometricAuthenticated");
+ mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
}
@Override
- public void onFingerprintHelp(String message) {
- if (DEBUG) Log.d(TAG, "onFingerprintHelp: " + message);
- mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+ public void onBiometricHelp(String message) {
+ if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
+ mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
}
@Override
- public void onFingerprintError(String error) {
- if (DEBUG) Log.d(TAG, "onFingerprintError: " + error);
- mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+ public void onBiometricError(String error) {
+ if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
+ mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
}
@Override
- public void hideFingerprintDialog() {
- if (DEBUG) Log.d(TAG, "hideFingerprintDialog");
+ public void hideBiometricDialog() {
+ if (DEBUG) Log.d(TAG, "hideBiometricDialog");
mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
}
@@ -148,21 +171,23 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call
mDialogShowing = true;
}
- private void handleFingerprintAuthenticated() {
- if (DEBUG) Log.d(TAG, "handleFingerprintAuthenticated");
+ private void handleBiometricAuthenticated() {
+ if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
+
+ // TODO: announce correct string depending on modality
mDialogView.announceForAccessibility(
mContext.getResources().getText(
com.android.internal.R.string.fingerprint_authenticated));
handleHideDialog(false /* userCanceled */);
}
- private void handleFingerprintHelp(String message) {
- if (DEBUG) Log.d(TAG, "handleFingerprintHelp: " + message);
+ private void handleBiometricHelp(String message) {
+ if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
mDialogView.showHelpMessage(message);
}
- private void handleFingerprintError(String error) {
- if (DEBUG) Log.d(TAG, "handleFingerprintError: " + error);
+ private void handleBiometricError(String error) {
+ if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
if (!mDialogShowing) {
if (DEBUG) Log.d(TAG, "Dialog already dismissed");
return;
@@ -216,10 +241,6 @@ public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Call
handleHideDialog(false /* userCanceled */);
}
- private void handleClearMessage() {
- mDialogView.resetMessage();
- }
-
private void handleUserCanceled() {
handleHideDialog(true /* userCanceled */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
new file mode 100644
index 000000000000..f388d9c67718
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.biometrics;
+
+/**
+ * Callback interface for dialog views. These should be implemented by the controller (e.g.
+ * FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView).
+ */
+public interface DialogViewCallback {
+ /**
+ * Invoked when the user cancels authentication by tapping outside the prompt, etc. The dialog
+ * should be dismissed.
+ */
+ void onUserCanceled();
+
+ /**
+ * Invoked when an error is shown. The dialog should be dismissed after a set amount of time.
+ */
+ void onErrorShown();
+
+ /**
+ * Invoked when the negative button is pressed. The client should be notified and the dialog
+ * should be dismissed.
+ */
+ void onNegativePressed();
+
+ /**
+ * Invoked when the positive button is pressed. The client should be notified and the dialog
+ * should be dismissed.
+ */
+ void onPositivePressed();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 8013a9e29dfd..68c2c42dd961 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.fingerprint;
+package com.android.systemui.biometrics;
import android.content.Context;
import android.graphics.Color;
@@ -26,6 +26,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -56,6 +57,8 @@ public class FingerprintDialogView extends LinearLayout {
private static final int ANIMATION_DURATION_SHOW = 250; // ms
private static final int ANIMATION_DURATION_AWAY = 350; // ms
+ private static final int MSG_CLEAR_MESSAGE = 1;
+
private static final int STATE_NONE = 0;
private static final int STATE_FINGERPRINT = 1;
private static final int STATE_FINGERPRINT_ERROR = 2;
@@ -68,18 +71,17 @@ public class FingerprintDialogView extends LinearLayout {
private final int mErrorColor;
private final int mTextColor;
private final int mFingerprintColor;
+ private final float mDisplayWidth;
+ private final DialogViewCallback mCallback;
private ViewGroup mLayout;
private final TextView mErrorText;
- private Handler mHandler;
private Bundle mBundle;
private final LinearLayout mDialog;
private int mLastState;
private boolean mAnimatingAway;
private boolean mWasForceRemoved;
- private final float mDisplayWidth;
-
private final Runnable mShowAnimationRunnable = new Runnable() {
@Override
public void run() {
@@ -98,9 +100,23 @@ public class FingerprintDialogView extends LinearLayout {
}
};
- public FingerprintDialogView(Context context, Handler handler) {
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_CLEAR_MESSAGE:
+ handleClearMessage();
+ break;
+ default:
+ Log.e(TAG, "Unhandled message: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ public FingerprintDialogView(Context context, DialogViewCallback callback) {
super(context);
- mHandler = handler;
+ mCallback = callback;
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mAnimationTranslationOffset = getResources()
@@ -138,7 +154,7 @@ public class FingerprintDialogView extends LinearLayout {
downPressed = false;
} else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
downPressed = false;
- mHandler.obtainMessage(FingerprintDialogImpl.MSG_USER_CANCELED).sendToTarget();
+ mCallback.onUserCanceled();
}
return true;
}
@@ -155,11 +171,11 @@ public class FingerprintDialogView extends LinearLayout {
setDismissesDialog(rightSpace);
negative.setOnClickListener((View v) -> {
- mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+ mCallback.onNegativePressed();
});
positive.setOnClickListener((View v) -> {
- mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+ mCallback.onPositivePressed();
});
mLayout.setFocusableInTouchMode(true);
@@ -230,8 +246,7 @@ public class FingerprintDialogView extends LinearLayout {
private void setDismissesDialog(View v) {
v.setClickable(true);
v.setOnTouchListener((View view, MotionEvent event) -> {
- mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, true /* userCanceled */)
- .sendToTarget();
+ mCallback.onUserCanceled();
return true;
});
}
@@ -289,7 +304,7 @@ public class FingerprintDialogView extends LinearLayout {
}
// Clears the temporary message and shows the help message.
- protected void resetMessage() {
+ private void handleClearMessage() {
updateFingerprintIcon(STATE_FINGERPRINT);
mErrorText.setText(R.string.fingerprint_dialog_touch_sensor);
mErrorText.setTextColor(mTextColor);
@@ -297,12 +312,12 @@ public class FingerprintDialogView extends LinearLayout {
// Shows an error/help message
private void showTemporaryMessage(String message) {
- mHandler.removeMessages(FingerprintDialogImpl.MSG_CLEAR_MESSAGE);
+ mHandler.removeMessages(MSG_CLEAR_MESSAGE);
updateFingerprintIcon(STATE_FINGERPRINT_ERROR);
mErrorText.setText(message);
mErrorText.setTextColor(mErrorColor);
mErrorText.setContentDescription(message);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
BiometricPrompt.HIDE_DIALOG_DELAY);
}
@@ -312,8 +327,7 @@ public class FingerprintDialogView extends LinearLayout {
public void showErrorMessage(String error) {
showTemporaryMessage(error);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG,
- false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
+ mCallback.onErrorShown();
}
private void updateFingerprintIcon(int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 8f30edd4c9be..03a573ea48d8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -65,6 +65,7 @@ public class PipTouchHandler {
private static final boolean ENABLE_FLING_DISMISS = false;
private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
+ private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
// Allow dragging the PIP to a location to close it
private final boolean mEnableDimissDragToEdge;
@@ -314,8 +315,10 @@ public class PipTouchHandler {
// above the position as if shelf/IME shows, don't move the PIP window.
int movementBoundsAdjustment = toMovementBounds.bottom - mMovementBounds.bottom;
int offsetAdjustment = fromImeAdjustment ? mImeOffset : mShelfHeight;
+ final float bottomOffsetBufferInPx = BOTTOM_OFFSET_BUFFER_DP
+ * mContext.getResources().getDisplayMetrics().density;
if (toAdjustedBounds.bottom >= mMovementBounds.bottom
- && animatingBounds.top
+ && animatingBounds.top + Math.round(bottomOffsetBufferInPx)
< toAdjustedBounds.bottom - movementBoundsAdjustment - offsetAdjustment) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 145a2466f7d2..909cd794f3fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -85,11 +85,11 @@ public class CommandQueue extends IStatusBar.Stub {
private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT;
private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT;
private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT;
- private static final int MSG_FINGERPRINT_SHOW = 39 << MSG_SHIFT;
- private static final int MSG_FINGERPRINT_AUTHENTICATED = 40 << MSG_SHIFT;
- private static final int MSG_FINGERPRINT_HELP = 41 << MSG_SHIFT;
- private static final int MSG_FINGERPRINT_ERROR = 42 << MSG_SHIFT;
- private static final int MSG_FINGERPRINT_HIDE = 43 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_SHOW = 39 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_AUTHENTICATED = 40 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_HELP = 41 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_ERROR = 42 << MSG_SHIFT;
+ private static final int MSG_BIOMETRIC_HIDE = 43 << MSG_SHIFT;
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
@@ -160,11 +160,11 @@ public class CommandQueue extends IStatusBar.Stub {
default void onRotationProposal(int rotation, boolean isValid) { }
- default void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
- default void onFingerprintAuthenticated() { }
- default void onFingerprintHelp(String message) { }
- default void onFingerprintError(String error) { }
- default void hideFingerprintDialog() { }
+ default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
+ default void onBiometricAuthenticated() { }
+ default void onBiometricHelp(String message) { }
+ default void onBiometricError(String error) { }
+ default void hideBiometricDialog() { }
}
@VisibleForTesting
@@ -513,41 +513,41 @@ public class CommandQueue extends IStatusBar.Stub {
}
@Override
- public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
- mHandler.obtainMessage(MSG_FINGERPRINT_SHOW, args)
+ mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
}
@Override
- public void onFingerprintAuthenticated() {
+ public void onBiometricAuthenticated() {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+ mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
}
}
@Override
- public void onFingerprintHelp(String message) {
+ public void onBiometricHelp(String message) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+ mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
}
}
@Override
- public void onFingerprintError(String error) {
+ public void onBiometricError(String error) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+ mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
}
}
@Override
- public void hideFingerprintDialog() {
+ public void hideBiometricDialog() {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_FINGERPRINT_HIDE).sendToTarget();
+ mHandler.obtainMessage(MSG_BIOMETRIC_HIDE).sendToTarget();
}
}
@@ -752,34 +752,34 @@ public class CommandQueue extends IStatusBar.Stub {
mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
}
break;
- case MSG_FINGERPRINT_SHOW:
- mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
- mHandler.removeMessages(MSG_FINGERPRINT_HELP);
- mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+ case MSG_BIOMETRIC_SHOW:
+ mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
+ mHandler.removeMessages(MSG_BIOMETRIC_HELP);
+ mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showFingerprintDialog(
+ mCallbacks.get(i).showBiometricDialog(
(Bundle)((SomeArgs)msg.obj).arg1,
(IBiometricPromptReceiver)((SomeArgs)msg.obj).arg2);
}
break;
- case MSG_FINGERPRINT_AUTHENTICATED:
+ case MSG_BIOMETRIC_AUTHENTICATED:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onFingerprintAuthenticated();
+ mCallbacks.get(i).onBiometricAuthenticated();
}
break;
- case MSG_FINGERPRINT_HELP:
+ case MSG_BIOMETRIC_HELP:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onFingerprintHelp((String) msg.obj);
+ mCallbacks.get(i).onBiometricHelp((String) msg.obj);
}
break;
- case MSG_FINGERPRINT_ERROR:
+ case MSG_BIOMETRIC_ERROR:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onFingerprintError((String) msg.obj);
+ mCallbacks.get(i).onBiometricError((String) msg.obj);
}
break;
- case MSG_FINGERPRINT_HIDE:
+ case MSG_BIOMETRIC_HIDE:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).hideFingerprintDialog();
+ mCallbacks.get(i).hideBiometricDialog();
}
break;
case MSG_SHOW_CHARGING_ANIMATION:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index 92bf82164917..47b7fe9d7277 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -27,6 +27,7 @@ import com.android.systemui.statusbar.notification.NotificationData;
import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -38,18 +39,21 @@ public class NotificationUiAdjustment {
public final String key;
public final List<Notification.Action> smartActions;
+ public final CharSequence[] smartReplies;
@VisibleForTesting
- NotificationUiAdjustment(String key, List<Notification.Action> smartActions) {
+ NotificationUiAdjustment(
+ String key, List<Notification.Action> smartActions, CharSequence[] smartReplies) {
this.key = key;
this.smartActions = smartActions == null
? Collections.emptyList()
: new ArrayList<>(smartActions);
+ this.smartReplies = smartReplies == null ? new CharSequence[0] : smartReplies.clone();
}
public static NotificationUiAdjustment extractFromNotificationEntry(
NotificationData.Entry entry) {
- return new NotificationUiAdjustment(entry.key, entry.smartActions);
+ return new NotificationUiAdjustment(entry.key, entry.smartActions, entry.smartReplies);
}
public static boolean needReinflate(
@@ -58,7 +62,13 @@ public class NotificationUiAdjustment {
if (oldAdjustment == newAdjustment) {
return false;
}
- return areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions);
+ if (areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions)) {
+ return true;
+ }
+ if (!Arrays.equals(oldAdjustment.smartReplies, newAdjustment.smartReplies)) {
+ return true;
+ }
+ return false;
}
public static boolean areDifferent(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 81d61918875d..32fd054e8ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -147,7 +147,7 @@ public class HvacController {
return;
}
view.setTemp(mHvacManager.getFloatProperty(id, zone));
- } catch (CarNotConnectedException e) {
+ } catch (Exception e) {
view.setTemp(Float.NaN);
Log.e(TAG, "Failed to get value from hvac service", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ce8f224c71e6..804e8429e171 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -112,6 +112,7 @@ public class NotificationData {
public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
@NonNull
public List<Notification.Action> smartActions = Collections.emptyList();
+ public CharSequence[] smartReplies = new CharSequence[0];
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -155,6 +156,9 @@ public class NotificationData {
userSentiment = ranking.getUserSentiment();
smartActions = ranking.getSmartActions() == null
? Collections.emptyList() : ranking.getSmartActions();
+ smartReplies = ranking.getSmartReplies() == null
+ ? new CharSequence[0]
+ : ranking.getSmartReplies().toArray(new CharSequence[0]);
}
public void setInterruption() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index da1fd3b83452..0110610ca5aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -36,6 +36,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -1229,32 +1230,40 @@ public class NotificationContentView extends FrameLayout {
boolean hasRemoteInput = false;
RemoteInput remoteInputWithChoices = null;
PendingIntent pendingIntentWithChoices = null;
+ CharSequence[] choices = null;
Notification.Action[] actions = entry.notification.getNotification().actions;
if (actions != null) {
for (Notification.Action a : actions) {
- if (a.getRemoteInputs() != null) {
- for (RemoteInput ri : a.getRemoteInputs()) {
- boolean showRemoteInputView = ri.getAllowFreeFormInput();
- boolean showSmartReplyView = enableSmartReplies && ri.getChoices() != null
- && ri.getChoices().length > 0;
- if (showRemoteInputView) {
- hasRemoteInput = true;
- }
- if (showSmartReplyView) {
- remoteInputWithChoices = ri;
- pendingIntentWithChoices = a.actionIntent;
- }
- if (showRemoteInputView || showSmartReplyView) {
- break;
+ if (a.getRemoteInputs() == null) {
+ continue;
+ }
+ for (RemoteInput ri : a.getRemoteInputs()) {
+ boolean showRemoteInputView = ri.getAllowFreeFormInput();
+ boolean showSmartReplyView = enableSmartReplies
+ && (ArrayUtils.isEmpty(ri.getChoices())
+ || (showRemoteInputView && !ArrayUtils.isEmpty(entry.smartReplies)));
+ if (showRemoteInputView) {
+ hasRemoteInput = true;
+ }
+ if (showSmartReplyView) {
+ remoteInputWithChoices = ri;
+ pendingIntentWithChoices = a.actionIntent;
+ if (!ArrayUtils.isEmpty(ri.getChoices())) {
+ choices = ri.getChoices();
+ } else {
+ choices = entry.smartReplies;
}
}
+ if (showRemoteInputView || showSmartReplyView) {
+ break;
+ }
}
}
}
applyRemoteInput(entry, hasRemoteInput);
- applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
+ applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
}
private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
@@ -1356,10 +1365,10 @@ public class NotificationContentView extends FrameLayout {
}
private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
- NotificationData.Entry entry) {
+ NotificationData.Entry entry, CharSequence[] choices) {
if (mExpandedChild != null) {
mExpandedSmartReplyView =
- applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
+ applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
if (mExpandedSmartReplyView != null && remoteInput != null
&& remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
mSmartReplyController.smartRepliesAdded(entry, remoteInput.getChoices().length);
@@ -1369,7 +1378,7 @@ public class NotificationContentView extends FrameLayout {
private SmartReplyView applySmartReplyView(
View view, RemoteInput remoteInput, PendingIntent pendingIntent,
- NotificationData.Entry entry) {
+ NotificationData.Entry entry, CharSequence[] choices) {
View smartReplyContainerCandidate = view.findViewById(
com.android.internal.R.id.smart_reply_container);
if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
@@ -1406,7 +1415,8 @@ public class NotificationContentView extends FrameLayout {
}
if (smartReplyView != null) {
smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
- mSmartReplyController, entry, smartReplyContainer);
+ mSmartReplyController, entry, smartReplyContainer, choices
+ );
smartReplyContainer.setVisibility(View.VISIBLE);
}
return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 252ab22a008f..c76a4b566939 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -173,14 +173,14 @@ public class SmartReplyView extends ViewGroup {
Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
}
- public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent,
+ public void setRepliesFromRemoteInput(
+ RemoteInput remoteInput, PendingIntent pendingIntent,
SmartReplyController smartReplyController, NotificationData.Entry entry,
- View smartReplyContainer) {
+ View smartReplyContainer, CharSequence[] choices) {
mSmartReplyContainer = smartReplyContainer;
removeAllViews();
mCurrentBackgroundColor = mDefaultBackgroundColor;
if (remoteInput != null && pendingIntent != null) {
- CharSequence[] choices = remoteInput.getChoices();
if (choices != null) {
for (int i = 0; i < choices.length; ++i) {
Button replyButton = inflateReplyButton(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7855b4736532..02babacc3acd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -148,6 +148,7 @@ public class VolumeDialogImpl implements VolumeDialog {
private SafetyWarningDialog mSafetyWarning;
private boolean mHovering = false;
private boolean mShowActiveStreamOnly;
+ private boolean mConfigChanged = false;
public VolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
@@ -551,6 +552,11 @@ public class VolumeDialogImpl implements VolumeDialog {
rescheduleTimeoutH();
mShowing = true;
+ if (mConfigChanged) {
+ initDialog();
+ mConfigurableTexts.update();
+ mConfigChanged = false;
+ }
initSettingsH();
mDialog.show();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
@@ -1102,8 +1108,7 @@ public class VolumeDialogImpl implements VolumeDialog {
@Override
public void onConfigurationChanged() {
mDialog.dismiss();
- initDialog();
- mConfigurableTexts.update();
+ mConfigChanged = true;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
index ce47e60f3e1c..db1e049dec9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -30,6 +30,7 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Test;
import java.util.Collections;
+import java.util.List;
@SmallTest
public class NotificationUiAdjustmentTest extends SysuiTestCase {
@@ -41,8 +42,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
Notification.Action action =
createActionBuilder("first", R.drawable.ic_corp_icon, pendingIntent).build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.emptyList()),
- new NotificationUiAdjustment("second", Collections.singletonList(action))))
+ createUiAdjustmentFromSmartActions("first", Collections.emptyList()),
+ createUiAdjustmentFromSmartActions("second", Collections.singletonList(action))))
.isTrue();
}
@@ -56,8 +57,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
createActionBuilder("second", R.drawable.ic_corp_icon, pendingIntent).build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
- new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+ createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
.isTrue();
}
@@ -72,8 +73,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
.build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
- new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+ createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
.isTrue();
}
@@ -91,8 +92,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
.build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
- new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+ createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
.isTrue();
}
@@ -116,8 +117,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
.build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
- new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+ createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
.isTrue();
}
@@ -141,8 +142,8 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
.build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
- new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+ createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
.isTrue();
}
@@ -163,8 +164,25 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
.addRemoteInput(secondRemoteInput).build();
assertThat(NotificationUiAdjustment.needReinflate(
- new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
- new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+ createUiAdjustmentFromSmartActions(
+ "second", Collections.singletonList(secondAction))))
+ .isFalse();
+ }
+
+ @Test
+ public void needReinflate_differentSmartReplies() {
+ assertThat(NotificationUiAdjustment.needReinflate(
+ createUiAdjustmentFromSmartReplies("first", new CharSequence[]{"a", "b"}),
+ createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"b", "a"})))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_sameSmartReplies() {
+ assertThat(NotificationUiAdjustment.needReinflate(
+ createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"a", "b"}),
+ createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"a", "b"})))
.isFalse();
}
@@ -177,4 +195,14 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase {
private RemoteInput createRemoteInput(String resultKey, String label, CharSequence[] choices) {
return new RemoteInput.Builder(resultKey).setLabel(label).setChoices(choices).build();
}
+
+ private NotificationUiAdjustment createUiAdjustmentFromSmartActions(
+ String key, List<Notification.Action> actions) {
+ return new NotificationUiAdjustment(key, actions, new CharSequence[0]);
+ }
+
+ private NotificationUiAdjustment createUiAdjustmentFromSmartReplies(
+ String key, CharSequence[] replies) {
+ return new NotificationUiAdjustment(key, Collections.emptyList(), replies);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index c3683b39d47d..de5a8a0a6d2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -430,21 +430,22 @@ public class NotificationDataTest extends SysuiTestCase {
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
- null);
+ null, null);
} else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), 255,
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), true, null);
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), true, null, null);
} else {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), false, null);
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), false, null,
+ null);
}
return true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 61c1ecb6a17e..6543bdb32913 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -157,7 +157,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false, null);
+ null, null, null, true, sentiment, false, null, null);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
@@ -176,7 +176,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
null, null,
null, null, null, true,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false,
- smartActions);
+ smartActions, null);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 5ac2190f6a3f..01e63070e07d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -375,7 +375,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
- mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer);
+ mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
}
/** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 11b2343d0e1b..cf086812cb76 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -22,10 +22,8 @@ import android.os.PowerManager;
import android.util.DebugUtils;
import android.util.ExceptionUtils;
import android.util.Log;
-import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.view.Choreographer;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputFilter;
@@ -104,31 +102,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
| FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
| FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
- private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
- @Override
- public void run() {
- final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
- if (DEBUG) {
- Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos);
- }
- processBatchedEvents(frameTimeNanos);
- if (DEBUG) {
- Slog.i(TAG, "End batch processing.");
- }
- if (mEventQueue != null) {
- scheduleProcessBatchedEvents();
- }
- }
- };
-
private final Context mContext;
private final PowerManager mPm;
private final AccessibilityManagerService mAms;
- private final Choreographer mChoreographer;
-
private boolean mInstalled;
private int mUserId;
@@ -147,8 +126,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private EventStreamTransformation mEventHandler;
- private MotionEventHolder mEventQueue;
-
private EventStreamState mMouseStreamState;
private EventStreamState mTouchScreenStreamState;
@@ -160,7 +137,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mContext = context;
mAms = service;
mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mChoreographer = Choreographer.getInstance();
}
@Override
@@ -274,7 +250,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return;
}
- batchMotionEvent(event, policyFlags);
+ handleMotionEvent(event, policyFlags);
}
private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
@@ -285,68 +261,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mEventHandler.onKeyEvent(event, policyFlags);
}
- private void scheduleProcessBatchedEvents() {
- mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
- mProcessBatchedEventsRunnable, null);
- }
-
- private void batchMotionEvent(MotionEvent event, int policyFlags) {
- if (DEBUG) {
- Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags);
- }
- if (mEventQueue == null) {
- mEventQueue = MotionEventHolder.obtain(event, policyFlags);
- scheduleProcessBatchedEvents();
- return;
- }
- if (mEventQueue.event.addBatch(event)) {
- return;
- }
- MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags);
- holder.next = mEventQueue;
- mEventQueue.previous = holder;
- mEventQueue = holder;
- }
-
- private void processBatchedEvents(long frameNanos) {
- MotionEventHolder current = mEventQueue;
- if (current == null) {
- return;
- }
- while (current.next != null) {
- current = current.next;
- }
- while (true) {
- if (current == null) {
- mEventQueue = null;
- break;
- }
- if (current.event.getEventTimeNano() >= frameNanos) {
- // Finished with this choreographer frame. Do the rest on the next one.
- current.next = null;
- break;
- }
- handleMotionEvent(current.event, current.policyFlags);
- MotionEventHolder prior = current;
- current = current.previous;
- prior.recycle();
- }
- }
-
private void handleMotionEvent(MotionEvent event, int policyFlags) {
if (DEBUG) {
- Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags);
- }
- // Since we do batch processing it is possible that by the time the
- // next batch is processed the event handle had been set to null.
- if (mEventHandler != null) {
- mPm.userActivity(event.getEventTime(), false);
- MotionEvent transformedEvent = MotionEvent.obtain(event);
- mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
- transformedEvent.recycle();
- } else {
- if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event);
+ Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags);
}
+ mPm.userActivity(event.getEventTime(), false);
+ MotionEvent transformedEvent = MotionEvent.obtain(event);
+ mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
+ transformedEvent.recycle();
}
@Override
@@ -469,9 +391,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
private void disableFeatures() {
- // Give the features a chance to process any batched events so we'll keep a consistent
- // event stream
- processBatchedEvents(Long.MAX_VALUE);
if (mMotionEventInjector != null) {
mAms.setMotionEventInjector(null);
mMotionEventInjector.onDestroy();
@@ -515,36 +434,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
/* ignore */
}
- private static class MotionEventHolder {
- private static final int MAX_POOL_SIZE = 32;
- private static final SimplePool<MotionEventHolder> sPool =
- new SimplePool<MotionEventHolder>(MAX_POOL_SIZE);
-
- public int policyFlags;
- public MotionEvent event;
- public MotionEventHolder next;
- public MotionEventHolder previous;
-
- public static MotionEventHolder obtain(MotionEvent event, int policyFlags) {
- MotionEventHolder holder = sPool.acquire();
- if (holder == null) {
- holder = new MotionEventHolder();
- }
- holder.event = MotionEvent.obtain(event);
- holder.policyFlags = policyFlags;
- return holder;
- }
-
- public void recycle() {
- event.recycle();
- event = null;
- policyFlags = 0;
- next = null;
- previous = null;
- sPool.release(this);
- }
- }
-
/**
* Keeps state of event streams observed for an input device with a certain source.
* Provides information about whether motion and key events should be processed by accessibility
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 293f908e2708..8ee65711feb4 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -17,8 +17,8 @@ package com.android.server.autofill;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
-import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
-import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
+import static android.service.autofill.AutofillFieldClassificationService.RESOURCE_AVAILABLE_ALGORITHMS;
+import static android.service.autofill.AutofillFieldClassificationService.RESOURCE_DEFAULT_ALGORITHM;
import android.Manifest;
import android.annotation.MainThread;
@@ -226,7 +226,7 @@ final class FieldClassificationStrategy {
*/
@Nullable
String[] getAvailableAlgorithms() {
- return getMetadataValue(SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS,
+ return getMetadataValue(RESOURCE_AVAILABLE_ALGORITHMS, "array",
(res, id) -> res.getStringArray(id));
}
@@ -235,11 +235,12 @@ final class FieldClassificationStrategy {
*/
@Nullable
String getDefaultAlgorithm() {
- return getMetadataValue(SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM, (res, id) -> res.getString(id));
+ return getMetadataValue(RESOURCE_DEFAULT_ALGORITHM, "string",
+ (res, id) -> res.getString(id));
}
@Nullable
- private <T> T getMetadataValue(String field, MetadataParser<T> parser) {
+ private <T> T getMetadataValue(String field, String type, MetadataParser<T> parser) {
final ServiceInfo serviceInfo = getServiceInfo();
if (serviceInfo == null) return null;
@@ -253,7 +254,7 @@ final class FieldClassificationStrategy {
return null;
}
- final int resourceId = serviceInfo.metaData.getInt(field);
+ final int resourceId = res.getIdentifier(field, type, serviceInfo.packageName);
return parser.get(res, resourceId);
}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index ae2a36bdc12e..30ec8ab711b6 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -9,6 +9,7 @@ import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
+import android.app.backup.IBackupCallback;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
import android.content.pm.ApplicationInfo;
@@ -20,6 +21,7 @@ import android.os.SELinux;
import android.util.Slog;
import com.android.internal.util.Preconditions;
+import com.android.server.backup.remote.ServiceBackupCallback;
import com.android.server.backup.utils.FullBackupUtils;
import libcore.io.IoUtils;
@@ -158,10 +160,17 @@ public class KeyValueAdbBackupEngine {
mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
OP_TYPE_BACKUP_WAIT);
+ IBackupCallback callback =
+ new ServiceBackupCallback(
+ mBackupManagerService.getBackupManagerBinder(), token);
// Start backup and wait for BackupManagerService to get callback for success or timeout
agent.doBackup(
- mSavedState, mBackupData, mNewState, Long.MAX_VALUE, token,
- mBackupManagerService.getBackupManagerBinder(), /*transportFlags=*/ 0);
+ mSavedState,
+ mBackupData,
+ mNewState,
+ /* quotaBytes */ Long.MAX_VALUE,
+ callback,
+ /* transportFlags */ 0);
if (!mBackupManagerService.waitUntilOperationComplete(token)) {
Slog.e(TAG, "Key-value backup failed on package " + packageName);
return false;
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 69f08ae49a2e..66b6b37b8b4f 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -38,7 +38,6 @@ import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
-import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.TransportManager;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
@@ -52,6 +51,7 @@ import com.android.server.backup.params.RestoreGetSetsParams;
import com.android.server.backup.params.RestoreParams;
import com.android.server.backup.restore.PerformAdbRestoreTask;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
import java.util.ArrayList;
import java.util.Collections;
@@ -150,17 +150,22 @@ public class BackupHandler extends Handler {
if (queue.size() > 0) {
// Spin up a backup state sequence and set it running
try {
- String dirName = transport.transportDirName();
OnTaskFinishedListener listener =
caller ->
transportManager
.disposeOfTransportClient(transportClient, caller);
- PerformBackupTask pbt = new PerformBackupTask(
- backupManagerService, transportClient, dirName, queue,
- oldJournal, null, null, listener, Collections.emptyList(), false,
- false /* nonIncremental */);
- Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
- sendMessage(pbtMessage);
+ PerformBackupTask.start(
+ backupManagerService,
+ transportClient,
+ transport.transportDirName(),
+ queue,
+ oldJournal,
+ /* observer */ null,
+ /* monitor */ null,
+ listener,
+ Collections.emptyList(),
+ /* userInitiated */ false,
+ /* nonIncremental */ false);
} catch (Exception e) {
// unable to ask the transport its dir name -- transient failure, since
// the above check succeeded. Try again next time.
@@ -405,13 +410,18 @@ public class BackupHandler extends Handler {
backupManagerService.setBackupRunning(true);
backupManagerService.getWakelock().acquire();
- PerformBackupTask pbt = new PerformBackupTask(
+ PerformBackupTask.start(
backupManagerService,
- params.transportClient, params.dirName,
- kvQueue, null, params.observer, params.monitor, params.listener,
- params.fullPackages, true, params.nonIncrementalBackup);
- Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
- sendMessage(pbtMessage);
+ params.transportClient,
+ params.dirName,
+ kvQueue,
+ /* dataChangedJournal */ null,
+ params.observer,
+ params.monitor,
+ params.listener,
+ params.fullPackages,
+ /* userInitiated */ true,
+ params.nonIncrementalBackup);
break;
}
diff --git a/services/backup/java/com/android/server/backup/internal/BackupState.java b/services/backup/java/com/android/server/backup/internal/BackupState.java
index 937b16768c5a..320b55525ea4 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupState.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupState.java
@@ -7,5 +7,6 @@ enum BackupState {
INITIAL,
BACKUP_PM,
RUNNING_QUEUE,
+ CANCELLED,
FINAL
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 551b80f72e70..c5df82ed2e79 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -16,16 +16,11 @@
package com.android.server.backup.internal;
-import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_BACKUP_TRACE;
import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.OP_PENDING;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
import android.annotation.Nullable;
import android.app.ApplicationThreadConstants;
@@ -36,14 +31,16 @@ import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupTransport;
+import android.app.backup.IBackupCallback;
+import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Message;
+import android.os.ConditionVariable;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
@@ -51,23 +48,24 @@ import android.os.WorkSource;
import android.system.ErrnoException;
import android.system.Os;
import android.util.EventLog;
+import android.util.Pair;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
-import com.android.server.backup.PackageManagerBackupAgent;
-import com.android.server.backup.BackupManagerService;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.remote.RemoteCall;
+import com.android.server.backup.remote.RemoteCallable;
+import com.android.server.backup.remote.RemoteResult;
import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportUtils;
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
@@ -85,85 +83,225 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
/**
- * This class handles the process of backing up a given list of key/value backup packages.
- * Also takes in a list of pending dolly backups and kicks them off when key/value backups
- * are done.
+ * Represents the task of performing a sequence of key-value backups for a given list of packages.
+ * Method {@link #run()} executes the backups to the transport specified via the {@code
+ * transportClient} parameter in the constructor.
+ *
+ * <p>A few definitions:
+ *
+ * <ul>
+ * <li>State directory: {@link BackupManagerService#getBaseStateDir()}/&lt;transport&gt;
+ * <li>State file: {@link
+ * BackupManagerService#getBaseStateDir()}/&lt;transport&gt;/&lt;package&gt;<br>
+ * Represents the state of the backup data for a specific package in the current dataset.
+ * <li>Stage directory: {@link BackupManagerService#getDataDir()}
+ * <li>Stage file: {@link BackupManagerService#getDataDir()}/&lt;package&gt;.data<br>
+ * Contains staged data that the agents wrote via {@link BackupDataOutput}, to be transmitted
+ * to the transport.
+ * </ul>
+ *
+ * If there is no PackageManager (PM) pseudo-package state file in the state directory, the
+ * specified transport will be initialized with {@link IBackupTransport#initializeDevice()}.
+ *
+ * <p>The PM pseudo-package is the first package to be backed-up and sent to the transport in case
+ * of incremental choice. If non-incremental, PM will only be backed-up if specified in the queue,
+ * and if it's the case it will be re-positioned at the head of the queue.
+ *
+ * <p>Before starting, this task will register itself in {@link BackupManagerService} current
+ * operations.
*
- * Flow:
- * If required, backup @pm@.
- * For each pending key/value backup package:
- * - Bind to agent.
- * - Call agent.doBackup()
- * - Wait either for cancel/timeout or operationComplete() callback from the agent.
- * Start task to perform dolly backups.
+ * <p>In summary, this task will for each package:
*
- * There are three entry points into this class:
- * - execute() [Called from the handler thread]
- * - operationComplete(long result) [Called from the handler thread]
- * - handleCancel(boolean cancelAll) [Can be called from any thread]
- * These methods synchronize on mCancelLock.
+ * <ul>
+ * <li>Bind to its {@link IBackupAgent}.
+ * <li>Request transport quota and flags.
+ * <li>Call {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
+ * ParcelFileDescriptor, long, int, IBackupManager, int)} via {@link RemoteCall} passing the
+ * old state file descriptor (read), the backup data file descriptor (write), the new state
+ * file descriptor (write), the quota and the transport flags. This will call {@link
+ * BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} with
+ * the old state file to be read, a {@link BackupDataOutput} object to write the backup data
+ * and the new state file to write. By writing to {@link BackupDataOutput}, the agent will
+ * write data to the stage file. The task will block waiting for either:
+ * <ul>
+ * <li>Agent response.
+ * <li>Agent time-out (specified via {@link
+ * BackupManagerService#getAgentTimeoutParameters()}.
+ * <li>External cancellation or thread interrupt.
+ * </ul>
+ * <li>Unbind the agent.
+ * <li>Assuming agent response, send the staged data that the agent wrote to disk to the transport
+ * via {@link IBackupTransport#performBackup(PackageInfo, ParcelFileDescriptor, int)}.
+ * <li>Call {@link IBackupTransport#finishBackup()} if previous call was successful.
+ * <li>Save the new state in the state file. During the agent call it was being written to
+ * &lt;state file&gt;.new, here we rename it and replace the old one.
+ * <li>Delete the stage file.
+ * </ul>
*
- * Interaction with mCurrentOperations:
- * - An entry for this task is put into mCurrentOperations for the entire lifetime of the
- * task. This is useful to cancel the task if required.
- * - An ephemeral entry is put into mCurrentOperations each time we are waiting on for
- * response from a backup agent. This is used to plumb timeouts and completion callbacks.
+ * In the end, this task will:
+ *
+ * <ul>
+ * <li>Mark data-changed for the remaining packages in the queue (skipped packages).
+ * <li>Delete the {@link DataChangedJournal} provided. Note that this should not be the current
+ * journal.
+ * <li>Set {@link BackupManagerService} current token as {@link
+ * IBackupTransport#getCurrentRestoreSet()}, if applicable.
+ * <li>Add the transport to the list of transports pending initialization ({@link
+ * BackupManagerService#getPendingInits()}) and kick-off initialization if the transport ever
+ * returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
+ * <li>Unregister the task in current operations.
+ * <li>Release the wakelock.
+ * <li>Kick-off {@link PerformFullTransportBackupTask} if a list of full-backup packages was
+ * provided.
+ * </ul>
+ *
+ * The caller can specify whether this should be an incremental or non-incremental backup. In the
+ * case of non-incremental the agents will be passed an empty old state file, which signals that a
+ * complete backup should be performed.
+ *
+ * <p>This task is designed to run on a dedicated thread, with the exception of the {@link
+ * #handleCancel(boolean)} method, which can be called from any thread.
*/
-public class PerformBackupTask implements BackupRestoreTask {
+// TODO: Stop poking into BMS state and doing things for it (e.g. synchronizing on public locks)
+// TODO: Consider having the caller responsible for some clean-up (like resetting state)
+// TODO: Distinguish between cancel and time-out where possible for logging/monitoring/observing
+public class PerformBackupTask implements BackupRestoreTask, Runnable {
private static final String TAG = "PerformBackupTask";
+ private static final boolean DEBUG = BackupManagerService.DEBUG || true;
+ private static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
+ private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND;
+ private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
private static final String BLANK_STATE_FILE_NAME = "blank_state";
@VisibleForTesting
public static final String STAGING_FILE_SUFFIX = ".data";
@VisibleForTesting
public static final String NEW_STATE_FILE_SUFFIX = ".new";
- private BackupManagerService backupManagerService;
- private final Object mCancelLock = new Object();
-
- private ArrayList<BackupRequest> mQueue;
- private ArrayList<BackupRequest> mOriginalQueue;
- private File mStateDir;
- @Nullable private DataChangedJournal mJournal;
- private BackupState mCurrentState;
- private List<String> mPendingFullBackups;
- private IBackupObserver mObserver;
- private IBackupManagerMonitor mMonitor;
+ /**
+ * Creates a new {@link PerformBackupTask} for key-value backup operation, spins up a new
+ * dedicated thread and kicks off the operation in it.
+ *
+ * @param backupManagerService The {@link BackupManagerService} system service.
+ * @param transportClient The {@link TransportClient} that contains the transport used for the
+ * operation.
+ * @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
+ * transport whose {@link TransportClient} was provided above.
+ * @param queue The list of packages that will be backed-up, in the form of {@link
+ * BackupRequest}.
+ * @param dataChangedJournal The old data-changed journal file that will be deleted when the
+ * operation finishes (successfully or not) or {@code null}.
+ * @param observer A {@link IBackupObserver}.
+ * @param monitor A {@link IBackupManagerMonitor}.
+ * @param listener A {@link OnTaskFinishedListener} or {@code null}.
+ * @param pendingFullBackups The list of packages that will be passed for a new {@link
+ * PerformFullTransportBackupTask} operation, which will be started when this finishes.
+ * @param userInitiated Whether this was user-initiated or not.
+ * @param nonIncremental If {@code true}, this will be a complete backup for each package,
+ * otherwise it will be just an incremental one over the current dataset.
+ * @return The {@link PerformBackupTask} that was started.
+ */
+ public static PerformBackupTask start(
+ BackupManagerService backupManagerService,
+ TransportClient transportClient,
+ String transportDirName,
+ ArrayList<BackupRequest> queue,
+ @Nullable DataChangedJournal dataChangedJournal,
+ IBackupObserver observer,
+ IBackupManagerMonitor monitor,
+ @Nullable OnTaskFinishedListener listener,
+ List<String> pendingFullBackups,
+ boolean userInitiated,
+ boolean nonIncremental) {
+ PerformBackupTask task =
+ new PerformBackupTask(
+ backupManagerService,
+ transportClient,
+ transportDirName,
+ queue,
+ dataChangedJournal,
+ observer,
+ monitor,
+ listener,
+ pendingFullBackups,
+ userInitiated,
+ nonIncremental);
+ Thread thread = new Thread(task, "key-value-backup-" + THREAD_COUNT.incrementAndGet());
+ if (DEBUG) {
+ Slog.d(TAG, "Spinning thread " + thread.getName());
+ }
+ thread.start();
+ return task;
+ }
+ private final BackupManagerService mBackupManagerService;
private final TransportClient mTransportClient;
+ private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ private final IBackupObserver mObserver;
private final OnTaskFinishedListener mListener;
- private final PerformFullTransportBackupTask mFullBackupTask;
+ private final boolean mUserInitiated;
+ private final boolean mNonIncremental;
private final int mCurrentOpToken;
- private volatile int mEphemeralOpToken;
+ private final File mStateDir;
+ private final ArrayList<BackupRequest> mOriginalQueue;
+ private final ArrayList<BackupRequest> mQueue;
+ private final List<String> mPendingFullBackups;
+ @Nullable private final DataChangedJournal mJournal;
+ private IBackupManagerMonitor mMonitor;
+ @Nullable private PerformFullTransportBackupTask mFullBackupTask;
- // carried information about the current in-flight operation
private IBackupAgent mAgentBinder;
private PackageInfo mCurrentPackage;
- private File mSavedStateName;
- private File mBackupDataName;
- private File mNewStateName;
+ private File mSavedStateFile;
+ private File mBackupDataFile;
+ private File mNewStateFile;
private ParcelFileDescriptor mSavedState;
private ParcelFileDescriptor mBackupData;
private ParcelFileDescriptor mNewState;
private int mStatus;
- private boolean mFinished;
- private final boolean mUserInitiated;
- private final boolean mNonIncremental;
- private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
- private volatile boolean mCancelAll;
+ /**
+ * This {@link ConditionVariable} is used to signal that the cancel operation has been
+ * received by the task and that no more transport calls will be made. Anyone can call {@link
+ * ConditionVariable#block()} to wait for these conditions to hold true, but there should only
+ * be one place where {@link ConditionVariable#open()} is called. Also there should be no calls
+ * to {@link ConditionVariable#close()}, which means there is only one cancel per backup -
+ * subsequent calls to block will return immediately.
+ */
+ private final ConditionVariable mCancelAcknowledged = new ConditionVariable(false);
+
+ /**
+ * Set it to {@code true} and block on {@code mCancelAcknowledged} to wait for the cancellation.
+ * DO NOT set it to {@code false}.
+ */
+ private volatile boolean mCancelled = false;
- public PerformBackupTask(BackupManagerService backupManagerService,
- TransportClient transportClient, String dirName,
- ArrayList<BackupRequest> queue, @Nullable DataChangedJournal journal,
- IBackupObserver observer, IBackupManagerMonitor monitor,
- @Nullable OnTaskFinishedListener listener, List<String> pendingFullBackups,
- boolean userInitiated, boolean nonIncremental) {
- this.backupManagerService = backupManagerService;
+ /**
+ * If non-{@code null} there is a pending agent call being made. This call can be cancelled (and
+ * control returned to this task) with {@link RemoteCall#cancel()}.
+ */
+ @Nullable private volatile RemoteCall mPendingCall;
+
+ @VisibleForTesting
+ public PerformBackupTask(
+ BackupManagerService backupManagerService,
+ TransportClient transportClient,
+ String transportDirName,
+ ArrayList<BackupRequest> queue,
+ @Nullable DataChangedJournal journal,
+ IBackupObserver observer,
+ IBackupManagerMonitor monitor,
+ @Nullable OnTaskFinishedListener listener,
+ List<String> pendingFullBackups,
+ boolean userInitiated,
+ boolean nonIncremental) {
+ mBackupManagerService = backupManagerService;
mTransportClient = transportClient;
mOriginalQueue = queue;
- mQueue = new ArrayList<>();
+ // We need to retain the original queue contents in case of transport failure
+ mQueue = new ArrayList<>(mOriginalQueue);
mJournal = journal;
mObserver = observer;
mMonitor = monitor;
@@ -171,92 +309,80 @@ public class PerformBackupTask implements BackupRestoreTask {
mPendingFullBackups = pendingFullBackups;
mUserInitiated = userInitiated;
mNonIncremental = nonIncremental;
- mAgentTimeoutParameters = Preconditions.checkNotNull(
- backupManagerService.getAgentTimeoutParameters(),
- "Timeout parameters cannot be null");
-
- mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
+ mAgentTimeoutParameters =
+ Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
+ mStateDir = new File(backupManagerService.getBaseStateDir(), transportDirName);
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
-
- mFinished = false;
-
- synchronized (backupManagerService.getCurrentOpLock()) {
- if (backupManagerService.isBackupOperationInProgress()) {
- if (DEBUG) {
- Slog.d(TAG, "Skipping backup since one is already in progress.");
- }
- mCancelAll = true;
- mFullBackupTask = null;
- mCurrentState = BackupState.FINAL;
- backupManagerService.addBackupTrace("Skipped. Backup already in progress.");
- } else {
- mCurrentState = BackupState.INITIAL;
- CountDownLatch latch = new CountDownLatch(1);
- String[] fullBackups =
- mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
- mFullBackupTask =
- new PerformFullTransportBackupTask(backupManagerService,
- transportClient,
- /*fullBackupRestoreObserver*/ null,
- fullBackups, /*updateSchedule*/ false, /*runningJob*/ null,
- latch,
- mObserver, mMonitor, mListener, mUserInitiated);
-
- registerTask();
- backupManagerService.addBackupTrace("STATE => INITIAL");
- }
- }
}
- /**
- * Put this task in the repository of running tasks.
- */
private void registerTask() {
- backupManagerService.putOperation(
+ mBackupManagerService.putOperation(
mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
}
- /**
- * Remove this task from repository of running tasks.
- */
private void unregisterTask() {
- backupManagerService.removeOperation(mCurrentOpToken);
+ mBackupManagerService.removeOperation(mCurrentOpToken);
}
- // Main entry point: perform one chunk of work, updating the state as appropriate
- // and reposting the next chunk to the primary backup handler thread.
@Override
- @GuardedBy("mCancelLock")
- public void execute() {
- synchronized (mCancelLock) {
- switch (mCurrentState) {
- case INITIAL:
- beginBackup();
- break;
+ public void run() {
+ Process.setThreadPriority(THREAD_PRIORITY);
+ BackupState state = beginBackup();
+ while (state == BackupState.RUNNING_QUEUE || state == BackupState.BACKUP_PM) {
+ if (mCancelled) {
+ state = BackupState.CANCELLED;
+ }
+ switch (state) {
case BACKUP_PM:
- backupPm();
+ state = backupPm();
break;
-
case RUNNING_QUEUE:
- invokeNextAgent();
- break;
-
- case FINAL:
- if (!mFinished) {
- finalizeBackup();
- } else {
- Slog.e(TAG, "Duplicate finish of K/V pass");
+ Pair<BackupState, RemoteResult> stateAndResult = invokeNextAgent();
+ state = stateAndResult.first;
+ if (state == null) {
+ state = processAgentInvocation(stateAndResult.second);
}
break;
}
}
+ if (state == BackupState.CANCELLED) {
+ finalizeCancelledBackup();
+ } else {
+ finalizeBackup();
+ }
}
- // We're starting a backup pass. Initialize the transport if we haven't already.
- private void beginBackup() {
+ private BackupState processAgentInvocation(RemoteResult result) {
+ if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
+ // Not an explicit cancel, we need to flag it
+ mCancelled = true;
+ handleAgentCancelled();
+ return BackupState.CANCELLED;
+ }
+ if (result == RemoteResult.FAILED_CANCELLED) {
+ handleAgentCancelled();
+ return BackupState.CANCELLED;
+ }
+ if (result == RemoteResult.FAILED_TIMED_OUT) {
+ handleAgentTimeout();
+ return BackupState.RUNNING_QUEUE;
+ }
+ Preconditions.checkState(result.succeeded());
+ return handleAgentResult(result.get());
+ }
+
+ @Override
+ public void execute() {}
+
+ @Override
+ public void operationComplete(long unusedResult) {}
+
+ private BackupState beginBackup() {
if (DEBUG_BACKUP_TRACE) {
- backupManagerService.clearBackupTrace();
+ mBackupManagerService.clearBackupTrace();
StringBuilder b = new StringBuilder(256);
b.append("beginBackup: [");
for (BackupRequest req : mOriginalQueue) {
@@ -264,8 +390,34 @@ public class PerformBackupTask implements BackupRestoreTask {
b.append(req.packageName);
}
b.append(" ]");
- backupManagerService.addBackupTrace(b.toString());
+ mBackupManagerService.addBackupTrace(b.toString());
}
+ synchronized (mBackupManagerService.getCurrentOpLock()) {
+ if (mBackupManagerService.isBackupOperationInProgress()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Skipping backup since one is already in progress.");
+ }
+ mBackupManagerService.addBackupTrace("Skipped. Backup already in progress.");
+ return BackupState.FINAL;
+ }
+ }
+
+ String[] fullBackups = mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
+ mFullBackupTask =
+ new PerformFullTransportBackupTask(
+ mBackupManagerService,
+ mTransportClient,
+ /* fullBackupRestoreObserver */ null,
+ fullBackups,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ new CountDownLatch(1),
+ mObserver,
+ mMonitor,
+ mListener,
+ mUserInitiated);
+ registerTask();
+ mBackupManagerService.addBackupTrace("STATE => INITIAL");
mAgentBinder = null;
mStatus = BackupTransport.TRANSPORT_OK;
@@ -273,16 +425,10 @@ public class PerformBackupTask implements BackupRestoreTask {
// Sanity check: if the queue is empty we have no work to do.
if (mOriginalQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
- backupManagerService.addBackupTrace("queue empty at begin");
- executeNextState(BackupState.FINAL);
- return;
+ mBackupManagerService.addBackupTrace("queue empty at begin");
+ return BackupState.FINAL;
}
- // We need to retain the original queue contents in case of transport
- // failure, but we want a working copy that we can manipulate along
- // the way.
- mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
-
// When the transport is forcing non-incremental key/value payloads, we send the
// metadata only if it explicitly asks for it.
boolean skipPm = mNonIncremental;
@@ -292,8 +438,7 @@ public class PerformBackupTask implements BackupRestoreTask {
// we performed a backup. Drop it from the working queue now that
// we're committed to evaluating it for backup regardless.
for (int i = 0; i < mQueue.size(); i++) {
- if (PACKAGE_MANAGER_SENTINEL.equals(
- mQueue.get(i).packageName)) {
+ if (PACKAGE_MANAGER_SENTINEL.equals(mQueue.get(i).packageName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "Metadata in queue; eliding");
}
@@ -309,17 +454,17 @@ public class PerformBackupTask implements BackupRestoreTask {
File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
try {
IBackupTransport transport = mTransportClient.connectOrThrow("PBT.beginBackup()");
- final String transportName = transport.transportDirName();
+ String transportName = transport.name();
EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
// If we haven't stored package manager metadata yet, we must init the transport.
- if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
+ if (pmState.length() <= 0) {
Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
- backupManagerService.addBackupTrace("initializing transport " + transportName);
- backupManagerService.resetBackupState(mStateDir); // Just to make sure.
+ mBackupManagerService.addBackupTrace("initializing transport " + transportName);
+ mBackupManagerService.resetBackupState(mStateDir); // Just to make sure.
mStatus = transport.initializeDevice();
- backupManagerService.addBackupTrace("transport.initializeDevice() == " + mStatus);
+ mBackupManagerService.addBackupTrace("transport.initializeDevice() == " + mStatus);
if (mStatus == BackupTransport.TRANSPORT_OK) {
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
} else {
@@ -327,82 +472,81 @@ public class PerformBackupTask implements BackupRestoreTask {
Slog.e(TAG, "Transport error in initializeDevice()");
}
}
-
- if (skipPm) {
- Slog.d(TAG, "Skipping backup of package metadata.");
- executeNextState(BackupState.RUNNING_QUEUE);
- } else {
- // As the package manager is running here in the system process we can just set up
- // its agent directly. Thus we always run this pass because it's cheap and this way
- // we guarantee that we don't get out of step even if we're selecting among various
- // transports at run time.
- if (mStatus == BackupTransport.TRANSPORT_OK) {
- executeNextState(BackupState.BACKUP_PM);
- }
- }
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread during init", e);
- backupManagerService.addBackupTrace("Exception in backup thread during init: " + e);
+ mBackupManagerService.addBackupTrace("Exception in backup thread during init: " + e);
mStatus = BackupTransport.TRANSPORT_ERROR;
- } finally {
- // If we've succeeded so far, we will move to the BACKUP_PM state. If something has gone
- // wrong then that won't have happen so cleanup.
- backupManagerService.addBackupTrace("exiting prelim: " + mStatus);
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- // if things went wrong at this point, we need to
- // restage everything and try again later.
- backupManagerService.resetBackupState(mStateDir); // Just to make sure.
- // In case of any other error, it's backup transport error.
- executeNextState(BackupState.FINAL);
- }
}
+ mBackupManagerService.addBackupTrace("exiting prelim: " + mStatus);
+
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
+ // if things went wrong at this point, we need to
+ // restage everything and try again later.
+ mBackupManagerService.resetBackupState(mStateDir); // Just to make sure.
+ return BackupState.FINAL;
+ }
+
+ if (skipPm) {
+ Slog.d(TAG, "Skipping backup of package metadata.");
+ return BackupState.RUNNING_QUEUE;
+ }
+
+ return BackupState.BACKUP_PM;
}
- private void backupPm() {
+ private BackupState backupPm() {
+ RemoteResult agentResult = null;
+ BackupState nextState;
try {
// The package manager doesn't have a proper <application> etc, but since it's running
// here in the system process we can just set up its agent directly and use a synthetic
// BackupRequest.
- BackupAgent pmAgent = backupManagerService.makeMetadataAgent();
- mStatus = invokeAgentForBackup(
- PACKAGE_MANAGER_SENTINEL,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()));
- backupManagerService.addBackupTrace("PMBA invoke: " + mStatus);
-
- // Because the PMBA is a local instance, it has already executed its backup callback and
- // returned. Blow away the lingering (spurious) pending timeout message for it.
- backupManagerService.getBackupHandler().removeMessages(
- MSG_BACKUP_OPERATION_TIMEOUT);
+ BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
+ Pair<Integer, RemoteResult> statusAndResult =
+ invokeAgentForBackup(
+ PACKAGE_MANAGER_SENTINEL,
+ IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+ mStatus = statusAndResult.first;
+ agentResult = statusAndResult.second;
+
+ mBackupManagerService.addBackupTrace("PMBA invoke: " + mStatus);
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread during pm", e);
- backupManagerService.addBackupTrace("Exception in backup thread during pm: " + e);
+ mBackupManagerService.addBackupTrace("Exception in backup thread during pm: " + e);
mStatus = BackupTransport.TRANSPORT_ERROR;
- } finally {
- // If we've succeeded so far, invokeAgentForBackup() will have run the PM
- // metadata and its completion/timeout callback will continue the state
- // machine chain. If it failed that won't happen; we handle that now.
- backupManagerService.addBackupTrace("exiting backupPm: " + mStatus);
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- // if things went wrong at this point, we need to
- // restage everything and try again later.
- backupManagerService.resetBackupState(mStateDir); // Just to make sure.
- executeNextState(BackupState.FINAL);
- }
}
+ mBackupManagerService.addBackupTrace("exiting backupPm: " + mStatus);
+
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
+ Preconditions.checkNotNull(agentResult);
+ nextState = processAgentInvocation(agentResult);
+ } else {
+ // if things went wrong at this point, we need to
+ // restage everything and try again later.
+ mBackupManagerService.resetBackupState(mStateDir); // Just to make sure.
+ nextState = BackupState.FINAL;
+ }
+
+ return nextState;
}
- // Transport has been initialized and the PM metadata submitted successfully
- // if that was warranted. Now we process the single next thing in the queue.
- private void invokeNextAgent() {
+ /**
+ * Returns either:
+ *
+ * <ul>
+ * <li>(next state, {@code null}): In case we failed to call the agent.
+ * <li>({@code null}, agent result): In case we successfully called the agent.
+ * </ul>
+ */
+ private Pair<BackupState, RemoteResult> invokeNextAgent() {
mStatus = BackupTransport.TRANSPORT_OK;
- backupManagerService.addBackupTrace("invoke q=" + mQueue.size());
+ mBackupManagerService.addBackupTrace("invoke q=" + mQueue.size());
// Sanity check that we have work to do. If not, skip to the end where
// we reestablish the wakelock invariants etc.
if (mQueue.isEmpty()) {
if (MORE_DEBUG) Slog.i(TAG, "queue now empty");
- executeNextState(BackupState.FINAL);
- return;
+ return Pair.create(BackupState.FINAL, null);
}
// pop the entry we're going to process on this step
@@ -410,15 +554,16 @@ public class PerformBackupTask implements BackupRestoreTask {
mQueue.remove(0);
Slog.d(TAG, "starting key/value backup of " + request);
- backupManagerService.addBackupTrace("launch agent for " + request.packageName);
+ mBackupManagerService.addBackupTrace("launch agent for " + request.packageName);
// Verify that the requested app exists; it might be something that
// requested a backup but was then uninstalled. The request was
// journalled and rather than tamper with the journal it's safer
// to sanity-check here. This also gives us the classname of the
// package's backup agent.
+ RemoteResult agentResult = null;
try {
- PackageManager pm = backupManagerService.getPackageManager();
+ PackageManager pm = mBackupManagerService.getPackageManager();
mCurrentPackage = pm.getPackageInfo(request.packageName,
PackageManager.GET_SIGNING_CERTIFICATES);
if (!AppBackupUtils.appIsEligibleForBackup(mCurrentPackage.applicationInfo, pm)) {
@@ -427,14 +572,13 @@ public class PerformBackupTask implements BackupRestoreTask {
// backups.
Slog.i(TAG, "Package " + request.packageName
+ " no longer supports backup; skipping");
- backupManagerService.addBackupTrace("skipping - not eligible, completion is noop");
+ mBackupManagerService.addBackupTrace("skipping - not eligible, completion is noop");
// Shouldn't happen in case of requested backup, as pre-check was done in
// #requestBackup(), except to app update done concurrently
BackupObserverUtils.sendBackupOnPackageResult(mObserver,
mCurrentPackage.packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- executeNextState(BackupState.RUNNING_QUEUE);
- return;
+ return Pair.create(BackupState.RUNNING_QUEUE, null);
}
if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) {
@@ -443,42 +587,41 @@ public class PerformBackupTask implements BackupRestoreTask {
// Don't proceed with a key/value backup for it in this case.
Slog.i(TAG, "Package " + request.packageName
+ " requests full-data rather than key/value; skipping");
- backupManagerService.addBackupTrace(
+ mBackupManagerService.addBackupTrace(
"skipping - fullBackupOnly, completion is noop");
// Shouldn't happen in case of requested backup, as pre-check was done in
// #requestBackup()
BackupObserverUtils.sendBackupOnPackageResult(mObserver,
mCurrentPackage.packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- executeNextState(BackupState.RUNNING_QUEUE);
- return;
+ return Pair.create(BackupState.RUNNING_QUEUE, null);
}
if (AppBackupUtils.appIsStopped(mCurrentPackage.applicationInfo)) {
// The app has been force-stopped or cleared or just installed,
// and not yet launched out of that state, so just as it won't
// receive broadcasts, we won't run it for backup.
- backupManagerService.addBackupTrace("skipping - stopped");
+ mBackupManagerService.addBackupTrace("skipping - stopped");
BackupObserverUtils.sendBackupOnPackageResult(mObserver,
mCurrentPackage.packageName,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- executeNextState(BackupState.RUNNING_QUEUE);
- return;
+ return Pair.create(BackupState.RUNNING_QUEUE, null);
}
- IBackupAgent agent = null;
try {
- backupManagerService.setWorkSource(
+ mBackupManagerService.setWorkSource(
new WorkSource(mCurrentPackage.applicationInfo.uid));
- agent = backupManagerService.bindToAgentSynchronous(mCurrentPackage.applicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
- backupManagerService.addBackupTrace("agent bound; a? = " + (agent != null));
+ IBackupAgent agent =
+ mBackupManagerService.bindToAgentSynchronous(
+ mCurrentPackage.applicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
+ mBackupManagerService.addBackupTrace("agent bound; a? = " + (agent != null));
if (agent != null) {
mAgentBinder = agent;
- mStatus = invokeAgentForBackup(request.packageName, agent);
- // at this point we'll either get a completion callback from the
- // agent, or a timeout message on the main handler. either way, we're
- // done here as long as we're successful so far.
+ Pair<Integer, RemoteResult> statusAndResult =
+ invokeAgentForBackup(request.packageName, agent);
+ mStatus = statusAndResult.first;
+ agentResult = statusAndResult.second;
} else {
// Timeout waiting for the agent
mStatus = BackupTransport.AGENT_ERROR;
@@ -487,62 +630,59 @@ public class PerformBackupTask implements BackupRestoreTask {
// Try for the next one.
Slog.d(TAG, "error in bind/backup", ex);
mStatus = BackupTransport.AGENT_ERROR;
- backupManagerService.addBackupTrace("agent SE");
+ mBackupManagerService.addBackupTrace("agent SE");
}
- } catch (NameNotFoundException e) {
+ } catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Package does not exist; skipping");
- backupManagerService.addBackupTrace("no such package");
+ mBackupManagerService.addBackupTrace("no such package");
mStatus = BackupTransport.AGENT_UNKNOWN;
} finally {
- backupManagerService.setWorkSource(null);
-
- // If there was an agent error, no timeout/completion handling will occur.
- // That means we need to direct to the next state ourselves.
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- BackupState nextState = BackupState.RUNNING_QUEUE;
- mAgentBinder = null;
-
- // An agent-level failure means we reenqueue this one agent for
- // a later retry, but otherwise proceed normally.
- if (mStatus == BackupTransport.AGENT_ERROR) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Agent failure for " + request.packageName
- + " - restaging");
- }
- backupManagerService.dataChangedImpl(request.packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- if (mQueue.isEmpty()) nextState = BackupState.FINAL;
- BackupObserverUtils
- .sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
- BackupManager.ERROR_AGENT_FAILURE);
- } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
- // Failed lookup of the app, so we couldn't bring up an agent, but
- // we're otherwise fine. Just drop it and go on to the next as usual.
- mStatus = BackupTransport.TRANSPORT_OK;
- BackupObserverUtils
- .sendBackupOnPackageResult(mObserver, request.packageName,
- BackupManager.ERROR_PACKAGE_NOT_FOUND);
- } else {
- // Transport-level failure means we reenqueue everything
- revertAndEndBackup();
- nextState = BackupState.FINAL;
- }
+ mBackupManagerService.setWorkSource(null);
+ }
+
+ if (mStatus != BackupTransport.TRANSPORT_OK) {
+ BackupState nextState = BackupState.RUNNING_QUEUE;
+ mAgentBinder = null;
- executeNextState(nextState);
+ // An agent-level failure means we re-enqueue this one agent for
+ // a later retry, but otherwise proceed normally.
+ if (mStatus == BackupTransport.AGENT_ERROR) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Agent failure for " + request.packageName + " - restaging");
+ }
+ mBackupManagerService.dataChangedImpl(request.packageName);
+ mStatus = BackupTransport.TRANSPORT_OK;
+ BackupObserverUtils
+ .sendBackupOnPackageResult(mObserver, mCurrentPackage.packageName,
+ BackupManager.ERROR_AGENT_FAILURE);
+ } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
+ // Failed lookup of the app, so we couldn't bring up an agent, but
+ // we're otherwise fine. Just drop it and go on to the next as usual.
+ mStatus = BackupTransport.TRANSPORT_OK;
+ BackupObserverUtils
+ .sendBackupOnPackageResult(mObserver, request.packageName,
+ BackupManager.ERROR_PACKAGE_NOT_FOUND);
} else {
- // success case
- backupManagerService.addBackupTrace("expecting completion/timeout callback");
+ // Transport-level failure means we re-enqueue everything
+ revertAndEndBackup();
+ nextState = BackupState.FINAL;
}
+
+ return Pair.create(nextState, null);
}
+
+ // Success: caller will figure out the state based on call result
+ mBackupManagerService.addBackupTrace("call made; result = " + agentResult);
+ return Pair.create(null, agentResult);
}
private void finalizeBackup() {
- backupManagerService.addBackupTrace("finishing");
+ mBackupManagerService.addBackupTrace("finishing");
// Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
// backup.
for (BackupRequest req : mQueue) {
- backupManagerService.dataChangedImpl(req.packageName);
+ mBackupManagerService.dataChangedImpl(req.packageName);
}
// Either backup was successful, in which case we of course do not need
@@ -557,65 +697,62 @@ public class PerformBackupTask implements BackupRestoreTask {
// done a backup, we can now record what the current backup dataset token
// is.
String callerLogString = "PBT.finalizeBackup()";
- if ((backupManagerService.getCurrentToken() == 0) && (mStatus
+ if ((mBackupManagerService.getCurrentToken() == 0) && (mStatus
== BackupTransport.TRANSPORT_OK)) {
- backupManagerService.addBackupTrace("success; recording token");
+ mBackupManagerService.addBackupTrace("success; recording token");
try {
- IBackupTransport transport =
- mTransportClient.connectOrThrow(callerLogString);
- backupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
- backupManagerService.writeRestoreTokens();
+ IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
+ mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
+ mBackupManagerService.writeRestoreTokens();
} catch (Exception e) {
// nothing for it at this point, unfortunately, but this will be
// recorded the next time we fully succeed.
Slog.e(TAG, "Transport threw reporting restore set: " + e.getMessage());
- backupManagerService.addBackupTrace("transport threw returning token");
+ mBackupManagerService.addBackupTrace("transport threw returning token");
}
}
// Set up the next backup pass - at this point we can set mBackupRunning
- // to false to allow another pass to fire, because we're done with the
- // state machine sequence and the wakelock is refcounted.
- synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.setBackupRunning(false);
+ // to false to allow another pass to fire
+ synchronized (mBackupManagerService.getQueueLock()) {
+ mBackupManagerService.setBackupRunning(false);
if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
// Make sure we back up everything and perform the one-time init
if (MORE_DEBUG) {
Slog.d(TAG, "Server requires init; rerunning");
}
- backupManagerService.addBackupTrace("init required; rerunning");
+ mBackupManagerService.addBackupTrace("init required; rerunning");
try {
- String name = backupManagerService.getTransportManager()
+ String name = mBackupManagerService.getTransportManager()
.getTransportName(mTransportClient.getTransportComponent());
- backupManagerService.getPendingInits().add(name);
+ mBackupManagerService.getPendingInits().add(name);
} catch (Exception e) {
Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage());
// swallow it and proceed; we don't rely on this
}
clearMetadata();
- backupManagerService.backupNow();
+ mBackupManagerService.backupNow();
}
}
- backupManagerService.clearBackupTrace();
+ mBackupManagerService.clearBackupTrace();
unregisterTask();
- if (!mCancelAll && mStatus == BackupTransport.TRANSPORT_OK &&
+ if (!mCancelled && mStatus == BackupTransport.TRANSPORT_OK &&
mPendingFullBackups != null && !mPendingFullBackups.isEmpty()) {
Slog.d(TAG, "Starting full backups for: " + mPendingFullBackups);
// Acquiring wakelock for PerformFullTransportBackupTask before its start.
- backupManagerService.getWakelock().acquire();
+ mBackupManagerService.getWakelock().acquire();
// The full-backup task is now responsible for calling onFinish() on mListener, which
// was the listener we passed it.
(new Thread(mFullBackupTask, "full-transport-requested")).start();
- } else if (mCancelAll) {
+ } else if (mCancelled) {
mListener.onFinished(callerLogString);
if (mFullBackupTask != null) {
mFullBackupTask.unregisterTask();
}
- BackupObserverUtils.sendBackupFinished(mObserver,
- BackupManager.ERROR_BACKUP_CANCELLED);
+ BackupObserverUtils.sendBackupFinished(mObserver, BackupManager.ERROR_BACKUP_CANCELLED);
} else {
mListener.onFinished(callerLogString);
mFullBackupTask.unregisterTask();
@@ -623,8 +760,7 @@ public class PerformBackupTask implements BackupRestoreTask {
case BackupTransport.TRANSPORT_OK:
case BackupTransport.TRANSPORT_QUOTA_EXCEEDED:
case BackupTransport.TRANSPORT_PACKAGE_REJECTED:
- BackupObserverUtils.sendBackupFinished(mObserver,
- BackupManager.SUCCESS);
+ BackupObserverUtils.sendBackupFinished(mObserver, BackupManager.SUCCESS);
break;
case BackupTransport.TRANSPORT_NOT_INITIALIZED:
BackupObserverUtils.sendBackupFinished(mObserver,
@@ -637,10 +773,9 @@ public class PerformBackupTask implements BackupRestoreTask {
break;
}
}
- mFinished = true;
Slog.i(TAG, "K/V backup pass finished.");
// Only once we're entirely finished do we release the wakelock for k/v backup.
- backupManagerService.getWakelock().release();
+ mBackupManagerService.getWakelock().release();
}
// Remove the PM metadata state. This will generate an init on the next pass.
@@ -649,27 +784,34 @@ public class PerformBackupTask implements BackupRestoreTask {
if (pmState.exists()) pmState.delete();
}
- // Invoke an agent's doBackup() and start a timeout message spinning on the main
- // handler in case it doesn't get back to us.
- private int invokeAgentForBackup(String packageName, IBackupAgent agent) {
+ /**
+ * Returns a {@link Pair}. The first of the pair contains the status. In case the status is
+ * {@link BackupTransport#TRANSPORT_OK}, the second of the pair contains the agent result,
+ * otherwise {@code null}.
+ */
+ private Pair<Integer, RemoteResult> invokeAgentForBackup(
+ String packageName, IBackupAgent agent) {
if (DEBUG) {
Slog.d(TAG, "invokeAgentForBackup on " + packageName);
}
- backupManagerService.addBackupTrace("invoking " + packageName);
+ mBackupManagerService.addBackupTrace("invoking " + packageName);
+
+ File blankStateFile = new File(mStateDir, BLANK_STATE_FILE_NAME);
+ mSavedStateFile = new File(mStateDir, packageName);
+ mBackupDataFile =
+ new File(mBackupManagerService.getDataDir(), packageName + STAGING_FILE_SUFFIX);
+ mNewStateFile = new File(mStateDir, packageName + NEW_STATE_FILE_SUFFIX);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "data file: " + mBackupDataFile);
+ }
- File blankStateName = new File(mStateDir, BLANK_STATE_FILE_NAME);
- mSavedStateName = new File(mStateDir, packageName);
- mBackupDataName =
- new File(backupManagerService.getDataDir(), packageName + STAGING_FILE_SUFFIX);
- mNewStateName = new File(mStateDir, packageName + NEW_STATE_FILE_SUFFIX);
- if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
mSavedState = null;
mBackupData = null;
mNewState = null;
boolean callingAgent = false;
- mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
+ final RemoteResult agentResult;
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
@@ -681,24 +823,21 @@ public class PerformBackupTask implements BackupRestoreTask {
mCurrentPackage.packageName = packageName;
}
- // In a full backup, we pass a null ParcelFileDescriptor as
- // the saved-state "file". For key/value backups we pass the old state if
- // an incremental backup is required, and a blank state otherwise.
mSavedState = ParcelFileDescriptor.open(
- mNonIncremental ? blankStateName : mSavedStateName,
+ (mNonIncremental) ? blankStateFile : mSavedStateFile,
ParcelFileDescriptor.MODE_READ_ONLY |
ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
- mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+ mBackupData = ParcelFileDescriptor.open(mBackupDataFile,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
- if (!SELinux.restorecon(mBackupDataName)) {
- Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
+ if (!SELinux.restorecon(mBackupDataFile)) {
+ Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataFile);
}
- mNewState = ParcelFileDescriptor.open(mNewStateName,
+ mNewState = ParcelFileDescriptor.open(mNewStateFile,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
@@ -710,36 +849,36 @@ public class PerformBackupTask implements BackupRestoreTask {
callingAgent = true;
// Initiate the target's backup pass
- backupManagerService.addBackupTrace("setting timeout");
long kvBackupAgentTimeoutMillis =
mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
- backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
- backupManagerService.addBackupTrace("calling agent doBackup()");
-
- agent.doBackup(
- mSavedState, mBackupData, mNewState, quota, mEphemeralOpToken,
- backupManagerService.getBackupManagerBinder(), transport.getTransportFlags());
+ mBackupManagerService.addBackupTrace("calling agent doBackup()");
+
+ agentResult =
+ remoteCall(
+ callback ->
+ agent.doBackup(
+ mSavedState,
+ mBackupData,
+ mNewState,
+ quota,
+ callback,
+ transport.getTransportFlags()),
+ kvBackupAgentTimeoutMillis);
} catch (Exception e) {
Slog.e(TAG, "Error invoking for backup on " + packageName + ". " + e);
- backupManagerService.addBackupTrace("exception: " + e);
- EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
- e.toString());
+ mBackupManagerService.addBackupTrace("exception: " + e);
+ EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
errorCleanup();
- return callingAgent ? BackupTransport.AGENT_ERROR
- : BackupTransport.TRANSPORT_ERROR;
+ int status =
+ callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR;
+ return Pair.create(status, null);
} finally {
if (mNonIncremental) {
- blankStateName.delete();
+ blankStateFile.delete();
}
}
- // At this point the agent is off and running. The next thing to happen will
- // either be a callback from the agent, at which point we'll process its data
- // for transport, or a timeout. Either way the next phase will happen in
- // response to the TimeoutHandler interface callbacks.
- backupManagerService.addBackupTrace("invoke success");
- return BackupTransport.TRANSPORT_OK;
+ return Pair.create(BackupTransport.TRANSPORT_OK, agentResult);
}
private void failAgent(IBackupAgent agent, String message) {
@@ -829,327 +968,319 @@ public class PerformBackupTask implements BackupRestoreTask {
}
}
- @Override
- @GuardedBy("mCancelLock")
- public void operationComplete(long unusedResult) {
- backupManagerService.removeOperation(mEphemeralOpToken);
- synchronized (mCancelLock) {
- // The agent reported back to us!
- if (mFinished) {
- Slog.d(TAG, "operationComplete received after task finished.");
- return;
- }
-
- if (mBackupData == null) {
- // This callback was racing with our timeout, so we've cleaned up the
- // agent state already and are on to the next thing. We have nothing
- // further to do here: agent state having been cleared means that we've
- // initiated the appropriate next operation.
- final String pkg = (mCurrentPackage != null)
- ? mCurrentPackage.packageName : "[none]";
- if (MORE_DEBUG) {
- Slog.i(TAG, "Callback after agent teardown: " + pkg);
- }
- backupManagerService.addBackupTrace("late opComplete; curPkg = " + pkg);
- return;
- }
+ private BackupState handleAgentResult(long unusedResult) {
+ Preconditions.checkState(mBackupData != null);
- final String pkgName = mCurrentPackage.packageName;
- final long filepos = mBackupDataName.length();
- FileDescriptor fd = mBackupData.getFileDescriptor();
- try {
- // If it's a 3rd party app, see whether they wrote any protected keys
- // and complain mightily if they are attempting shenanigans.
- if (mCurrentPackage.applicationInfo != null &&
- (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
- == 0) {
- ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
- ParcelFileDescriptor.MODE_READ_ONLY);
- BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
- try {
- while (in.readNextHeader()) {
- final String key = in.getKey();
- if (key != null && key.charAt(0) >= 0xff00) {
- // Not okay: crash them and bail.
- failAgent(mAgentBinder, "Illegal backup key: " + key);
- backupManagerService
- .addBackupTrace("illegal key " + key + " from " + pkgName);
- EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
- "bad key");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
- BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY,
- mCurrentPackage,
- BackupManagerMonitor
- .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(null,
- BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY,
- key));
- backupManagerService.getBackupHandler().removeMessages(
- MSG_BACKUP_OPERATION_TIMEOUT);
- BackupObserverUtils
- .sendBackupOnPackageResult(mObserver, pkgName,
- BackupManager.ERROR_AGENT_FAILURE);
- errorCleanup();
- if (MORE_DEBUG) {
- Slog.i(TAG, "Agent failure for " + pkgName
- + " with illegal key: " + key + "; dropped");
- }
- executeNextState(mQueue.isEmpty() ? BackupState.FINAL
- : BackupState.RUNNING_QUEUE);
- return;
+ final String pkgName = mCurrentPackage.packageName;
+ final long filepos = mBackupDataFile.length();
+ FileDescriptor fd = mBackupData.getFileDescriptor();
+ try {
+ // If it's a 3rd party app, see whether they wrote any protected keys
+ // and complain mightily if they are attempting shenanigans.
+ if (mCurrentPackage.applicationInfo != null &&
+ (mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+ == 0) {
+ ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
+ try {
+ while (in.readNextHeader()) {
+ final String key = in.getKey();
+ if (key != null && key.charAt(0) >= 0xff00) {
+ // Not okay: crash them and bail.
+ failAgent(mAgentBinder, "Illegal backup key: " + key);
+ mBackupManagerService
+ .addBackupTrace("illegal key " + key + " from " + pkgName);
+ EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
+ "bad key");
+ mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY,
+ mCurrentPackage,
+ BackupManagerMonitor
+ .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ BackupManagerMonitorUtils.putMonitoringExtra(null,
+ BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY,
+ key));
+ BackupObserverUtils
+ .sendBackupOnPackageResult(mObserver, pkgName,
+ BackupManager.ERROR_AGENT_FAILURE);
+ errorCleanup();
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Agent failure for " + pkgName
+ + " with illegal key: " + key + "; dropped");
}
- in.skipEntityData();
- }
- } finally {
- if (readFd != null) {
- readFd.close();
+
+ return BackupState.RUNNING_QUEUE;
}
+ in.skipEntityData();
+ }
+ } finally {
+ if (readFd != null) {
+ readFd.close();
}
- }
-
- // Piggyback the widget state payload, if any
- writeWidgetPayloadIfAppropriate(fd, pkgName);
- } catch (IOException e) {
- // Hard disk error; recovery/failure policy TBD. For now roll back,
- // but we may want to consider this a transport-level failure (i.e.
- // we're in such a bad state that we can't contemplate doing backup
- // operations any more during this pass).
- Slog.w(TAG, "Unable to save widget state for " + pkgName);
- try {
- Os.ftruncate(fd, filepos);
- } catch (ErrnoException ee) {
- Slog.w(TAG, "Unable to roll back!");
}
}
- // Spin the data off to the transport and proceed with the next stage.
- if (MORE_DEBUG) {
- Slog.v(TAG, "operationComplete(): sending data to transport for "
- + pkgName);
- }
- backupManagerService.getBackupHandler().removeMessages(MSG_BACKUP_OPERATION_TIMEOUT);
- clearAgentState();
- backupManagerService.addBackupTrace("operation complete");
-
- IBackupTransport transport = mTransportClient.connect("PBT.operationComplete()");
- ParcelFileDescriptor backupData = null;
- mStatus = BackupTransport.TRANSPORT_OK;
- long size = 0;
+ // Piggyback the widget state payload, if any
+ writeWidgetPayloadIfAppropriate(fd, pkgName);
+ } catch (IOException e) {
+ // Hard disk error; recovery/failure policy TBD. For now roll back,
+ // but we may want to consider this a transport-level failure (i.e.
+ // we're in such a bad state that we can't contemplate doing backup
+ // operations any more during this pass).
+ Slog.w(TAG, "Unable read backup data or to save widget state for " + pkgName);
try {
- TransportUtils.checkTransportNotNull(transport);
- size = mBackupDataName.length();
- if (size > 0) {
- boolean isNonIncremental = mSavedStateName.length() == 0;
- if (mStatus == BackupTransport.TRANSPORT_OK) {
- backupData = ParcelFileDescriptor.open(mBackupDataName,
- ParcelFileDescriptor.MODE_READ_ONLY);
- backupManagerService.addBackupTrace("sending data to transport");
-
- int userInitiatedFlag =
- mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
- int incrementalFlag =
- isNonIncremental
+ Os.ftruncate(fd, filepos);
+ } catch (ErrnoException ee) {
+ Slog.w(TAG, "Unable to roll back!");
+ }
+ }
+
+ clearAgentState();
+ mBackupManagerService.addBackupTrace("operation complete");
+
+ ParcelFileDescriptor backupData = null;
+ mStatus = BackupTransport.TRANSPORT_OK;
+ long size = 0;
+ try {
+ IBackupTransport transport = mTransportClient.connectOrThrow("PBT.handleAgentResult()");
+ size = mBackupDataFile.length();
+ if (size > 0) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Sending non-empty data to transport for " + pkgName);
+ }
+ boolean isNonIncremental = mSavedStateFile.length() == 0;
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
+ backupData = ParcelFileDescriptor.open(mBackupDataFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ mBackupManagerService.addBackupTrace("sending data to transport");
+
+ int userInitiatedFlag =
+ mUserInitiated ? BackupTransport.FLAG_USER_INITIATED : 0;
+ int incrementalFlag =
+ isNonIncremental
? BackupTransport.FLAG_NON_INCREMENTAL
: BackupTransport.FLAG_INCREMENTAL;
- int flags = userInitiatedFlag | incrementalFlag;
+ int flags = userInitiatedFlag | incrementalFlag;
- mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
- }
+ mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
+ }
- if (isNonIncremental
+ if (isNonIncremental
&& mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
- // TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED is only valid if the backup was
- // incremental, as if the backup is non-incremental there is no state to
- // clear. This avoids us ending up in a retry loop if the transport always
- // returns this code.
- Slog.w(TAG,
- "Transport requested non-incremental but already the case, error");
- backupManagerService.addBackupTrace(
- "Transport requested non-incremental but already the case, error");
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
-
- // TODO - We call finishBackup() for each application backed up, because
- // we need to know now whether it succeeded or failed. Instead, we should
- // hold off on finishBackup() until the end, which implies holding off on
- // renaming *all* the output state files (see below) until that happens.
-
- backupManagerService.addBackupTrace("data delivered: " + mStatus);
- if (mStatus == BackupTransport.TRANSPORT_OK) {
- backupManagerService.addBackupTrace("finishing op on transport");
- mStatus = transport.finishBackup();
- backupManagerService.addBackupTrace("finished: " + mStatus);
- } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
- backupManagerService.addBackupTrace("transport rejected package");
- }
- } else {
- if (MORE_DEBUG) {
- Slog.i(TAG, "no backup data written; not calling transport");
- }
- backupManagerService.addBackupTrace("no data to send");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
- BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- null);
+ // TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED is only valid if the backup was
+ // incremental, as if the backup is non-incremental there is no state to
+ // clear. This avoids us ending up in a retry loop if the transport always
+ // returns this code.
+ Slog.w(TAG, "Transport requested non-incremental but already the case, error");
+ mBackupManagerService.addBackupTrace(
+ "Transport requested non-incremental but already the case, error");
+ mStatus = BackupTransport.TRANSPORT_ERROR;
}
+ mBackupManagerService.addBackupTrace("data delivered: " + mStatus);
if (mStatus == BackupTransport.TRANSPORT_OK) {
- // After successful transport, delete the now-stale data
- // and juggle the files so that next time we supply the agent
- // with the new state file it just created.
- mBackupDataName.delete();
- mNewStateName.renameTo(mSavedStateName);
- BackupObserverUtils
- .sendBackupOnPackageResult(mObserver, pkgName, BackupManager.SUCCESS);
- EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
- backupManagerService.logBackupComplete(pkgName);
+ mBackupManagerService.addBackupTrace("finishing op on transport");
+ mStatus = transport.finishBackup();
+ mBackupManagerService.addBackupTrace("finished: " + mStatus);
} else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
- // The transport has rejected backup of this specific package. Roll it
- // back but proceed with running the rest of the queue.
- mBackupDataName.delete();
- mNewStateName.delete();
- BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
- BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
- EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
- } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
- BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
- BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
- EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
-
- } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
- Slog.i(TAG, "Transport lost data, retrying package");
- backupManagerService.addBackupTrace(
- "Transport lost data, retrying package:" + pkgName);
- BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
- BackupManagerMonitor
- .LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
- /*extras=*/ null);
-
- mBackupDataName.delete();
- mSavedStateName.delete();
- mNewStateName.delete();
-
- // Immediately retry the package by adding it back to the front of the queue.
- // We cannot add @pm@ to the queue because we back it up separately at the start
- // of the backup pass in state BACKUP_PM. Instead we retry this state (see
- // below).
- if (!PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
- mQueue.add(0, new BackupRequest(pkgName));
- }
-
- } else {
- // Actual transport-level failure to communicate the data to the backend
- BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
- BackupManager.ERROR_TRANSPORT_ABORTED);
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
+ mBackupManagerService.addBackupTrace("transport rejected package");
}
- } catch (Exception e) {
- BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
- BackupManager.ERROR_TRANSPORT_ABORTED);
- Slog.e(TAG, "Transport error backing up " + pkgName, e);
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
- mStatus = BackupTransport.TRANSPORT_ERROR;
- } finally {
- try {
- if (backupData != null) backupData.close();
- } catch (IOException e) {
+ } else {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "No backup data written; not calling transport");
}
+ mBackupManagerService.addBackupTrace("no data to send");
+ mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ null);
}
- final BackupState nextState;
- if (mStatus == BackupTransport.TRANSPORT_OK
- || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
- // Success or single-package rejection. Proceed with the next app if any,
- // otherwise we're done.
- nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+ if (mStatus == BackupTransport.TRANSPORT_OK) {
+ // After successful transport, delete the now-stale data
+ // and juggle the files so that next time we supply the agent
+ // with the new state file it just created.
+ mBackupDataFile.delete();
+ mNewStateFile.renameTo(mSavedStateFile);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, pkgName, BackupManager.SUCCESS);
+ EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
+ mBackupManagerService.logBackupComplete(pkgName);
+ } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ // The transport has rejected backup of this specific package. Roll it
+ // back but proceed with running the rest of the queue.
+ mBackupDataFile.delete();
+ mNewStateFile.delete();
+ BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+ BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
+ EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
+ } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+ BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
+ EventLog.writeEvent(EventLogTags.BACKUP_QUOTA_EXCEEDED, pkgName);
} else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
- // We want to immediately retry the current package.
- if (PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
- nextState = BackupState.BACKUP_PM;
- } else {
- // This is an ordinary package so we will have added it back into the queue
- // above. Thus, we proceed processing the queue.
- nextState = BackupState.RUNNING_QUEUE;
+ Slog.i(TAG, "Transport lost data, retrying package");
+ mBackupManagerService.addBackupTrace(
+ "Transport lost data, retrying package:" + pkgName);
+ BackupManagerMonitorUtils.monitorEvent(
+ mMonitor,
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ /*extras=*/ null);
+
+ mBackupDataFile.delete();
+ mSavedStateFile.delete();
+ mNewStateFile.delete();
+
+ // Immediately retry the package by adding it back to the front of the queue.
+ // We cannot add @pm@ to the queue because we back it up separately at the start
+ // of the backup pass in state BACKUP_PM. Instead we retry this state (see
+ // below).
+ if (!PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
+ mQueue.add(0, new BackupRequest(pkgName));
}
- } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
- if (MORE_DEBUG) {
- Slog.d(TAG, "Package " + mCurrentPackage.packageName +
- " hit quota limit on k/v backup");
- }
- if (mAgentBinder != null) {
- try {
- TransportUtils.checkTransportNotNull(transport);
- long quota = transport.getBackupQuota(mCurrentPackage.packageName, false);
- mAgentBinder.doQuotaExceeded(size, quota);
- } catch (Exception e) {
- Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
- }
+ } else {
+ // Actual transport-level failure to communicate the data to the backend
+ BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+ BackupManager.ERROR_TRANSPORT_ABORTED);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
+ }
+ } catch (Exception e) {
+ BackupObserverUtils.sendBackupOnPackageResult(mObserver, pkgName,
+ BackupManager.ERROR_TRANSPORT_ABORTED);
+ Slog.e(TAG, "Transport error backing up " + pkgName, e);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
+ mStatus = BackupTransport.TRANSPORT_ERROR;
+ } finally {
+ try {
+ if (backupData != null) {
+ backupData.close();
}
- nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+ } catch (IOException e) {
+ Slog.w(TAG, "Error closing backup data fd");
+ }
+ }
+
+ final BackupState nextState;
+ if (mStatus == BackupTransport.TRANSPORT_OK
+ || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ // Success or single-package rejection. Proceed with the next app if any,
+ // otherwise we're done.
+ nextState = BackupState.RUNNING_QUEUE;
+
+ } else if (mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+ // We want to immediately retry the current package.
+ if (PACKAGE_MANAGER_SENTINEL.equals(pkgName)) {
+ nextState = BackupState.BACKUP_PM;
} else {
- // Any other error here indicates a transport-level failure. That means
- // we need to halt everything and reschedule everything for next time.
- revertAndEndBackup();
- nextState = BackupState.FINAL;
+ // This is an ordinary package so we will have added it back into the queue
+ // above. Thus, we proceed processing the queue.
+ nextState = BackupState.RUNNING_QUEUE;
}
- executeNextState(nextState);
+ } else if (mStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + mCurrentPackage.packageName +
+ " hit quota limit on k/v backup");
+ }
+ if (mAgentBinder != null) {
+ try {
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow("PBT.handleAgentResult()");
+ long quota = transport.getBackupQuota(mCurrentPackage.packageName, false);
+ mAgentBinder.doQuotaExceeded(size, quota);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
+ }
+ }
+ nextState = BackupState.RUNNING_QUEUE;
+ } else {
+ // Any other error here indicates a transport-level failure. That means
+ // we need to halt everything and reschedule everything for next time.
+ revertAndEndBackup();
+ nextState = BackupState.FINAL;
}
- }
+ return nextState;
+ }
+ /**
+ * Cancels this task. After this method returns there will be no more calls to the transport.
+ *
+ * <p>If this method is executed while an agent is performing a backup, we will stop waiting for
+ * it, disregard its backup data and finalize the task. However, if this method is executed in
+ * between agent calls, the backup data of the last called agent will be sent to
+ * the transport and we will not consider the next agent (nor the rest of the queue), proceeding
+ * to finalize the backup.
+ *
+ * @param cancelAll MUST be {@code true}. Will be removed.
+ */
@Override
- @GuardedBy("mCancelLock")
public void handleCancel(boolean cancelAll) {
- backupManagerService.removeOperation(mEphemeralOpToken);
- synchronized (mCancelLock) {
- if (mFinished) {
- // We have already cancelled this operation.
- if (MORE_DEBUG) {
- Slog.d(TAG, "Ignoring stale cancel. cancelAll=" + cancelAll);
- }
- return;
- }
- mCancelAll = cancelAll;
- final String logPackageName = (mCurrentPackage != null)
- ? mCurrentPackage.packageName
- : "no_package_yet";
- Slog.i(TAG, "Cancel backing up " + logPackageName);
- EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, logPackageName);
- backupManagerService.addBackupTrace(
- "cancel of " + logPackageName + ", cancelAll=" + cancelAll);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
- BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
- mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
- BackupManagerMonitorUtils.putMonitoringExtra(null,
- BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL,
- mCancelAll));
- errorCleanup();
- if (!cancelAll) {
- // The current agent either timed out or was cancelled running doBackup().
- // Restage it for the next time we run a backup pass.
- // !!! TODO: keep track of failure counts per agent, and blacklist those which
- // fail repeatedly (i.e. have proved themselves to be buggy).
- executeNextState(
- mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
- backupManagerService.dataChangedImpl(mCurrentPackage.packageName);
- } else {
- finalizeBackup();
- }
+ Preconditions.checkArgument(cancelAll, "Can't partially cancel a key-value backup task");
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Cancel received");
}
+ mCancelled = true;
+ RemoteCall pendingCall = mPendingCall;
+ if (pendingCall != null) {
+ pendingCall.cancel();
+ }
+ mCancelAcknowledged.block();
+ }
+
+ private void handleAgentTimeout() {
+ String packageName = getPackageNameForLog();
+ Slog.i(TAG, "Agent " + packageName + " timed out");
+ EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
+ mBackupManagerService.addBackupTrace("timeout of " + packageName);
+ mMonitor =
+ BackupManagerMonitorUtils.monitorEvent(
+ mMonitor,
+ BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ BackupManagerMonitorUtils.putMonitoringExtra(
+ null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, false));
+ errorCleanup();
+ }
+
+ private void handleAgentCancelled() {
+ String packageName = getPackageNameForLog();
+ Slog.i(TAG, "Cancel backing up " + packageName);
+ EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
+ mBackupManagerService.addBackupTrace("cancel of " + packageName);
+ errorCleanup();
+ }
+
+ private void finalizeCancelledBackup() {
+ mMonitor =
+ BackupManagerMonitorUtils.monitorEvent(
+ mMonitor,
+ BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ BackupManagerMonitorUtils.putMonitoringExtra(
+ null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, true));
+ finalizeBackup();
+ // finalizeBackup() may call the transport, so we only acknowledge the cancellation here.
+ mCancelAcknowledged.open();
+ }
+
+ private String getPackageNameForLog() {
+ return (mCurrentPackage != null) ? mCurrentPackage.packageName : "no_package_yet";
}
private void revertAndEndBackup() {
if (MORE_DEBUG) {
Slog.i(TAG, "Reverting backup queue - restaging everything");
}
- backupManagerService.addBackupTrace("transport error; reverting");
+ mBackupManagerService.addBackupTrace("transport error; reverting");
// We want to reset the backup schedule based on whatever the transport suggests
// by way of retry/backoff time.
@@ -1162,59 +1293,65 @@ public class PerformBackupTask implements BackupRestoreTask {
Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
delay = 0; // use the scheduler's default
}
- KeyValueBackupJob.schedule(backupManagerService.getContext(), delay,
- backupManagerService.getConstants());
+ KeyValueBackupJob.schedule(mBackupManagerService.getContext(), delay,
+ mBackupManagerService.getConstants());
for (BackupRequest request : mOriginalQueue) {
- backupManagerService.dataChangedImpl(request.packageName);
+ mBackupManagerService.dataChangedImpl(request.packageName);
}
-
}
private void errorCleanup() {
- mBackupDataName.delete();
- mNewStateName.delete();
+ mBackupDataFile.delete();
+ mNewStateFile.delete();
clearAgentState();
}
// Cleanup common to both success and failure cases
private void clearAgentState() {
try {
- if (mSavedState != null) mSavedState.close();
+ if (mSavedState != null) {
+ mSavedState.close();
+ }
} catch (IOException e) {
+ Slog.w(TAG, "Error closing old state fd");
}
try {
- if (mBackupData != null) mBackupData.close();
+ if (mBackupData != null) {
+ mBackupData.close();
+ }
} catch (IOException e) {
+ Slog.w(TAG, "Error closing backup data fd");
}
try {
- if (mNewState != null) mNewState.close();
+ if (mNewState != null) {
+ mNewState.close();
+ }
} catch (IOException e) {
+ Slog.w(TAG, "Error closing new state fd");
}
- synchronized (backupManagerService.getCurrentOpLock()) {
+ synchronized (mBackupManagerService.getCurrentOpLock()) {
// Current-operation callback handling requires the validity of these various
// bits of internal state as an invariant of the operation still being live.
// This means we make sure to clear all of the state in unison inside the lock.
- backupManagerService.getCurrentOperations().remove(mEphemeralOpToken);
mSavedState = mBackupData = mNewState = null;
}
- // If this was a pseudopackage there's no associated Activity Manager state
+ // If this was a pseudo-package there's no associated Activity Manager state
if (mCurrentPackage.applicationInfo != null) {
- backupManagerService.addBackupTrace("unbinding " + mCurrentPackage.packageName);
- backupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
+ mBackupManagerService.addBackupTrace("unbinding " + mCurrentPackage.packageName);
+ mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
}
}
- private void executeNextState(BackupState nextState) {
+ private RemoteResult remoteCall(RemoteCallable<IBackupCallback> remoteCallable, long timeoutMs)
+ throws RemoteException {
+ mPendingCall = new RemoteCall(mCancelled, remoteCallable, timeoutMs);
+ RemoteResult result = mPendingCall.call();
if (MORE_DEBUG) {
- Slog.i(TAG, " => executing next step on "
- + this + " nextState=" + nextState);
+ Slog.v(TAG, "Agent call returned " + result);
}
- backupManagerService.addBackupTrace("executeNextState => " + nextState);
- mCurrentState = nextState;
- Message msg = backupManagerService.getBackupHandler().obtainMessage(
- MSG_BACKUP_RESTORE_STEP, this);
- backupManagerService.getBackupHandler().sendMessage(msg);
+ mPendingCall = null;
+ return result;
}
}
diff --git a/services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java b/services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java
new file mode 100644
index 000000000000..1445cc3065b8
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/FutureBackupCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import android.app.backup.IBackupCallback;
+import android.os.RemoteException;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * An implementation of {@link IBackupCallback} that completes the {@link CompletableFuture}
+ * provided in the constructor with a successful {@link RemoteResult}.
+ */
+public class FutureBackupCallback extends IBackupCallback.Stub {
+ private final CompletableFuture<RemoteResult> mFuture;
+
+ FutureBackupCallback(CompletableFuture<RemoteResult> future) {
+ mFuture = future;
+ }
+
+ @Override
+ public void operationComplete(long result) throws RemoteException {
+ mFuture.complete(RemoteResult.successful(result));
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/RemoteCall.java b/services/backup/java/com/android/server/backup/remote/RemoteCall.java
new file mode 100644
index 000000000000..ac848113f08f
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/RemoteCall.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import android.annotation.WorkerThread;
+import android.app.backup.IBackupCallback;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A wrapper that encapsulates an outbound call from the system process, converting an asynchronous
+ * operation into a synchronous operation with time-out and cancellation built-in. This was built to
+ * be able to call one-way binder methods that accept a {@link IBackupCallback} as a callback and
+ * handle the result inline.
+ *
+ * <p>Create one {@link RemoteCall} object providing the actual call in the form of a {@link
+ * RemoteCallable} that accepts a {@link IBackupCallback}. Perform the call by calling {@link
+ * #call()}, at which point {@link RemoteCall} will execute the callable providing an implementation
+ * of the callback that communicates the result back to this object. Even if the call returns
+ * straight away (which is the case for one-way methods) the method will only return when either the
+ * callback is called, time-out happens, or someone calls {@link #cancel()}.
+ *
+ * <p>This class was designed to have the method {@link #call()} called only once.
+ */
+// TODO: Kick-off callable in dedicated thread (because of local calls, which are synchronous)
+public class RemoteCall {
+ private final RemoteCallable<IBackupCallback> mCallable;
+ private final CompletableFuture<RemoteResult> mFuture;
+ private final long mTimeoutMs;
+
+ /**
+ * Creates a new {@link RemoteCall} object for a given callable.
+ *
+ * @param callable A function that signals its completion by calling {@link
+ * IBackupCallback#operationComplete(long)} on the object provided as a parameter.
+ * @param timeoutMs The time in milliseconds after which {@link #call()} will return with {@link
+ * RemoteResult#FAILED_TIMED_OUT} if the callable hasn't completed and no one canceled. The
+ * time starts to be counted in {@link #call()}.
+ */
+ public RemoteCall(RemoteCallable<IBackupCallback> callable, long timeoutMs) {
+ this(false, callable, timeoutMs);
+ }
+
+ /**
+ * Same as {@link #RemoteCall(RemoteCallable, long)} but with parameter {@code cancelled}.
+ *
+ * @param cancelled Whether the call has already been canceled. It has the same effect of
+ * calling {@link #cancel()} before {@link #call()}.
+ * @see #RemoteCall(RemoteCallable, long)
+ */
+ public RemoteCall(boolean cancelled, RemoteCallable<IBackupCallback> callable, long timeoutMs) {
+ mCallable = callable;
+ mTimeoutMs = timeoutMs;
+ mFuture = new CompletableFuture<>();
+ if (cancelled) {
+ cancel();
+ }
+ }
+
+ /**
+ * Kicks-off the callable provided in the constructor and blocks before returning, waiting for
+ * the first of these to happen:
+ *
+ * <ul>
+ * <li>The callback passed to {@link RemoteCallable} is called with the result. We return a
+ * successful {@link RemoteResult} with the result.
+ * <li>Time-out happens. We return {@link RemoteResult#FAILED_TIMED_OUT}.
+ * <li>Someone calls {@link #cancel()} on this object. We return {@link
+ * RemoteResult#FAILED_CANCELLED}.
+ * </ul>
+ *
+ * <p>This method can't be called from the main thread and was designed to be called only once.
+ *
+ * @return A {@link RemoteResult} with the result of the operation.
+ * @throws RemoteException If the callable throws it.
+ */
+ @WorkerThread
+ public RemoteResult call() throws RemoteException {
+ // If called on the main-thread we would never get a time-out != 0
+ Preconditions.checkState(
+ !Looper.getMainLooper().isCurrentThread(), "Can't call call() on main thread");
+
+ if (!mFuture.isDone()) {
+ if (mTimeoutMs == 0L) {
+ timeOut();
+ } else {
+ Handler.getMain().postDelayed(this::timeOut, mTimeoutMs);
+ mCallable.call(new FutureBackupCallback(mFuture));
+ }
+ }
+ try {
+ return mFuture.get();
+ } catch (InterruptedException e) {
+ return RemoteResult.FAILED_THREAD_INTERRUPTED;
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("Future unexpectedly completed with an exception");
+ }
+ }
+
+ /**
+ * Attempts to cancel the operation. It will only be successful if executed before the callback
+ * is called and before the time-out.
+ *
+ * <p>This method can be called from any thread, any time, including the same thread that called
+ * {@link #call()} (which is obviously only possible if the former is called before the latter).
+ */
+ public void cancel() {
+ mFuture.complete(RemoteResult.FAILED_CANCELLED);
+ }
+
+ private void timeOut() {
+ mFuture.complete(RemoteResult.FAILED_TIMED_OUT);
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/RemoteCallable.java b/services/backup/java/com/android/server/backup/remote/RemoteCallable.java
new file mode 100644
index 000000000000..d2671ff83604
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/RemoteCallable.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import android.os.RemoteException;
+
+/**
+ * Implementations should perform a remote call in {@link #call(Object)}, possibly throwing {@link
+ * RemoteException}.
+ */
+@FunctionalInterface
+public interface RemoteCallable<T> {
+ void call(T input) throws RemoteException;
+}
diff --git a/services/backup/java/com/android/server/backup/remote/RemoteResult.java b/services/backup/java/com/android/server/backup/remote/RemoteResult.java
new file mode 100644
index 000000000000..7f4f4696395e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/RemoteResult.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import android.annotation.IntDef;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents the result of a {@link RemoteCall}. It can be either {@link #FAILED_TIMED_OUT}, {@link
+ * #FAILED_CANCELLED}, {@link #FAILED_THREAD_INTERRUPTED} or a successful result, in which case
+ * {@link #get()} returns its value.
+ *
+ * <p>Use {@link #succeeded()} to check for successful result, or direct identity comparison to
+ * check for specific failures, like {@code result == RemoteResult.FAILED_CANCELLED}.
+ */
+public class RemoteResult {
+ public static final RemoteResult FAILED_TIMED_OUT = new RemoteResult(Type.FAILED_TIMED_OUT, 0);
+ public static final RemoteResult FAILED_CANCELLED = new RemoteResult(Type.FAILED_CANCELLED, 0);
+ public static final RemoteResult FAILED_THREAD_INTERRUPTED =
+ new RemoteResult(Type.FAILED_THREAD_INTERRUPTED, 0);
+
+ public static RemoteResult successful(long value) {
+ return new RemoteResult(Type.SUCCESS, value);
+ }
+
+ @Type private final int mType;
+ private final long mValue;
+
+ private RemoteResult(@Type int type, long value) {
+ mType = type;
+ mValue = value;
+ }
+
+ public boolean succeeded() {
+ return mType == Type.SUCCESS;
+ }
+
+ /**
+ * Returns the value of this result.
+ *
+ * @throws IllegalStateException in case this is not a successful result.
+ */
+ public long get() {
+ Preconditions.checkState(succeeded(), "Can't obtain value of failed result");
+ return mValue;
+ }
+
+ @Override
+ public String toString() {
+ return "RemoteResult{" + toStringDescription() + "}";
+ }
+
+ private String toStringDescription() {
+ switch (mType) {
+ case Type.SUCCESS:
+ return Long.toString(mValue);
+ case Type.FAILED_TIMED_OUT:
+ return "FAILED_TIMED_OUT";
+ case Type.FAILED_CANCELLED:
+ return "FAILED_CANCELLED";
+ case Type.FAILED_THREAD_INTERRUPTED:
+ return "FAILED_THREAD_INTERRUPTED";
+ }
+ throw new AssertionError("Unknown type");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof RemoteResult)) {
+ return false;
+ }
+ RemoteResult that = (RemoteResult) o;
+ return mType == that.mType && mValue == that.mValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mValue);
+ }
+
+ @IntDef({
+ Type.SUCCESS,
+ Type.FAILED_TIMED_OUT,
+ Type.FAILED_CANCELLED,
+ Type.FAILED_THREAD_INTERRUPTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Type {
+ int SUCCESS = 0;
+ int FAILED_TIMED_OUT = 1;
+ int FAILED_CANCELLED = 2;
+ int FAILED_THREAD_INTERRUPTED = 3;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
new file mode 100644
index 000000000000..28d85a694d4b
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import android.app.backup.IBackupCallback;
+import android.app.backup.IBackupManager;
+import android.os.RemoteException;
+
+import com.android.server.backup.BackupManagerService;
+
+/**
+ * An implementation of {@link IBackupCallback} that routes the result to {@link
+ * BackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token provided
+ * in the constructor.
+ */
+public class ServiceBackupCallback extends IBackupCallback.Stub {
+ private final IBackupManager mBackupManager;
+ private final int mToken;
+
+ public ServiceBackupCallback(IBackupManager backupManager, int token) {
+ mBackupManager = backupManager;
+ mToken = token;
+ }
+
+ @Override
+ public void operationComplete(long result) throws RemoteException {
+ mBackupManager.opComplete(mToken, result);
+ }
+}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index c63b8f4191de..9a7c345808f2 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -34,7 +34,6 @@ import android.util.Slog;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
-import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -131,6 +130,7 @@ public class BinderCallsStatsService extends Binder {
public static class LifeCycle extends SystemService {
private BinderCallsStatsService mService;
+ private BinderCallsStats mBinderCallsStats;
public LifeCycle(Context context) {
super(context);
@@ -138,9 +138,9 @@ public class BinderCallsStatsService extends Binder {
@Override
public void onStart() {
- BinderCallsStats binderCallsStats = new BinderCallsStats(new Random());
- mService = new BinderCallsStatsService(binderCallsStats);
- LocalServices.addService(Internal.class, new Internal(binderCallsStats));
+ mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+ mService = new BinderCallsStatsService(mBinderCallsStats);
+ publishLocalService(Internal.class, new Internal(mBinderCallsStats));
publishBinderService("binder_calls_stats", mService);
boolean detailedTrackingEnabled = SystemProperties.getBoolean(
PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
@@ -149,7 +149,7 @@ public class BinderCallsStatsService extends Binder {
Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
+ PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
+ " or via dumpsys binder_calls_stats --enable-detailed-tracking");
- binderCallsStats.setDetailedTracking(true);
+ mBinderCallsStats.setDetailedTracking(true);
}
}
@@ -157,6 +157,7 @@ public class BinderCallsStatsService extends Binder {
public void onBootPhase(int phase) {
if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
mService.systemReady(getContext());
+ mBinderCallsStats.systemReady(getContext());
}
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2054e0a97e35..6b3f8f8eb216 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -500,6 +500,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final IpConnectivityLog mMetricsLog;
+ @GuardedBy("mBandwidthRequests")
+ private final SparseArray<Integer> mBandwidthRequests = new SparseArray(10);
+
@VisibleForTesting
final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
@@ -2107,6 +2110,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.println("currently holding WakeLock for: " + (duration / 1000) + "s");
}
mWakelockLogs.reverseDump(fd, pw, args);
+
+ pw.println();
+ pw.println("bandwidth update requests (by uid):");
+ pw.increaseIndent();
+ synchronized (mBandwidthRequests) {
+ for (int i = 0; i < mBandwidthRequests.size(); i++) {
+ pw.println("[" + mBandwidthRequests.keyAt(i)
+ + "]: " + mBandwidthRequests.valueAt(i));
+ }
+ }
+ pw.decreaseIndent();
+
pw.decreaseIndent();
}
}
@@ -4267,6 +4282,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (nai != null) {
nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
+ synchronized (mBandwidthRequests) {
+ final int uid = Binder.getCallingUid();
+ Integer uidReqs = mBandwidthRequests.get(uid);
+ if (uidReqs == null) {
+ uidReqs = new Integer(0);
+ }
+ mBandwidthRequests.put(uid, ++uidReqs);
+ }
return true;
}
return false;
@@ -5863,4 +5886,4 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.println(" Get airplane mode.");
}
}
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9631e469f79a..abcd6ef1140f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1495,10 +1495,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
try {
- mConnector.execute("idletimer", "add", iface, Integer.toString(timeout),
- Integer.toString(type));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.idletimerAddInterface(iface, timeout, Integer.toString(type));
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
@@ -1529,10 +1528,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
try {
- mConnector.execute("idletimer", "remove", iface,
- Integer.toString(params.timeout), Integer.toString(params.type));
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.idletimerRemoveInterface(iface,
+ params.timeout, Integer.toString(params.type));
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
mActiveIdleTimers.remove(iface);
mDaemonHandler.post(new Runnable() {
diff --git a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
index 02cc6d522030..10a1b90b9135 100644
--- a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
@@ -114,7 +114,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
if (mBundle != null) {
try {
if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- mStatusBarService.onFingerprintHelp(
+ mStatusBarService.onBiometricHelp(
mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
}
return false; // acquisition continues
@@ -143,7 +143,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
}
if (mBundle != null) {
try {
- mStatusBarService.onFingerprintError(
+ mStatusBarService.onBiometricError(
mFingerprintManager.getErrorString(error, vendorCode));
} catch (RemoteException e) {
Slog.e(getLogTag(), "Remote exception when sending error", e);
@@ -162,9 +162,9 @@ public abstract class AuthenticationClient extends ClientMonitor {
if (mBundle != null) {
try {
if (authenticated) {
- mStatusBarService.onFingerprintAuthenticated();
+ mStatusBarService.onBiometricAuthenticated();
} else {
- mStatusBarService.onFingerprintHelp(getContext().getResources().getString(
+ mStatusBarService.onBiometricHelp(getContext().getResources().getString(
com.android.internal.R.string.fingerprint_not_recognized));
}
} catch (RemoteException e) {
@@ -224,7 +224,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
// Send the lockout message to the system dialog
if (mBundle != null) {
- mStatusBarService.onFingerprintError(
+ mStatusBarService.onBiometricError(
mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
}
} catch (RemoteException e) {
@@ -263,7 +263,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
// If authenticating with system dialog, show the dialog
if (mBundle != null) {
try {
- mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
+ mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver);
} catch (RemoteException e) {
Slog.e(getLogTag(), "Unable to show fingerprint dialog", e);
}
@@ -301,7 +301,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
// after BiometricPrompt.HIDE_DIALOG_DELAY
if (mBundle != null && !mDialogDismissed && !mInLockout) {
try {
- mStatusBarService.hideFingerprintDialog();
+ mStatusBarService.hideBiometricDialog();
} catch (RemoteException e) {
Slog.e(getLogTag(), "Unable to hide fingerprint dialog", e);
}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index dea9309d550b..d154830062f6 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -318,6 +318,17 @@ final class Constants {
static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
"persist.sys.hdmi.property_sytem_audio_device_arc_port";
+ /**
+ * Property to strip local audio of amplifier and use local speaker
+ * when TV does not support system audio mode.
+ *
+ * <p>This property applies to device with both audio system/playback types.
+ * <p>True means using local speaker when TV does not support system audio.
+ * <p>False means passing audio to TV. Default is true.
+ */
+ static final String PROPERTY_STRIP_AUDIO_TV_NO_SYSTEM_AUDIO =
+ "persist.sys.hdmi.property_strip_audio_tv_no_system_audio";
+
static final int RECORDING_TYPE_DIGITAL_RF = 1;
static final int RECORDING_TYPE_ANALOGUE_RF = 2;
static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index ca85249b1f87..14af15f0a3fd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -327,18 +327,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
!isPhysicalAddressMeOrBelow(targetPhysicalAddress)) {
switchToAudioInput();
}
- // Mute device when feature is turned off and unmute device when feature is turned on
- boolean currentMuteStatus =
- mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
- if (currentMuteStatus == newSystemAudioMode) {
- mService.getAudioManager()
- .adjustStreamVolume(
- AudioManager.STREAM_MUSIC,
- newSystemAudioMode
- ? AudioManager.ADJUST_UNMUTE
- : AudioManager.ADJUST_MUTE,
- 0);
- }
+ // TODO(b/80297700): Mute device when TV terminates the system audio control
updateAudioManagerForSystemAudio(newSystemAudioMode);
synchronized (mLock) {
if (mSystemAudioActivated != newSystemAudioMode) {
diff --git a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
index a3949a4521c2..596a4c02e780 100644
--- a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
+++ b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -16,8 +16,6 @@
package com.android.server.job.controllers.idle;
-import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -35,20 +33,27 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
- public static final String ACTION_FORCE_IDLE = "com.android.server.ACTION_FORCE_IDLE";
- public static final String ACTION_UNFORCE_IDLE = "com.android.server.ACTION_UNFORCE_IDLE";
+ public static final String ACTION_GARAGE_MODE_ON =
+ "com.android.server.jobscheduler.GARAGE_MODE_ON";
+ public static final String ACTION_GARAGE_MODE_OFF =
+ "com.android.server.jobscheduler.GARAGE_MODE_OFF";
+
+ public static final String ACTION_FORCE_IDLE = "com.android.server.jobscheduler.FORCE_IDLE";
+ public static final String ACTION_UNFORCE_IDLE = "com.android.server.jobscheduler.UNFORCE_IDLE";
// After construction, mutations of idle/screen-on state will only happen
// on the main looper thread, either in onReceive() or in an alarm callback.
private boolean mIdle;
- private boolean mScreenOn;
+ private boolean mGarageModeOn;
+ private boolean mForced;
private IdlenessListener mIdleListener;
public CarIdlenessTracker() {
// At boot we presume that the user has just "interacted" with the
// device in some meaningful way.
mIdle = false;
- mScreenOn = true;
+ mGarageModeOn = false;
+ mForced = false;
}
@Override
@@ -62,9 +67,9 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen
IntentFilter filter = new IntentFilter();
- // Screen state
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
+ // State of GarageMode
+ filter.addAction(ACTION_GARAGE_MODE_ON);
+ filter.addAction(ACTION_GARAGE_MODE_OFF);
// Debugging/instrumentation
filter.addAction(ACTION_FORCE_IDLE);
@@ -77,7 +82,7 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen
@Override
public void dump(PrintWriter pw) {
pw.print(" mIdle: "); pw.println(mIdle);
- pw.print(" mScreenOn: "); pw.println(mScreenOn);
+ pw.print(" mGarageModeOn: "); pw.println(mGarageModeOn);
}
@Override
@@ -88,47 +93,58 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen
// Check for forced actions
if (action.equals(ACTION_FORCE_IDLE)) {
logIfDebug("Forcing idle...");
- enterIdleState(true);
+ setForceIdleState(true);
} else if (action.equals(ACTION_UNFORCE_IDLE)) {
logIfDebug("Unforcing idle...");
- exitIdleState(true);
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- logIfDebug("Going idle...");
- mScreenOn = false;
- enterIdleState(false);
- } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
- logIfDebug("exiting idle...");
- mScreenOn = true;
- exitIdleState(true);
+ setForceIdleState(false);
+ } else if (action.equals(ACTION_GARAGE_MODE_ON)) {
+ logIfDebug("GarageMode is on...");
+ mGarageModeOn = true;
+ updateIdlenessState();
+ } else if (action.equals(ACTION_GARAGE_MODE_OFF)) {
+ logIfDebug("GarageMode is off...");
+ mGarageModeOn = false;
+ updateIdlenessState();
} else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
- if (!mScreenOn) {
+ if (!mGarageModeOn) {
logIfDebug("Idle trigger fired...");
- enterIdleState(false);
+ triggerIdlenessOnce();
} else {
logIfDebug("TRIGGER_IDLE received but not changing state; idle="
- + mIdle + " screen=" + mScreenOn);
+ + mIdle + " screen=" + mGarageModeOn);
}
}
}
- private void enterIdleState(boolean forced) {
- if (!forced && mIdle) {
- // Already idle and don't need to trigger callbacks since not forced
- logIfDebug("Device is already considered idle");
- return;
+ private void setForceIdleState(boolean forced) {
+ mForced = forced;
+ updateIdlenessState();
+ }
+
+ private void updateIdlenessState() {
+ final boolean newState = (mForced || mGarageModeOn);
+ if (mIdle != newState) {
+ // State of idleness changed. Notifying idleness controller
+ logIfDebug("Device idleness changed. New idle=" + newState);
+ mIdle = newState;
+ mIdleListener.reportNewIdleState(mIdle);
+ } else {
+ // Nothing changed, device idleness is in the same state as new state
+ logIfDebug("Device idleness is the same. Current idle=" + newState);
}
- mIdle = true;
- mIdleListener.reportNewIdleState(mIdle);
}
- private void exitIdleState(boolean forced) {
- if (!forced && !mIdle) {
- // Already out of idle and don't need to trigger callbacks since not forced
- logIfDebug("Device is already considered not idle");
- return;
+ private void triggerIdlenessOnce() {
+ // This is simply triggering idleness once until some constraint will switch it back off
+ if (mIdle) {
+ // Already in idle state. Nothing to do
+ logIfDebug("Device is already idle");
+ } else {
+ // Going idle once
+ logIfDebug("Device is going idle once");
+ mIdle = true;
+ mIdleListener.reportNewIdleState(mIdle);
}
- mIdle = false;
- mIdleListener.reportNewIdleState(mIdle);
}
private void logIfDebug(String msg) {
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 74930c8821d9..4243f02a15b2 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -45,7 +45,7 @@ import java.util.function.Consumer;
/*
* Local flag to enable debug logging.
*/
- private static final boolean DEBUG_LOG_ENABLED = true;
+ private static final boolean DEBUG_LOG_ENABLED = false;
/*
* The context of the service.
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index dc95d41090b2..27509deb958f 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -77,6 +77,11 @@ public class ContextHubService extends IContextHubService.Stub {
private static final int OS_APP_INSTANCE = -1;
+ /*
+ * Local flag to enable debug logging.
+ */
+ private static final boolean DEBUG_LOG_ENABLED = false;
+
private final Context mContext;
private final Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
@@ -779,12 +784,16 @@ public class ContextHubService extends IContextHubService.Stub {
int msgVersion = 0;
int callbacksCount = mCallbacksList.beginBroadcast();
- Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
- contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
- + callbacksCount);
+ if (DEBUG_LOG_ENABLED) {
+ Log.v(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle "
+ + contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
+ + callbacksCount);
+ }
if (callbacksCount < 1) {
- Log.v(TAG, "No message callbacks registered.");
+ if (DEBUG_LOG_ENABLED) {
+ Log.v(TAG, "No message callbacks registered.");
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d0789b734953..de53427a93b8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4031,6 +4031,7 @@ public class NotificationManagerService extends SystemService {
+ " notification=" + notification);
}
checkCallerIsSystemOrSameApp(pkg);
+ checkRestrictedCategories(notification);
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
@@ -5272,6 +5273,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
ArrayList<ArrayList<Notification.Action>> smartActionsBefore = new ArrayList<>(N);
+ ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -5284,6 +5286,7 @@ public class NotificationManagerService extends SystemService {
userSentimentBefore.add(r.getUserSentiment());
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
smartActionsBefore.add(r.getSmartActions());
+ smartRepliesBefore.add(r.getSmartReplies());
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -5299,7 +5302,8 @@ public class NotificationManagerService extends SystemService {
|| !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
|| !Objects.equals(suppressVisuallyBefore.get(i),
r.getSuppressedVisualEffects())
- || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())) {
+ || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())
+ || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
mHandler.scheduleSendRankingUpdate();
return;
}
@@ -6197,6 +6201,26 @@ public class NotificationManagerService extends SystemService {
checkCallerIsSameApp(pkg);
}
+ /**
+ * Check if the notification is of a category type that is restricted to system use only,
+ * if so throw SecurityException
+ */
+ private void checkRestrictedCategories(final Notification notification) {
+ try {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ return;
+ }
+ } catch (RemoteException re) {
+ if (DBG) Log.e(TAG, "Unable to confirm if it's safe to skip category "
+ + "restrictions check thus the check will be done anyway");
+ }
+ if (Notification.CATEGORY_CAR_EMERGENCY.equals(notification.category)
+ || Notification.CATEGORY_CAR_WARNING.equals(notification.category)
+ || Notification.CATEGORY_CAR_INFORMATION.equals(notification.category)) {
+ checkCallerIsSystem();
+ }
+ }
+
private boolean isCallerInstantApp(String pkg) {
// System is always allowed to act for ephemeral apps.
if (isCallerSystemOrPhone()) {
@@ -6276,6 +6300,7 @@ public class NotificationManagerService extends SystemService {
Bundle userSentiment = new Bundle();
Bundle hidden = new Bundle();
Bundle smartActions = new Bundle();
+ Bundle smartReplies = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -6304,6 +6329,7 @@ public class NotificationManagerService extends SystemService {
userSentiment.putInt(key, record.getUserSentiment());
hidden.putBoolean(key, record.isHidden());
smartActions.putParcelableArrayList(key, record.getSmartActions());
+ smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
}
final int M = keys.size();
String[] keysAr = keys.toArray(new String[M]);
@@ -6315,7 +6341,7 @@ public class NotificationManagerService extends SystemService {
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
- smartActions);
+ smartActions, smartReplies);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f32b3383d480..469828b73ef7 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -162,6 +162,7 @@ public final class NotificationRecord {
private String mGroupLogTag;
private String mChannelIdLogTag;
private ArrayList<Notification.Action> mSmartActions;
+ private ArrayList<CharSequence> mSmartReplies;
private final List<Adjustment> mAdjustments;
private final NotificationStats mStats;
@@ -637,6 +638,9 @@ public final class NotificationRecord {
if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
}
+ if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
+ setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+ }
}
}
}
@@ -1064,6 +1068,14 @@ public final class NotificationRecord {
return mSmartActions;
}
+ public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
+ mSmartReplies = smartReplies;
+ }
+
+ public ArrayList<CharSequence> getSmartReplies() {
+ return mSmartReplies;
+ }
+
/**
* @return all {@link Uri} that should have permission granted to whoever
* will be rendering it. This list has already been vetted to only
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e46c03edc77d..a8b92a60ac7c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -288,6 +288,8 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.DisplayPolicy;
+import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -500,8 +502,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mStatusBar = null;
private final int[] mStatusBarHeightForRotation = new int[4];
WindowState mNavigationBar = null;
- boolean mHasNavigationBar = false;
- boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
@NavigationBarPosition
int mNavigationBarPosition = NAV_BAR_BOTTOM;
int[] mNavigationBarHeightForRotationDefault = new int[4];
@@ -556,8 +556,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
volatile boolean mRecentsVisible;
volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
volatile boolean mPictureInPictureVisible;
- // Written by vr manager thread, only read in this class.
- volatile private boolean mPersistentVrModeEnabled;
volatile private boolean mDismissImeOnBackKeyPressed;
// Used to hold the last user key used to wake the device. This helps us prevent up events
@@ -567,40 +565,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mRecentAppsHeldModifiers;
boolean mLanguageSwitchKeyPressed;
- int mLidState = LID_ABSENT;
int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT;
boolean mHaveBuiltInKeyboard;
boolean mSystemReady;
boolean mSystemBooted;
- boolean mHdmiPlugged;
HdmiControl mHdmiControl;
IUiModeManager mUiModeManager;
int mUiMode;
- int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
- int mLidOpenRotation;
- int mCarDockRotation;
- int mDeskDockRotation;
- int mUndockedHdmiRotation;
- int mDemoHdmiRotation;
- boolean mDemoHdmiRotationLock;
- int mDemoRotation;
- boolean mDemoRotationLock;
boolean mWakeGestureEnabledSetting;
MyWakeGestureListener mWakeGestureListener;
- // Default display does not rotate, apps that require non-default orientation will have to
- // have the orientation emulated.
- private boolean mForceDefaultOrientation = false;
-
- int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
- int mUserRotation = Surface.ROTATION_0;
-
- boolean mSupportAutoRotation;
- int mAllowAllRotations = -1;
- boolean mCarDockEnablesAccelerometer;
- boolean mDeskDockEnablesAccelerometer;
int mLidKeyboardAccessibility;
int mLidNavigationAccessibility;
boolean mLidControlsScreenLock;
@@ -613,14 +589,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mLongPressOnBackBehavior;
int mShortPressOnSleepBehavior;
int mShortPressOnWindowBehavior;
- volatile boolean mAwake;
- boolean mScreenOnEarly;
- boolean mScreenOnFully;
- ScreenOnListener mScreenOnListener;
- boolean mKeyguardDrawComplete;
- boolean mWindowManagerDrawComplete;
- boolean mOrientationSensorEnabled = false;
- int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mHasSoftInput = false;
boolean mTranslucentDecorEnabled = true;
boolean mUseTvRouting;
@@ -729,18 +697,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Behavior of Back button while in-call and screen on
int mIncallBackBehavior;
- // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
- int mShowRotationSuggestions;
-
// Whether system navigation keys are enabled
boolean mSystemNavigationKeysEnabled;
- Display mDisplay;
-
- int mLandscapeRotation = 0; // default landscape rotation
- int mSeascapeRotation = 0; // "other" landscape rotation, 180 degrees from mLandscapeRotation
- int mPortraitRotation = 0; // default portrait rotation
- int mUpsideDownRotation = 0; // "other" portrait rotation
+ // TODO(b/111361251): Remove default when the dependencies are multi-display ready.
+ Display mDefaultDisplay;
+ DisplayRotation mDefaultDisplayRotation;
+ DisplayPolicy mDefaultDisplayPolicy;
+ WindowOrientationListener mDefaultOrientationListener;
// What we do when the user long presses on home
private int mLongPressOnHomeBehavior;
@@ -963,7 +927,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private UEventObserver mHDMIObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
- setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
+ mDefaultDisplayPolicy.setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
}
};
@@ -988,12 +952,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
- Settings.System.ACCELEROMETER_ROTATION), false, this,
- UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.System.getUriFor(
- Settings.System.USER_ROTATION), false, this,
- UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.System.getUriFor(
@@ -1006,9 +964,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
- UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -1043,53 +998,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- class MyOrientationListener extends WindowOrientationListener {
-
- private SparseArray<Runnable> mRunnableCache;
-
- MyOrientationListener(Context context, Handler handler) {
- super(context, handler);
- mRunnableCache = new SparseArray<>(5);
- }
-
- private class UpdateRunnable implements Runnable {
- private final int mRotation;
- UpdateRunnable(int rotation) {
- mRotation = rotation;
- }
-
- @Override
- public void run() {
- // send interaction hint to improve redraw performance
- mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
- if (isRotationChoicePossible(mCurrentAppOrientation)) {
- final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,
- mRotation);
- sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
- } else {
- updateRotation(false);
- }
- }
- }
-
- @Override
- public void onProposedRotationChanged(int rotation) {
- if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
- Runnable r = mRunnableCache.get(rotation, null);
- if (r == null){
- r = new UpdateRunnable(rotation);
- mRunnableCache.put(rotation, r);
- }
- mHandler.post(r);
- }
- }
- MyOrientationListener mOrientationListener;
-
final IPersistentVrStateCallbacks mPersistentVrModeListener =
new IPersistentVrStateCallbacks.Stub() {
@Override
public void onPersistentVrStateChanged(boolean enabled) {
- mPersistentVrModeEnabled = enabled;
+ mDefaultDisplayPolicy.setPersistentVrModeEnabled(enabled);
}
};
@@ -1171,100 +1084,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- /*
- * We always let the sensor be switched on by default except when
- * the user has explicitly disabled sensor based rotation or when the
- * screen is switched off.
- */
- boolean needSensorRunningLp() {
- if (mSupportAutoRotation) {
- if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
- || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
- || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
- // If the application has explicitly requested to follow the
- // orientation, then we need to turn the sensor on.
- return true;
- }
- }
- if ((mCarDockEnablesAccelerometer && mDockMode == Intent.EXTRA_DOCK_STATE_CAR) ||
- (mDeskDockEnablesAccelerometer && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
- // enable accelerometer if we are docked in a dock that enables accelerometer
- // orientation management,
- return true;
- }
- if (mUserRotationMode == USER_ROTATION_LOCKED) {
- // If the setting for using the sensor by default is enabled, then
- // we will always leave it on. Note that the user could go to
- // a window that forces an orientation that does not use the
- // sensor and in theory we could turn it off... however, when next
- // turning it on we won't have a good value for the current
- // orientation for a little bit, which can cause orientation
- // changes to lag, so we'd like to keep it always on. (It will
- // still be turned off when the screen is off.)
-
- // When locked we can provide rotation suggestions users can approve to change the
- // current screen rotation. To do this the sensor needs to be running.
- return mSupportAutoRotation &&
- mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
- }
- return mSupportAutoRotation;
- }
-
- /*
- * Various use cases for invoking this function
- * screen turning off, should always disable listeners if already enabled
- * screen turned on and current app has sensor based orientation, enable listeners
- * if not already enabled
- * screen turned on and current app does not have sensor orientation, disable listeners if
- * already enabled
- * screen turning on and current app has sensor based orientation, enable listeners if needed
- * screen turning on and current app has nosensor based orientation, do nothing
- */
- void updateOrientationListenerLp() {
- if (!mOrientationListener.canDetectOrientation()) {
- // If sensor is turned off or nonexistent for some reason
- return;
- }
- // Could have been invoked due to screen turning on or off or
- // change of the currently visible window's orientation.
- if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly
- + ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation
- + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled
- + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
- + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-
- boolean disable = true;
- // Note: We postpone the rotating of the screen until the keyguard as well as the
- // window manager have reported a draw complete or the keyguard is going away in dismiss
- // mode.
- if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) {
- if (needSensorRunningLp()) {
- disable = false;
- //enable listener if not already enabled
- if (!mOrientationSensorEnabled) {
- // Don't clear the current sensor orientation if the keyguard is going away in
- // dismiss mode. This allows window manager to use the last sensor reading to
- // determine the orientation vs. falling back to the last known orientation if
- // the sensor reading was cleared which can cause it to relaunch the app that
- // will show in the wrong orientation first before correcting leading to app
- // launch delays.
- mOrientationListener.enable(true /* clearCurrentRotation */);
- if(localLOGV) Slog.v(TAG, "Enabling listeners");
- mOrientationSensorEnabled = true;
- }
- }
- }
- //check if sensors need to be disabled
- if (disable && mOrientationSensorEnabled) {
- mOrientationListener.disable();
- if(localLOGV) Slog.v(TAG, "Disabling listeners");
- mOrientationSensorEnabled = false;
- }
- }
-
private void interceptBackKeyDown() {
MetricsLogger.count(mContext, "key_back_down", 1);
// Reset back key state for long press
@@ -1490,7 +1309,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void powerPress(long eventTime, boolean interactive, int count) {
- if (mScreenOnEarly && !mScreenOnFully) {
+ if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
Slog.i(TAG, "Suppressed redundant power key press while "
+ "already in the process of turning the screen on.");
return;
@@ -1990,6 +1809,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mContext.getResources().getConfiguration().isScreenRound();
}
+ @Override
+ public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
+ mDefaultDisplay = displayContentInfo.getDisplay();
+ mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
+ mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
+ mDefaultOrientationListener = mDefaultDisplayRotation.getOrientationListener();
+ }
+
/** {@inheritDoc} */
@Override
public void init(Context context, IWindowManager windowManager,
@@ -2046,10 +1873,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHandler = new PolicyHandler();
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
- mOrientationListener = new MyOrientationListener(mContext, mHandler);
- try {
- mOrientationListener.setCurrentRotation(windowManager.getDefaultDisplayRotation());
- } catch (RemoteException ex) { }
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
mShortcutManager = new ShortcutManager(context);
@@ -2080,20 +1903,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"PhoneWindowManager.mPowerKeyWakeLock");
mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
- mSupportAutoRotation = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_supportAutoRotation);
- mLidOpenRotation = readRotation(
- com.android.internal.R.integer.config_lidOpenRotation);
- mCarDockRotation = readRotation(
- com.android.internal.R.integer.config_carDockRotation);
- mDeskDockRotation = readRotation(
- com.android.internal.R.integer.config_deskDockRotation);
- mUndockedHdmiRotation = readRotation(
- com.android.internal.R.integer.config_undockedHdmiRotation);
- mCarDockEnablesAccelerometer = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_carDockEnablesAccelerometer);
- mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
mLidKeyboardAccessibility = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lidKeyboardAccessibility);
mLidNavigationAccessibility = mContext.getResources().getInteger(
@@ -2167,8 +1976,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Intent intent = context.registerReceiver(mDockReceiver, filter);
if (intent != null) {
// Retrieve current sticky dock event broadcast.
- mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
- Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED));
}
// register for dream-related broadcasts
@@ -2222,11 +2031,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
public void onDown() {
- mOrientationListener.onTouchStart();
+ mDefaultOrientationListener.onTouchStart();
}
@Override
public void onUpOrCancel() {
- mOrientationListener.onTouchEnd();
+ mDefaultOrientationListener.onTouchEnd();
}
@Override
public void onMouseHoverAtTop() {
@@ -2333,110 +2142,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
com.android.internal.R.integer.config_navBarOpacityMode);
}
- @Override
- public void setInitialDisplaySize(Display display, int width, int height, int density) {
- // This method might be called before the policy has been fully initialized
- // or for other displays we don't care about.
- // TODO(multi-display): Define policy for secondary displays.
- if (mContext == null || display.getDisplayId() != DEFAULT_DISPLAY) {
- return;
- }
- mDisplay = display;
-
- final Resources res = mContext.getResources();
- int shortSize, longSize;
- if (width > height) {
- shortSize = height;
- longSize = width;
- mLandscapeRotation = Surface.ROTATION_0;
- mSeascapeRotation = Surface.ROTATION_180;
- if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
- mPortraitRotation = Surface.ROTATION_90;
- mUpsideDownRotation = Surface.ROTATION_270;
- } else {
- mPortraitRotation = Surface.ROTATION_270;
- mUpsideDownRotation = Surface.ROTATION_90;
- }
- } else {
- shortSize = width;
- longSize = height;
- mPortraitRotation = Surface.ROTATION_0;
- mUpsideDownRotation = Surface.ROTATION_180;
- if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
- mLandscapeRotation = Surface.ROTATION_270;
- mSeascapeRotation = Surface.ROTATION_90;
- } else {
- mLandscapeRotation = Surface.ROTATION_90;
- mSeascapeRotation = Surface.ROTATION_270;
- }
- }
-
- // SystemUI (status bar) layout policy
- int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
- int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
-
- // Allow the navigation bar to move on non-square small devices (phones).
- mNavigationBarCanMove = width != height && shortSizeDp < 600;
-
- mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
-
- // Allow a system property to override this. Used by the emulator.
- // See also hasNavigationBar().
- String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
- if ("1".equals(navBarOverride)) {
- mHasNavigationBar = false;
- } else if ("0".equals(navBarOverride)) {
- mHasNavigationBar = true;
- }
-
- // For demo purposes, allow the rotation of the HDMI display to be controlled.
- // By default, HDMI locks rotation to landscape.
- if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
- mDemoHdmiRotation = mPortraitRotation;
- } else {
- mDemoHdmiRotation = mLandscapeRotation;
- }
- mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
-
- // For demo purposes, allow the rotation of the remote display to be controlled.
- // By default, remote display locks rotation to landscape.
- if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
- mDemoRotation = mPortraitRotation;
- } else {
- mDemoRotation = mLandscapeRotation;
- }
- mDemoRotationLock = SystemProperties.getBoolean(
- "persist.demo.rotationlock", false);
-
- // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
- // http://developer.android.com/guide/practices/screens_support.html#range
- // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
- // so if the orientation is forced, we need to respect that no matter what.
- final boolean isCar = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- // For TV, it's usually 960dp x 540dp, ignore the size limitation.
- // so if the orientation is forced, we need to respect that no matter what.
- final boolean isTv = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LEANBACK);
- mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
- res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
- // For debug purposes the next line turns this feature off with:
- // $ adb shell setprop config.override_forced_orient true
- // $ adb shell wm size reset
- !"true".equals(SystemProperties.get("config.override_forced_orient"));
- }
-
/**
* @return whether the navigation bar can be hidden, e.g. the device has a
* navigation bar and touch exploration is not enabled
*/
private boolean canHideNavigationBar() {
- return mHasNavigationBar;
- }
-
- @Override
- public boolean isDefaultOrientationForced() {
- return mForceDefaultOrientation;
+ return mDefaultDisplayPolicy.hasNavigationBar();
}
public void updateSettings() {
@@ -2465,15 +2176,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
.getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
}
- // Configure rotation suggestions.
- int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
- Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
- UserHandle.USER_CURRENT);
- if (mShowRotationSuggestions != showRotationSuggestions) {
- mShowRotationSuggestions = showRotationSuggestions;
- updateOrientationListenerLp(); // Enable, disable the orientation listener
- }
// Configure wake gesture.
boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -2484,24 +2186,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
updateWakeGestureListenerLp();
}
- // Configure rotation lock.
- int userRotation = Settings.System.getIntForUser(resolver,
- Settings.System.USER_ROTATION, Surface.ROTATION_0,
- UserHandle.USER_CURRENT);
- if (mUserRotation != userRotation) {
- mUserRotation = userRotation;
- updateRotation = true;
- }
- int userRotationMode = Settings.System.getIntForUser(resolver,
- Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
- WindowManagerPolicy.USER_ROTATION_FREE :
- WindowManagerPolicy.USER_ROTATION_LOCKED;
- if (mUserRotationMode != userRotationMode) {
- mUserRotationMode = userRotationMode;
- updateRotation = true;
- updateOrientationListenerLp();
- }
-
if (mSystemReady) {
int pointerLocation = Settings.System.getIntForUser(resolver,
Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT);
@@ -2542,8 +2226,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private boolean shouldEnableWakeGestureLp() {
- return mWakeGestureEnabledSetting && !mAwake
- && (!mLidControlsSleep || mLidState != LID_CLOSED)
+ return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake()
+ && (!mLidControlsSleep || mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
&& mWakeGestureListener.isSupported();
}
@@ -2583,24 +2267,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private int readRotation(int resID) {
- try {
- int rotation = mContext.getResources().getInteger(resID);
- switch (rotation) {
- case 0:
- return Surface.ROTATION_0;
- case 90:
- return Surface.ROTATION_90;
- case 180:
- return Surface.ROTATION_180;
- case 270:
- return Surface.ROTATION_270;
- }
- } catch (Resources.NotFoundException e) {
- // fall through
- }
- return -1;
- }
/** {@inheritDoc} */
@Override
@@ -2829,7 +2495,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
void readLidState() {
- mLidState = mWindowManagerFuncs.getLidState();
+ mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
}
private void readCameraLensCoverState() {
@@ -2837,11 +2503,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private boolean isHidden(int accessibilityMode) {
+ final int lidState = mDefaultDisplayPolicy.getLidState();
switch (accessibilityMode) {
case 1:
- return mLidState == LID_CLOSED;
+ return lidState == LID_CLOSED;
case 2:
- return mLidState == LID_OPEN;
+ return lidState == LID_OPEN;
default:
return false;
}
@@ -2873,53 +2540,62 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void onOverlayChangedLw() {
- onConfigurationChanged();
+ public void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {
+ onConfigurationChanged(displayContentInfo);
}
@Override
- public void onConfigurationChanged() {
+ public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
+ final DisplayRotation displayRotation = displayContentInfo.getDisplayRotation();
// TODO(multi-display): Define policy for secondary displays.
- Context uiContext = getSystemUiContext();
+ if (!displayRotation.isDefaultDisplay) {
+ return;
+ }
+
+ final Context uiContext = getSystemUiContext();
final Resources res = uiContext.getResources();
+ final int portraitRotation = displayRotation.getPortraitRotation();
+ final int upsideDownRotation = displayRotation.getUpsideDownRotation();
+ final int landscapeRotation = displayRotation.getLandscapeRotation();
+ final int seascapeRotation = displayRotation.getSeascapeRotation();
- mStatusBarHeightForRotation[mPortraitRotation] =
- mStatusBarHeightForRotation[mUpsideDownRotation] = res.getDimensionPixelSize(
+ mStatusBarHeightForRotation[portraitRotation] =
+ mStatusBarHeightForRotation[upsideDownRotation] = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height_portrait);
- mStatusBarHeightForRotation[mLandscapeRotation] =
- mStatusBarHeightForRotation[mSeascapeRotation] = res.getDimensionPixelSize(
+ mStatusBarHeightForRotation[landscapeRotation] =
+ mStatusBarHeightForRotation[seascapeRotation] = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height_landscape);
// Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationDefault[mPortraitRotation] =
- mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
+ mNavigationBarHeightForRotationDefault[portraitRotation] =
+ mNavigationBarHeightForRotationDefault[upsideDownRotation] =
res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
- mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
- mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
+ mNavigationBarHeightForRotationDefault[landscapeRotation] =
+ mNavigationBarHeightForRotationDefault[seascapeRotation] = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_height_landscape);
// Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationDefault[mPortraitRotation] =
- mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
- mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
- mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
+ mNavigationBarWidthForRotationDefault[portraitRotation] =
+ mNavigationBarWidthForRotationDefault[upsideDownRotation] =
+ mNavigationBarWidthForRotationDefault[landscapeRotation] =
+ mNavigationBarWidthForRotationDefault[seascapeRotation] =
res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
if (ALTERNATE_CAR_MODE_NAV_SIZE) {
// Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
- mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
+ mNavigationBarHeightForRotationInCarMode[portraitRotation] =
+ mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_height_car_mode);
- mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
- mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
+ mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
+ mNavigationBarHeightForRotationInCarMode[seascapeRotation] = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
// Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
- mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
- mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
- mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
+ mNavigationBarWidthForRotationInCarMode[portraitRotation] =
+ mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
+ mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
+ mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_width_car_mode);
}
@@ -2948,10 +2624,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int displayId, DisplayCutout displayCutout) {
int width = fullWidth;
// TODO(multi-display): Support navigation bar on secondary displays.
- if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
+ if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
// For a basic navigation bar, when we are in landscape mode we place
// the navigation bar to the side.
- if (mNavigationBarCanMove && fullWidth > fullHeight) {
+ if (mDefaultDisplayPolicy.navigationBarCanMove() && fullWidth > fullHeight) {
width -= getNavigationBarWidth(rotation, uiMode);
}
}
@@ -2974,10 +2650,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int displayId, DisplayCutout displayCutout) {
int height = fullHeight;
// TODO(multi-display): Support navigation bar on secondary displays.
- if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
+ if (displayId == DEFAULT_DISPLAY && mDefaultDisplayPolicy.hasNavigationBar()) {
// For a basic navigation bar, when we are in portrait mode we place
// the navigation bar to the bottom.
- if (!mNavigationBarCanMove || fullWidth < fullHeight) {
+ if (!mDefaultDisplayPolicy.navigationBarCanMove() || fullWidth < fullHeight) {
height -= getNavigationBarHeight(rotation, uiMode);
}
}
@@ -3065,8 +2741,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// In that case, we want to continue hiding the IME until the windows have completed
// drawing. This way, we know that the IME can be safely shown since the other windows are
// now shown.
- final boolean hideIme =
- win.isInputMethodWindow() && (mAodShowing || !mWindowManagerDrawComplete);
+ final boolean hideIme = win.isInputMethodWindow()
+ && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
|| hideDockDivider || hideIme;
}
@@ -3444,7 +3120,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void selectRotationAnimationLw(int anim[]) {
// If the screen is off or non-interactive, force a jumpcut.
- final boolean forceJumpcut = !mScreenOnFully || !okToAnimate();
+ final boolean forceJumpcut = !mDefaultDisplayPolicy.isScreenOnFully() || !okToAnimate();
if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
+ mTopFullscreenOpaqueWindowState + " rotationAnimation="
+ (mTopFullscreenOpaqueWindowState == null ?
@@ -3851,7 +3527,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If the device is in VR mode and keys are "internal" (e.g. on the side of the
// device), then drop the volume keys and don't forward it to the application/dispatch
// the audio event.
- if (mPersistentVrModeEnabled) {
+ if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
final InputDevice d = event.getDevice();
if (d != null && !d.isExternal()) {
return -1;
@@ -4941,7 +4617,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@NavigationBarPosition
private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
- if (mNavigationBarCanMove && displayWidth > displayHeight) {
+ if (mDefaultDisplayPolicy.navigationBarCanMove() && displayWidth > displayHeight) {
if (displayRotation == Surface.ROTATION_270) {
return NAV_BAR_LEFT;
} else {
@@ -5075,7 +4751,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
- final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
+ final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar()
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
@@ -5884,11 +5560,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
// lid changed state
final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED;
- if (newLidState == mLidState) {
+ if (newLidState == mDefaultDisplayPolicy.getLidState()) {
return;
}
- mLidState = newLidState;
+ mDefaultDisplayPolicy.setLidState(newLidState);
applyLidSwitchState();
updateRotation(true);
@@ -5923,17 +5599,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mCameraLensCoverState = lensCoverState;
}
- void setHdmiPlugged(boolean plugged) {
- if (mHdmiPlugged != plugged) {
- mHdmiPlugged = plugged;
- updateRotation(true, true);
- Intent intent = new Intent(ACTION_HDMI_PLUGGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
- }
-
void initializeHdmiState() {
final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
@@ -5973,8 +5638,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
// This dance forces the code in setHdmiPlugged to run.
// Always do this so the sticky intent is stuck (to false) if there is no hdmi.
- mHdmiPlugged = !plugged;
- setHdmiPlugged(!mHdmiPlugged);
+ mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
}
@@ -6414,16 +6078,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/**
- * Notify the StatusBar that system rotation suggestion has changed.
- */
- private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
- StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
- if (statusBar != null) {
- statusBar.onProposedRotationChanged(rotation, isValid);
- }
- }
-
- /**
* Returns true if the key can have global actions attached to it.
* We reserve all power management keys for the system since they require
* very careful handling.
@@ -6452,7 +6106,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
- return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ return mDefaultDisplayPolicy.getDockMode() != Intent.EXTRA_DOCK_STATE_UNDOCKED;
// ignore media and camera keys
case KeyEvent.KEYCODE_MUTE:
@@ -6500,7 +6154,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) {
- final boolean displayOff = (mDisplay == null || mDisplay.getState() == STATE_OFF);
+ final boolean displayOff = (mDefaultDisplay == null
+ || mDefaultDisplay.getState() == STATE_OFF);
if (displayOff && !mHasFeatureWatch) {
return false;
@@ -6650,8 +6305,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
- mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
- Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED));
} else {
try {
IUiModeManager uiModeService = IUiModeManager.Stub.asInterface(
@@ -6661,9 +6316,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
updateRotation(true);
- synchronized (mLock) {
- updateOrientationListenerLp();
- }
+ mDefaultDisplayRotation.updateOrientationListener();
}
};
@@ -6691,6 +6344,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// and then updates our own bookkeeping based on the now-
// current user.
mSettingsObserver.onChange(false);
+ mDefaultDisplayRotation.onUserSwitch();
// force a re-application of focused window sysui visibility.
// the window may never have been shown for this user
@@ -6764,15 +6418,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mGoingToSleep = false;
mRequestedOrGoingToSleep = false;
+ mDefaultDisplayPolicy.setAwake(false);
// We must get this work done here because the power manager will drop
// the wake lock and let the system suspend once this function returns.
synchronized (mLock) {
- mAwake = false;
updateWakeGestureListenerLp();
- updateOrientationListenerLp();
updateLockScreenTimeout();
}
+ mDefaultDisplayRotation.updateOrientationListener();
+
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedGoingToSleep(why,
mCameraGestureTriggeredDuringGoingToSleep);
@@ -6786,17 +6441,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
EventLog.writeEvent(70000, 1);
if (DEBUG_WAKEUP) Slog.i(TAG, "Started waking up...");
+ mDefaultDisplayPolicy.setAwake(true);
+
// Since goToSleep performs these functions synchronously, we must
// do the same here. We cannot post this work to a handler because
// that might cause it to become reordered with respect to what
// may happen in a future call to goToSleep.
synchronized (mLock) {
- mAwake = true;
-
updateWakeGestureListenerLp();
- updateOrientationListenerLp();
updateLockScreenTimeout();
}
+ mDefaultDisplayRotation.updateOrientationListener();
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedWakingUp();
@@ -6833,16 +6488,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void finishKeyguardDrawn() {
- synchronized (mLock) {
- if (!mScreenOnEarly || mKeyguardDrawComplete) {
- return; // We are not awake yet or we have already informed of this event.
- }
+ if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) {
+ return;
+ }
- mKeyguardDrawComplete = true;
+ synchronized (mLock) {
if (mKeyguardDelegate != null) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
}
- mWindowManagerDrawComplete = false;
}
// ... eventually calls finishWindowsDrawn which will finalize our screen turn on
@@ -6857,18 +6510,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
updateScreenOffSleepToken(true);
+ mDefaultDisplayPolicy.screenTurnedOff();
synchronized (mLock) {
- mScreenOnEarly = false;
- mScreenOnFully = false;
- mKeyguardDrawComplete = false;
- mWindowManagerDrawComplete = false;
- mScreenOnListener = null;
- updateOrientationListenerLp();
-
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOff();
}
}
+ mDefaultDisplayRotation.updateOrientationListener();
reportScreenStateToVrManager(false);
}
@@ -6885,13 +6533,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
updateScreenOffSleepToken(false);
- synchronized (mLock) {
- mScreenOnEarly = true;
- mScreenOnFully = false;
- mKeyguardDrawComplete = false;
- mWindowManagerDrawComplete = false;
- mScreenOnListener = screenOnListener;
+ mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
+ synchronized (mLock) {
if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
@@ -6934,46 +6578,29 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void finishWindowsDrawn() {
- synchronized (mLock) {
- if (!mScreenOnEarly || mWindowManagerDrawComplete) {
- return; // Screen is not turned on or we did already handle this case earlier.
- }
-
- mWindowManagerDrawComplete = true;
+ if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
+ return;
}
finishScreenTurningOn();
}
private void finishScreenTurningOn() {
- synchronized (mLock) {
- // We have just finished drawing screen content. Since the orientation listener
- // gets only installed when all windows are drawn, we try to install it again.
- updateOrientationListenerLp();
+ // We have just finished drawing screen content. Since the orientation listener
+ // gets only installed when all windows are drawn, we try to install it again.
+ mDefaultDisplayRotation.updateOrientationListener();
+
+ final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
+ if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
+ return; // Spurious or not ready yet.
}
- final ScreenOnListener listener;
+
final boolean enableScreen;
+ final boolean awake = mDefaultDisplayPolicy.isAwake();
synchronized (mLock) {
- if (DEBUG_WAKEUP) Slog.d(TAG,
- "finishScreenTurningOn: mAwake=" + mAwake
- + ", mScreenOnEarly=" + mScreenOnEarly
- + ", mScreenOnFully=" + mScreenOnFully
- + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
- + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
-
- if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
- || (mAwake && !mKeyguardDrawComplete)) {
- return; // spurious or not ready yet
- }
-
- if (DEBUG_WAKEUP) Slog.i(TAG, "Finished screen turning on...");
- listener = mScreenOnListener;
- mScreenOnListener = null;
- mScreenOnFully = true;
-
// Remember the first time we draw the keyguard so we know when we're done with
// the main part of booting and can enable the screen and hide boot messages.
- if (!mKeyguardDrawnOnce && mAwake) {
+ if (!mKeyguardDrawnOnce && awake) {
mKeyguardDrawnOnce = true;
enableScreen = true;
if (mBootMessageNeedsHiding) {
@@ -7014,14 +6641,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public boolean isScreenOn() {
- synchronized (mLock) {
- return mScreenOnEarly;
- }
+ return mDefaultDisplayPolicy.isScreenOnEarly();
}
@Override
public boolean okToAnimate() {
- return mAwake && !mGoingToSleep;
+ return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
}
/** {@inheritDoc} */
@@ -7131,7 +6756,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
outInsets.setEmpty();
// Only navigation bar
- if (mHasNavigationBar) {
+ if (mDefaultDisplayPolicy.hasNavigationBar()) {
int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
if (position == NAV_BAR_BOTTOM) {
outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
@@ -7165,7 +6790,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public boolean isDockSideAllowed(int dockSide, int originalDockSide, int displayWidth,
int displayHeight, int displayRotation) {
final int barPosition = navigationBarPosition(displayWidth, displayHeight, displayRotation);
- return isDockSideAllowed(dockSide, originalDockSide, barPosition, mNavigationBarCanMove);
+ return isDockSideAllowed(dockSide, originalDockSide, barPosition,
+ mDefaultDisplayPolicy.navigationBarCanMove());
}
@VisibleForTesting
@@ -7202,293 +6828,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
- if (false) {
- Slog.v(TAG, "rotationForOrientationLw(orient="
- + orientation + ", last=" + lastRotation
- + "); user=" + mUserRotation + " "
- + ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
- ? "USER_ROTATION_LOCKED" : "")
- );
- }
-
- if (mForceDefaultOrientation) {
- return Surface.ROTATION_0;
- }
-
- synchronized (mLock) {
- int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
- if (sensorRotation < 0) {
- sensorRotation = lastRotation;
- }
-
- final int preferredRotation;
- if (!defaultDisplay) {
- // For secondary displays we ignore things like displays sensors, docking mode and
- // rotation lock, and always prefer a default rotation.
- preferredRotation = Surface.ROTATION_0;
- } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
- // Ignore sensor when lid switch is open and rotation is forced.
- preferredRotation = mLidOpenRotation;
- } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
- && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
- // Ignore sensor when in car dock unless explicitly enabled.
- // This case can override the behavior of NOSENSOR, and can also
- // enable 180 degree rotation while docked.
- preferredRotation = mCarDockEnablesAccelerometer
- ? sensorRotation : mCarDockRotation;
- } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
- && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
- // Ignore sensor when in desk dock unless explicitly enabled.
- // This case can override the behavior of NOSENSOR, and can also
- // enable 180 degree rotation while docked.
- preferredRotation = mDeskDockEnablesAccelerometer
- ? sensorRotation : mDeskDockRotation;
- } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
- // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
- // Note that the dock orientation overrides the HDMI orientation.
- preferredRotation = mDemoHdmiRotation;
- } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
- && mUndockedHdmiRotation >= 0) {
- // Ignore sensor when plugged into HDMI and an undocked orientation has
- // been specified in the configuration (only for legacy devices without
- // full multi-display support).
- // Note that the dock orientation overrides the HDMI orientation.
- preferredRotation = mUndockedHdmiRotation;
- } else if (mDemoRotationLock) {
- // Ignore sensor when demo rotation lock is enabled.
- // Note that the dock orientation and HDMI rotation lock override this.
- preferredRotation = mDemoRotation;
- } else if (mPersistentVrModeEnabled) {
- // While in VR, apps always prefer a portrait rotation. This does not change
- // any apps that explicitly set landscape, but does cause sensors be ignored,
- // and ignored any orientation lock that the user has set (this conditional
- // should remain above the ORIENTATION_LOCKED conditional below).
- preferredRotation = mPortraitRotation;
- } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
- // Application just wants to remain locked in the last rotation.
- preferredRotation = lastRotation;
- } else if (!mSupportAutoRotation) {
- // If we don't support auto-rotation then bail out here and ignore
- // the sensor and any rotation lock settings.
- preferredRotation = -1;
- } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
- && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
- || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
- || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
- || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
- || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
- // Otherwise, use sensor only if requested by the application or enabled
- // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
- if (mAllowAllRotations < 0) {
- // Can't read this during init() because the context doesn't
- // have display metrics at that time so we cannot determine
- // tablet vs. phone then.
- mAllowAllRotations = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
- }
- if (sensorRotation != Surface.ROTATION_180
- || mAllowAllRotations == 1
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
- preferredRotation = sensorRotation;
- } else {
- preferredRotation = lastRotation;
- }
- } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
- && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
- // Apply rotation lock. Does not apply to NOSENSOR.
- // The idea is that the user rotation expresses a weak preference for the direction
- // of gravity and as NOSENSOR is never affected by gravity, then neither should
- // NOSENSOR be affected by rotation lock (although it will be affected by docks).
- preferredRotation = mUserRotation;
- } else {
- // No overriding preference.
- // We will do exactly what the application asked us to do.
- preferredRotation = -1;
- }
-
- switch (orientation) {
- case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
- // Return portrait unless overridden.
- if (isAnyPortrait(preferredRotation)) {
- return preferredRotation;
- }
- return mPortraitRotation;
-
- case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
- // Return landscape unless overridden.
- if (isLandscapeOrSeascape(preferredRotation)) {
- return preferredRotation;
- }
- return mLandscapeRotation;
-
- case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
- // Return reverse portrait unless overridden.
- if (isAnyPortrait(preferredRotation)) {
- return preferredRotation;
- }
- return mUpsideDownRotation;
-
- case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
- // Return seascape unless overridden.
- if (isLandscapeOrSeascape(preferredRotation)) {
- return preferredRotation;
- }
- return mSeascapeRotation;
-
- case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
- case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
- // Return either landscape rotation.
- if (isLandscapeOrSeascape(preferredRotation)) {
- return preferredRotation;
- }
- if (isLandscapeOrSeascape(lastRotation)) {
- return lastRotation;
- }
- return mLandscapeRotation;
-
- case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
- case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
- // Return either portrait rotation.
- if (isAnyPortrait(preferredRotation)) {
- return preferredRotation;
- }
- if (isAnyPortrait(lastRotation)) {
- return lastRotation;
- }
- return mPortraitRotation;
-
- default:
- // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
- // just return the preferred orientation we already calculated.
- if (preferredRotation >= 0) {
- return preferredRotation;
- }
- return Surface.ROTATION_0;
- }
- }
- }
-
- @Override
- public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation) {
- switch (orientation) {
- case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
- case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
- case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
- return isAnyPortrait(rotation);
-
- case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
- case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
- case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
- return isLandscapeOrSeascape(rotation);
-
- default:
- return true;
- }
- }
-
- @Override
- public void setRotationLw(int rotation) {
- mOrientationListener.setCurrentRotation(rotation);
- }
-
- public boolean isRotationChoicePossible(int orientation) {
- // Rotation choice is only shown when the user is in locked mode.
- if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
-
- // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
- // demo, hdmi, vr, etc mode
-
- // Determine if the rotation is currently forced
- if (mForceDefaultOrientation) {
- return false; // Rotation is forced to default orientation
-
- } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
- return false; // Rotation is forced mLidOpenRotation
-
- } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && !mCarDockEnablesAccelerometer) {
- return false; // Rotation forced to mCarDockRotation
-
- } else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
- && !mDeskDockEnablesAccelerometer) {
- return false; // Rotation forced to mDeskDockRotation
-
- } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
- return false; // Rotation forced to mDemoHdmiRotation
-
- } else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
- && mUndockedHdmiRotation >= 0) {
- return false; // Rotation forced to mUndockedHdmiRotation
-
- } else if (mDemoRotationLock) {
- return false; // Rotation forced to mDemoRotation
-
- } else if (mPersistentVrModeEnabled) {
- return false; // Rotation forced to mPortraitRotation
-
- } else if (!mSupportAutoRotation) {
- return false;
- }
-
- // Ensure that some rotation choice is possible for the given orientation
- switch (orientation) {
- case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
- case ActivityInfo.SCREEN_ORIENTATION_USER:
- case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
- case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
- case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
- // NOSENSOR description is ambiguous, in reality WM ignores user choice
- return true;
- }
-
- // Rotation is forced, should be controlled by system
- return false;
- }
-
- public boolean isValidRotationChoice(int orientation, final int preferredRotation) {
- // Determine if the given app orientation is compatible with the provided rotation choice
- switch (orientation) {
- case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
- // Works with any of the 4 rotations
- return preferredRotation >= 0;
-
- case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
- // It's possible for the user pref to be set at 180 because of FULL_USER. This would
- // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
- // but never to go to 180.
- return preferredRotation == mPortraitRotation;
-
- case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
- // Works landscape or seascape
- return isLandscapeOrSeascape(preferredRotation);
-
- case ActivityInfo.SCREEN_ORIENTATION_USER:
- case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
- // Works with any rotation except upside down
- return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
- }
-
- return false;
- }
-
- private boolean isLandscapeOrSeascape(int rotation) {
- return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
- }
-
- private boolean isAnyPortrait(int rotation) {
- return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
- }
-
- @Override
public int getUserRotationMode() {
return Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
@@ -7560,8 +6899,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
readCameraLensCoverState();
updateUiMode();
+ mDefaultDisplayRotation.updateOrientationListener();
synchronized (mLock) {
- updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
@Override
@@ -7599,9 +6938,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public boolean canDismissBootAnimation() {
- synchronized (mLock) {
- return mKeyguardDrawComplete;
- }
+ return mDefaultDisplayPolicy.isKeyguardDrawComplete();
}
ProgressDialog mBootMsgDialog = null;
@@ -7701,7 +7038,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- if (mAwake && mNotifyUserActivity) {
+ if (mDefaultDisplayPolicy.isAwake() && mNotifyUserActivity) {
mHandler.sendEmptyMessageDelayed(MSG_NOTIFY_USER_ACTIVITY,
USER_ACTIVITY_NOTIFICATION_DELAY);
mNotifyUserActivity = false;
@@ -7744,7 +7081,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void updateLockScreenTimeout() {
synchronized (mScreenLockTimeout) {
- boolean enable = (mAllowLockscreenWhenOn && mAwake &&
+ final boolean enable = (mAllowLockscreenWhenOn && mDefaultDisplayPolicy.isAwake() &&
mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId));
if (mLockScreenTimerActive != enable) {
if (enable) {
@@ -7799,10 +7136,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void applyLidSwitchState() {
- if (mLidState == LID_CLOSED && mLidControlsSleep) {
+ final int lidState = mDefaultDisplayPolicy.getLidState();
+ if (lidState == LID_CLOSED && mLidControlsSleep) {
goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
- } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) {
+ } else if (lidState == LID_CLOSED && mLidControlsScreenLock) {
mWindowManagerFuncs.lockDeviceNow();
}
@@ -7824,17 +7162,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
void updateRotation(boolean alwaysSendConfiguration) {
try {
- //set orientation on WindowManager
- mWindowManager.updateRotation(alwaysSendConfiguration, false);
- } catch (RemoteException e) {
- // Ignore
- }
- }
-
- void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
- try {
- //set orientation on WindowManager
- mWindowManager.updateRotation(alwaysSendConfiguration, forceRelayout);
+ // Set orientation on WindowManager.
+ mWindowManager.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
} catch (RemoteException e) {
// Ignore
}
@@ -7867,12 +7196,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
intent = mDeskDockIntent;
}
- } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH
- && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
- || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK)) {
- // Always launch dock home from home when watch is docked, if it exists.
- intent = mDeskDockIntent;
+ } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH) {
+ final int dockMode = mDefaultDisplayPolicy.getDockMode();
+ if (dockMode == Intent.EXTRA_DOCK_STATE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK) {
+ // Always launch dock home from home when watch is docked, if it exists.
+ intent = mDeskDockIntent;
+ }
} else if (mUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET) {
if (ENABLE_VR_HEADSET_HOME_CAPTURE) {
intent = mVrHeadsetHomeIntent;
@@ -7987,16 +7318,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
- @Override
- public void setCurrentOrientationLw(int newOrientation) {
- synchronized (mLock) {
- if (newOrientation != mCurrentAppOrientation) {
- mCurrentAppOrientation = newOrientation;
- updateOrientationListenerLp();
- }
- }
- }
-
private boolean isTheaterModeEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0) == 1;
@@ -8323,7 +7644,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
&& now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
- if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
+ if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard()
+ && mDefaultDisplayPolicy.isKeyguardDrawComplete()) {
// The user performed the panic gesture recently, we're about to hide the bars,
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
@@ -8466,7 +7788,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// overridden by qemu.hw.mainkeys in the emulator.
@Override
public boolean hasNavigationBar() {
- return mHasNavigationBar;
+ return mDefaultDisplayPolicy.hasNavigationBar();
}
@Override
@@ -8511,19 +7833,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) {
+ public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
+ int newRotation) {
// For the upside down rotation we don't rotate seamlessly as the navigation
// bar moves position.
// Note most apps (using orientation:sensor or user as opposed to fullSensor)
// will not enter the reverse portrait orientation, so actually the
// orientation won't change at all.
- if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
+ if (oldRotation == displayRotation.getUpsideDownRotation()
+ || newRotation == displayRotation.getUpsideDownRotation()) {
return false;
}
// If the navigation bar can't change sides, then it will
// jump when we change orientations and we don't rotate
// seamlessly.
- if (!mNavigationBarCanMove) {
+ if (!displayRotation.getDisplayPolicy().navigationBarCanMove()) {
return false;
}
int delta = newRotation - oldRotation;
@@ -8557,12 +7881,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(LAST_SYSTEM_UI_FLAGS, mLastSystemUiFlags);
- proto.write(ROTATION_MODE, mUserRotationMode);
- proto.write(ROTATION, mUserRotation);
- proto.write(ORIENTATION, mCurrentAppOrientation);
- proto.write(SCREEN_ON_FULLY, mScreenOnFully);
- proto.write(KEYGUARD_DRAW_COMPLETE, mKeyguardDrawComplete);
- proto.write(WINDOW_MANAGER_DRAW_COMPLETE, mWindowManagerDrawComplete);
+ proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
+ proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
+ proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
+ proto.write(SCREEN_ON_FULLY, mDefaultDisplayPolicy.isScreenOnFully());
+ proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
+ proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
+ mDefaultDisplayPolicy.isWindowManagerDrawComplete());
if (mFocusedApp != null) {
proto.write(FOCUSED_APP_TOKEN, mFocusedApp.toString());
}
@@ -8584,8 +7909,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
proto.write(FORCE_STATUS_BAR_FROM_KEYGUARD, mForceStatusBarFromKeyguard);
mStatusBarController.writeToProto(proto, STATUS_BAR);
mNavigationBarController.writeToProto(proto, NAVIGATION_BAR);
- if (mOrientationListener != null) {
- mOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
+ if (mDefaultOrientationListener != null) {
+ mDefaultOrientationListener.writeToProto(proto, ORIENTATION_LISTENER);
}
if (mKeyguardDelegate != null) {
mKeyguardDelegate.writeToProto(proto, KEYGUARD_DELEGATE);
@@ -8598,13 +7923,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
pw.print(" mSystemReady="); pw.print(mSystemReady);
pw.print(" mSystemBooted="); pw.println(mSystemBooted);
- pw.print(prefix); pw.print("mLidState=");
- pw.print(WindowManagerFuncs.lidStateToString(mLidState));
- pw.print(" mLidOpenRotation=");
- pw.println(Surface.rotationToString(mLidOpenRotation));
pw.print(prefix); pw.print("mCameraLensCoverState=");
- pw.print(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
- pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged);
+ pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
|| mForceClearedSystemUiFlags != 0) {
pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
@@ -8622,27 +7942,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.println(mWakeGestureEnabledSetting);
pw.print(prefix);
- pw.print("mSupportAutoRotation="); pw.print(mSupportAutoRotation);
- pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled);
- pw.print(prefix); pw.print("mUiMode="); pw.print(Configuration.uiModeToString(mUiMode));
- pw.print(" mDockMode="); pw.println(Intent.dockStateToString(mDockMode));
- pw.print(prefix); pw.print("mEnableCarDockHomeCapture=");
- pw.print(mEnableCarDockHomeCapture);
- pw.print(" mCarDockRotation=");
- pw.print(Surface.rotationToString(mCarDockRotation));
- pw.print(" mDeskDockRotation=");
- pw.println(Surface.rotationToString(mDeskDockRotation));
- pw.print(prefix); pw.print("mUserRotationMode=");
- pw.print(WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
- pw.print(" mUserRotation="); pw.print(Surface.rotationToString(mUserRotation));
- pw.print(" mAllowAllRotations=");
- pw.println(allowAllRotationsToString(mAllowAllRotations));
- pw.print(prefix); pw.print("mCurrentAppOrientation=");
- pw.println(ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
- pw.print(prefix); pw.print("mCarDockEnablesAccelerometer=");
- pw.print(mCarDockEnablesAccelerometer);
- pw.print(" mDeskDockEnablesAccelerometer=");
- pw.println(mDeskDockEnablesAccelerometer);
+ pw.print("mUiMode=");
+ pw.print(Configuration.uiModeToString(mUiMode));
+ pw.print("mEnableCarDockHomeCapture="); pw.println(mEnableCarDockHomeCapture);
pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
pw.print(mLidKeyboardAccessibility);
pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
@@ -8692,12 +7994,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(" mEndcallBehavior=");
pw.println(endcallBehaviorToString(mEndcallBehavior));
pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed);
- pw.print(prefix);
- pw.print("mAwake="); pw.print(mAwake);
- pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly);
- pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
- pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
- pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
@@ -8752,19 +8048,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
- pw.print(prefix); pw.print("mLandscapeRotation=");
- pw.print(Surface.rotationToString(mLandscapeRotation));
- pw.print(" mSeascapeRotation=");
- pw.println(Surface.rotationToString(mSeascapeRotation));
- pw.print(prefix); pw.print("mPortraitRotation=");
- pw.print(Surface.rotationToString(mPortraitRotation));
- pw.print(" mUpsideDownRotation=");
- pw.println(Surface.rotationToString(mUpsideDownRotation));
- pw.print(prefix); pw.print("mDemoHdmiRotation=");
- pw.print(Surface.rotationToString(mDemoHdmiRotation));
- pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock);
- pw.print(prefix); pw.print("mUndockedHdmiRotation=");
- pw.println(Surface.rotationToString(mUndockedHdmiRotation));
if (mHasFeatureLeanback) {
pw.print(prefix);
pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed);
@@ -8782,8 +8065,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mWakeGestureListener != null) {
mWakeGestureListener.dump(pw, prefix);
}
- if (mOrientationListener != null) {
- mOrientationListener.dump(pw, prefix);
+ if (mDefaultOrientationListener != null) {
+ mDefaultOrientationListener.dump(pw, prefix);
}
if (mBurnInProtectionHelper != null) {
mBurnInProtectionHelper.dump(prefix, pw);
@@ -8796,19 +8079,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
}
- private static String allowAllRotationsToString(int allowAll) {
- switch (allowAll) {
- case -1:
- return "unknown";
- case 0:
- return "false";
- case 1:
- return "true";
- default:
- return Integer.toString(allowAll);
- }
- }
-
private static String endcallBehaviorToString(int behavior) {
StringBuilder sb = new StringBuilder();
if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 00603285336d..cc3921776f56 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -93,6 +93,7 @@ import android.view.animation.Animation;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowFrames;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -177,7 +178,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Called when the resource overlays change.
*/
- default void onOverlayChangedLw() {}
+ default void onOverlayChangedLw(DisplayContentInfo displayContentInfo) {}
/**
* Interface to the Window Manager state associated with a particular
@@ -638,6 +639,27 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* The keyguard showing state has changed
*/
void onKeyguardShowingAndNotOccludedChanged();
+
+ DisplayContentInfo getDefaultDisplayContentInfo();
+ }
+
+ /**
+ * Provides the rotation of a device.
+ *
+ * @see com.android.server.policy.WindowOrientationListener
+ */
+ public interface RotationSource {
+ int getProposedRotation();
+
+ void setCurrentRotation(int rotation);
+ }
+
+ /**
+ * Interface to get public information of a display content.
+ */
+ public interface DisplayContentInfo {
+ DisplayRotation getDisplayRotation();
+ Display getDisplay();
}
/** Window has been added to the screen. */
@@ -669,6 +691,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
public final int USER_ROTATION_LOCKED = 1;
/**
+ * Set the default display content to provide basic functions for the policy.
+ */
+ public void setDefaultDisplay(DisplayContentInfo displayContentInfo);
+
+ /**
* Perform initialization of the policy.
*
* @param context The system context we are running in.
@@ -677,17 +704,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
WindowManagerFuncs windowManagerFuncs);
/**
- * @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true.
- */
- public boolean isDefaultOrientationForced();
-
- /**
- * Called by window manager once it has the initial, default native
- * display dimensions.
- */
- public void setInitialDisplaySize(Display display, int width, int height, int density);
-
- /**
* Check permissions when adding a window.
*
* @param attrs The window's LayoutParams.
@@ -1410,44 +1426,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
public boolean isShowingDreamLw();
/**
- * Given an orientation constant, returns the appropriate surface rotation,
- * taking into account sensors, docking mode, rotation lock, and other factors.
- *
- * @param orientation An orientation constant, such as
- * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
- * @param lastRotation The most recently used rotation.
- * @param defaultDisplay Flag indicating whether the rotation is computed for the default
- * display. Currently for all non-default displays sensors, docking mode,
- * rotation lock and other factors are ignored.
- * @return The surface rotation to use.
- */
- public int rotationForOrientationLw(@ActivityInfo.ScreenOrientation int orientation,
- int lastRotation, boolean defaultDisplay);
-
- /**
- * Given an orientation constant and a rotation, returns true if the rotation
- * has compatible metrics to the requested orientation. For example, if
- * the application requested landscape and got seascape, then the rotation
- * has compatible metrics; if the application requested portrait and got landscape,
- * then the rotation has incompatible metrics; if the application did not specify
- * a preference, then anything goes.
- *
- * @param orientation An orientation constant, such as
- * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
- * @param rotation The rotation to check.
- * @return True if the rotation is compatible with the requested orientation.
- */
- public boolean rotationHasCompatibleMetricsLw(@ActivityInfo.ScreenOrientation int orientation,
- int rotation);
-
- /**
- * Called by the window manager when the rotation changes.
- *
- * @param rotation The new rotation.
- */
- public void setRotationLw(int rotation);
-
- /**
* Called when the system is mostly done booting to set whether
* the system should go into safe mode.
*/
@@ -1487,8 +1465,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
*/
public void enableScreenAfterBoot();
- public void setCurrentOrientationLw(@ActivityInfo.ScreenOrientation int newOrientation);
-
/**
* Call from application to perform haptic feedback on its window.
*/
@@ -1702,9 +1678,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Called when the configuration has changed, and it's safe to load new values from resources.
*/
- public void onConfigurationChanged();
+ public void onConfigurationChanged(DisplayContentInfo displayContentInfo);
- public boolean shouldRotateSeamlessly(int oldRotation, int newRotation);
+ public boolean shouldRotateSeamlessly(DisplayRotation displayRotation,
+ int oldRotation, int newRotation);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 1508c9ecb394..d5adb5e1c111 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -204,6 +204,10 @@ public abstract class WindowOrientationListener {
}
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
/**
* Sets the current rotation.
*
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d6ccf3bbe540..556038f3f646 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -328,6 +328,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
PackageManager pm = context.getPackageManager();
String app = intent.getData().getSchemeSpecificPart();
sStatsd.informOnePackageRemoved(app, uid);
+ StatsLog.write(StatsLog.GENERIC_ATOM, uid, 1000);
}
} else {
PackageManager pm = context.getPackageManager();
@@ -336,6 +337,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
String app = intent.getData().getSchemeSpecificPart();
PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
+ StatsLog.write(StatsLog.GENERIC_ATOM, uid, 1001);
}
} catch (Exception e) {
Slog.w(TAG, "Failed to inform statsd of an app update", e);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 738b0ca4a74f..519881ef8c92 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -547,50 +547,50 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
- public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+ public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
if (mBar != null) {
try {
- mBar.showFingerprintDialog(bundle, receiver);
+ mBar.showBiometricDialog(bundle, receiver);
} catch (RemoteException ex) {
}
}
}
@Override
- public void onFingerprintAuthenticated() {
+ public void onBiometricAuthenticated() {
if (mBar != null) {
try {
- mBar.onFingerprintAuthenticated();
+ mBar.onBiometricAuthenticated();
} catch (RemoteException ex) {
}
}
}
@Override
- public void onFingerprintHelp(String message) {
+ public void onBiometricHelp(String message) {
if (mBar != null) {
try {
- mBar.onFingerprintHelp(message);
+ mBar.onBiometricHelp(message);
} catch (RemoteException ex) {
}
}
}
@Override
- public void onFingerprintError(String error) {
+ public void onBiometricError(String error) {
if (mBar != null) {
try {
- mBar.onFingerprintError(error);
+ mBar.onBiometricError(error);
} catch (RemoteException ex) {
}
}
}
@Override
- public void hideFingerprintDialog() {
+ public void hideBiometricDialog() {
if (mBar != null) {
try {
- mBar.hideFingerprintDialog();
+ mBar.hideBiometricDialog();
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 218fcb7a998c..d7d5cf84ede0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -173,7 +173,8 @@ import java.util.function.Predicate;
* IMPORTANT: No method from this class should ever be used without holding
* WindowManagerService.mWindowMap.
*/
-class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
+class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
+ implements WindowManagerPolicy.DisplayContentInfo {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
/** Unique identifier of this stack. */
@@ -234,6 +235,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private final DisplayPolicy mDisplayPolicy;
+ private DisplayRotation mDisplayRotation;
DisplayFrames mDisplayFrames;
/**
@@ -304,8 +307,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private boolean mLayoutNeeded;
int pendingLayoutChanges;
int mDeferredRotationPauseCount;
+
// TODO(multi-display): remove some of the usages.
+ @VisibleForTesting
boolean isDefaultDisplay;
+
/**
* Flag indicating whether WindowManager should override info for this display in
* DisplayManager.
@@ -762,6 +768,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
initializeDisplayBaseInfo();
+ mDisplayPolicy = new DisplayPolicy(service);
+ mDisplayRotation = new DisplayRotation(service, this);
+ if (isDefaultDisplay) {
+ // The policy may be invoked right after here, so it requires the necessary default
+ // fields of this display content.
+ mService.mPolicy.setDefaultDisplay(this);
+ }
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
@@ -801,7 +814,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// {@link DisplayContent} ready for use.
mDisplayReady = true;
- mInputMonitor = new InputMonitor(service, mDisplayId);
+ // TODO(b/112081256): Use independent InputMonitor.
+ mInputMonitor = isDefaultDisplay ? new InputMonitor(service, mDisplayId)
+ : mService.getDefaultDisplayContentLocked().mInputMonitor;
}
boolean isReady() {
@@ -905,7 +920,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
appToken.onRemovedFromDisplay();
}
- Display getDisplay() {
+ @Override
+ public Display getDisplay() {
return mDisplay;
}
@@ -917,6 +933,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mDisplayMetrics;
}
+ DisplayPolicy getDisplayPolicy() {
+ return mDisplayPolicy;
+ }
+
+ @Override
+ public DisplayRotation getDisplayRotation() {
+ return mDisplayRotation;
+ }
+
+ @VisibleForTesting
+ void setDisplayRotation(DisplayRotation displayRotation) {
+ mDisplayRotation = displayRotation;
+ }
+
int getRotation() {
return mRotation;
}
@@ -924,6 +954,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
@VisibleForTesting
void setRotation(int newRotation) {
mRotation = newRotation;
+ mDisplayRotation.setRotation(newRotation);
}
int getLastOrientation() {
@@ -969,14 +1000,45 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDeferredRotationPauseCount--;
if (mDeferredRotationPauseCount == 0) {
- final boolean changed = updateRotationUnchecked();
- if (changed) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
- }
+ updateRotationAndSendNewConfigIfNeeded();
}
}
/**
+ * If this is true we have updated our desired orientation, but not yet changed the real
+ * orientation our applied our screen rotation animation. For example, because a previous
+ * screen rotation was in progress.
+ *
+ * @return {@code true} if the there is an ongoing rotation change.
+ */
+ boolean rotationNeedsUpdate() {
+ final int lastOrientation = getLastOrientation();
+ final int oldRotation = getRotation();
+ final boolean oldAltOrientation = getAltOrientation();
+
+ final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
+ final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
+ lastOrientation, rotation);
+ if (oldRotation == rotation && oldAltOrientation == altOrientation) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Update rotation of the display and send configuration if the rotation is changed.
+ *
+ * @return {@code true} if the rotation has been changed and the new config is sent.
+ */
+ boolean updateRotationAndSendNewConfigIfNeeded() {
+ final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
+ if (changed) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+ }
+ return changed;
+ }
+
+ /**
* Update rotation of the display.
*
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
@@ -1034,13 +1096,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
- final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
- isDefaultDisplay);
+ final int rotation = mDisplayRotation.rotationForOrientation(lastOrientation, oldRotation);
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
+ mDisplayId + " based on lastOrientation=" + lastOrientation
+ " and oldRotation=" + oldRotation);
- boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
- rotation);
+ boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(mDisplayRotation,
+ oldRotation, rotation);
if (mayRotateSeamlessly) {
final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
@@ -1071,7 +1132,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// an orientation that has different metrics than it expected.
// eg. Portrait instead of Landscape.
- final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
+ final boolean altOrientation = !mDisplayRotation.rotationHasCompatibleMetrics(
lastOrientation, rotation);
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
@@ -1093,11 +1154,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mService.mWaitingForConfig = true;
}
- mRotation = rotation;
+ setRotation(rotation);
mAltOrientation = altOrientation;
- if (isDefaultDisplay) {
- mService.mPolicy.setRotationLw(rotation);
- }
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
@@ -1191,8 +1249,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
void configureDisplayPolicy() {
- mService.mPolicy.setInitialDisplaySize(getDisplay(),
- mBaseDisplayWidth, mBaseDisplayHeight, mBaseDisplayDensity);
+ final int width = mBaseDisplayWidth;
+ final int height = mBaseDisplayHeight;
+ final int shortSize;
+ final int longSize;
+ if (width > height) {
+ shortSize = height;
+ longSize = width;
+ } else {
+ shortSize = width;
+ longSize = height;
+ }
+
+ final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+
+ mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
+ mDisplayPolicy.configure(width, height, shortSizeDp);
mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
@@ -2367,6 +2440,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
mDisplayFrames.dump(prefix, pw);
pw.println();
+ mDisplayPolicy.dump(prefix, pw);
+ pw.println();
+ mDisplayRotation.dump(prefix, pw);
+ pw.println();
mInputMonitor.dump(pw, " ");
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
new file mode 100644
index 000000000000..9151ddf17173
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
+import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
+import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
+
+import java.io.PrintWriter;
+
+/**
+ * The policy that provides the basic behaviors and states of a display to show UI.
+ */
+public class DisplayPolicy {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
+
+ private final WindowManagerService mService;
+ private final Object mLock;
+
+ private final boolean mCarDockEnablesAccelerometer;
+ private final boolean mDeskDockEnablesAccelerometer;
+
+ private volatile int mLidState = LID_ABSENT;
+ private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private volatile boolean mHdmiPlugged;
+
+ private volatile boolean mHasNavigationBar;
+ // Can the navigation bar ever move to the side?
+ private volatile boolean mNavigationBarCanMove;
+
+ // Written by vr manager thread, only read in this class.
+ private volatile boolean mPersistentVrModeEnabled;
+
+ private volatile boolean mAwake;
+ private volatile boolean mScreenOnEarly;
+ private volatile boolean mScreenOnFully;
+ private volatile ScreenOnListener mScreenOnListener;
+
+ private volatile boolean mKeyguardDrawComplete;
+ private volatile boolean mWindowManagerDrawComplete;
+
+ DisplayPolicy(WindowManagerService service) {
+ mService = service;
+ mLock = service.getWindowManagerLock();
+ mCarDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_carDockEnablesAccelerometer);
+ mDeskDockEnablesAccelerometer = service.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
+ }
+
+ void configure(int width, int height, int shortSizeDp) {
+ // Allow the navigation bar to move on non-square small devices (phones).
+ mNavigationBarCanMove = width != height && shortSizeDp < 600;
+
+ mHasNavigationBar = mService.mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_showNavigationBar);
+
+ // Allow a system property to override this. Used by the emulator.
+ // See also hasNavigationBar().
+ String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+ if ("1".equals(navBarOverride)) {
+ mHasNavigationBar = false;
+ } else if ("0".equals(navBarOverride)) {
+ mHasNavigationBar = true;
+ }
+ }
+
+ public void setHdmiPlugged(boolean plugged) {
+ setHdmiPlugged(plugged, false /* force */);
+ }
+
+ public void setHdmiPlugged(boolean plugged, boolean force) {
+ if (force || mHdmiPlugged != plugged) {
+ mHdmiPlugged = plugged;
+ mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */);
+ final Intent intent = new Intent(ACTION_HDMI_PLUGGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged);
+ mService.mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ boolean isHdmiPlugged() {
+ return mHdmiPlugged;
+ }
+
+ boolean isCarDockEnablesAccelerometer() {
+ return mCarDockEnablesAccelerometer;
+ }
+
+ boolean isDeskDockEnablesAccelerometer() {
+ return mDeskDockEnablesAccelerometer;
+ }
+
+ public void setPersistentVrModeEnabled(boolean persistentVrModeEnabled) {
+ mPersistentVrModeEnabled = persistentVrModeEnabled;
+ }
+
+ public boolean isPersistentVrModeEnabled() {
+ return mPersistentVrModeEnabled;
+ }
+
+ public void setDockMode(int dockMode) {
+ mDockMode = dockMode;
+ }
+
+ public int getDockMode() {
+ return mDockMode;
+ }
+
+ public boolean hasNavigationBar() {
+ return mHasNavigationBar;
+ }
+
+ public boolean navigationBarCanMove() {
+ return mNavigationBarCanMove;
+ }
+
+ public void setLidState(int lidState) {
+ mLidState = lidState;
+ }
+
+ public int getLidState() {
+ return mLidState;
+ }
+
+ public void setAwake(boolean awake) {
+ mAwake = awake;
+ }
+
+ public boolean isAwake() {
+ return mAwake;
+ }
+
+ public boolean isScreenOnEarly() {
+ return mScreenOnEarly;
+ }
+
+ public boolean isScreenOnFully() {
+ return mScreenOnFully;
+ }
+
+ public boolean isKeyguardDrawComplete() {
+ return mKeyguardDrawComplete;
+ }
+
+ public boolean isWindowManagerDrawComplete() {
+ return mWindowManagerDrawComplete;
+ }
+
+ public ScreenOnListener getScreenOnListener() {
+ return mScreenOnListener;
+ }
+
+ public void screenTurnedOn(ScreenOnListener screenOnListener) {
+ synchronized (mLock) {
+ mScreenOnEarly = true;
+ mScreenOnFully = false;
+ mKeyguardDrawComplete = false;
+ mWindowManagerDrawComplete = false;
+ mScreenOnListener = screenOnListener;
+ }
+ }
+
+ public void screenTurnedOff() {
+ synchronized (mLock) {
+ mScreenOnEarly = false;
+ mScreenOnFully = false;
+ mKeyguardDrawComplete = false;
+ mWindowManagerDrawComplete = false;
+ mScreenOnListener = null;
+ }
+ }
+
+ /** Return false if we are not awake yet or we have already informed of this event. */
+ public boolean finishKeyguardDrawn() {
+ synchronized (mLock) {
+ if (!mScreenOnEarly || mKeyguardDrawComplete) {
+ return false;
+ }
+
+ mKeyguardDrawComplete = true;
+ mWindowManagerDrawComplete = false;
+ }
+ return true;
+ }
+
+ /** Return false if screen is not turned on or we did already handle this case earlier. */
+ public boolean finishWindowsDrawn() {
+ synchronized (mLock) {
+ if (!mScreenOnEarly || mWindowManagerDrawComplete) {
+ return false;
+ }
+
+ mWindowManagerDrawComplete = true;
+ }
+ return true;
+ }
+
+ /** Return false if it is not ready to turn on. */
+ public boolean finishScreenTurningOn() {
+ synchronized (mLock) {
+ if (DEBUG_SCREEN_ON) Slog.d(TAG,
+ "finishScreenTurningOn: mAwake=" + mAwake
+ + ", mScreenOnEarly=" + mScreenOnEarly
+ + ", mScreenOnFully=" + mScreenOnFully
+ + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
+ + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+
+ if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
+ || (mAwake && !mKeyguardDrawComplete)) {
+ return false;
+ }
+
+ if (DEBUG_SCREEN_ON) Slog.i(TAG, "Finished screen turning on...");
+ mScreenOnListener = null;
+ mScreenOnFully = true;
+ }
+ return true;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DisplayPolicy");
+ pw.print(prefix + " mCarDockEnablesAccelerometer=" + mCarDockEnablesAccelerometer);
+ pw.println(" mDeskDockEnablesAccelerometer=" + mDeskDockEnablesAccelerometer);
+ pw.print(prefix + " mDockMode=" + Intent.dockStateToString(mDockMode));
+ pw.println(" mLidState=" + WindowManagerFuncs.lidStateToString(mLidState));
+ pw.print(prefix + " mAwake=" + mAwake);
+ pw.print(" mScreenOnEarly=" + mScreenOnEarly);
+ pw.println(" mScreenOnFully=" + mScreenOnFully);
+ pw.print(prefix + " mKeyguardDrawComplete=" + mKeyguardDrawComplete);
+ pw.println(" mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+ pw.println(prefix + " mHdmiPlugged=" + mHdmiPlugged);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
new file mode 100644
index 000000000000..685c444d92b5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -0,0 +1,881 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Surface;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowOrientationListener;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import java.io.PrintWriter;
+
+/**
+ * Defines the mapping between orientation and rotation of a display.
+ * Non-public methods are assumed to run inside WM lock.
+ */
+public class DisplayRotation {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
+
+ private final WindowManagerService mService;
+ private final DisplayPolicy mDisplayPolicy;
+ private final Context mContext;
+ private final Object mLock;
+
+ public final boolean isDefaultDisplay;
+ private final boolean mSupportAutoRotation;
+ private final int mLidOpenRotation;
+ private final int mCarDockRotation;
+ private final int mDeskDockRotation;
+ private final int mUndockedHdmiRotation;
+
+ private OrientationListener mOrientationListener;
+ private StatusBarManagerInternal mStatusBarManagerInternal;
+ private SettingsObserver mSettingsObserver;
+
+ // Default display does not rotate, apps that require non-default orientation will have to
+ // have the orientation emulated.
+ private boolean mForceDefaultOrientation;
+
+ private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ @VisibleForTesting
+ int mLandscapeRotation; // default landscape
+ @VisibleForTesting
+ int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation
+ @VisibleForTesting
+ int mPortraitRotation; // default portrait
+ @VisibleForTesting
+ int mUpsideDownRotation; // "other" portrait
+
+ // Behavior of rotation suggestions. (See Settings.Secure.SHOW_ROTATION_SUGGESTION)
+ private int mShowRotationSuggestions;
+
+ private int mAllowAllRotations = -1;
+ private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+ private int mUserRotation = Surface.ROTATION_0;
+
+ private int mDemoHdmiRotation;
+ private int mDemoRotation;
+ private boolean mDemoHdmiRotationLock;
+ private boolean mDemoRotationLock;
+
+ DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
+ this(service, displayContent, displayContent.getDisplayPolicy(),
+ service.mContext, service.getWindowManagerLock());
+ }
+
+ @VisibleForTesting
+ DisplayRotation(WindowManagerService service, DisplayContent displayContent,
+ DisplayPolicy displayPolicy, Context context, Object lock) {
+ mService = service;
+ mDisplayPolicy = displayPolicy;
+ mContext = context;
+ mLock = lock;
+ isDefaultDisplay = displayContent.isDefaultDisplay;
+
+ mSupportAutoRotation = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_supportAutoRotation);
+ mLidOpenRotation = readRotation(
+ com.android.internal.R.integer.config_lidOpenRotation);
+ mCarDockRotation = readRotation(
+ com.android.internal.R.integer.config_carDockRotation);
+ mDeskDockRotation = readRotation(
+ com.android.internal.R.integer.config_deskDockRotation);
+ mUndockedHdmiRotation = readRotation(
+ com.android.internal.R.integer.config_undockedHdmiRotation);
+
+ if (isDefaultDisplay) {
+ final Handler uiHandler = UiThread.getHandler();
+ mOrientationListener = new OrientationListener(mContext, uiHandler);
+ mOrientationListener.setCurrentRotation(displayContent.getRotation());
+ mSettingsObserver = new SettingsObserver(uiHandler);
+ mSettingsObserver.observe();
+ }
+ }
+
+ private int readRotation(int resID) {
+ try {
+ final int rotation = mContext.getResources().getInteger(resID);
+ switch (rotation) {
+ case 0:
+ return Surface.ROTATION_0;
+ case 90:
+ return Surface.ROTATION_90;
+ case 180:
+ return Surface.ROTATION_180;
+ case 270:
+ return Surface.ROTATION_270;
+ }
+ } catch (Resources.NotFoundException e) {
+ // fall through
+ }
+ return -1;
+ }
+
+ void configure(int width, int height, int shortSizeDp, int longSizeDp) {
+ final Resources res = mContext.getResources();
+ if (width > height) {
+ mLandscapeRotation = Surface.ROTATION_0;
+ mSeascapeRotation = Surface.ROTATION_180;
+ if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+ mPortraitRotation = Surface.ROTATION_90;
+ mUpsideDownRotation = Surface.ROTATION_270;
+ } else {
+ mPortraitRotation = Surface.ROTATION_270;
+ mUpsideDownRotation = Surface.ROTATION_90;
+ }
+ } else {
+ mPortraitRotation = Surface.ROTATION_0;
+ mUpsideDownRotation = Surface.ROTATION_180;
+ if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+ mLandscapeRotation = Surface.ROTATION_270;
+ mSeascapeRotation = Surface.ROTATION_90;
+ } else {
+ mLandscapeRotation = Surface.ROTATION_90;
+ mSeascapeRotation = Surface.ROTATION_270;
+ }
+ }
+
+ // For demo purposes, allow the rotation of the HDMI display to be controlled.
+ // By default, HDMI locks rotation to landscape.
+ if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
+ mDemoHdmiRotation = mPortraitRotation;
+ } else {
+ mDemoHdmiRotation = mLandscapeRotation;
+ }
+ mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
+
+ // For demo purposes, allow the rotation of the remote display to be controlled.
+ // By default, remote display locks rotation to landscape.
+ if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
+ mDemoRotation = mPortraitRotation;
+ } else {
+ mDemoRotation = mLandscapeRotation;
+ }
+ mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
+
+ // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
+ // http://developer.android.com/guide/practices/screens_support.html#range
+ // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
+ // so if the orientation is forced, we need to respect that no matter what.
+ final boolean isCar = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ // For TV, it's usually 960dp x 540dp, ignore the size limitation.
+ // so if the orientation is forced, we need to respect that no matter what.
+ final boolean isTv = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK);
+ mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
+ res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
+ // For debug purposes the next line turns this feature off with:
+ // $ adb shell setprop config.override_forced_orient true
+ // $ adb shell wm size reset
+ !"true".equals(SystemProperties.get("config.override_forced_orient"));
+ }
+
+ void setRotation(int rotation) {
+ if (mOrientationListener != null) {
+ mOrientationListener.setCurrentRotation(rotation);
+ }
+ }
+
+ void setCurrentOrientation(int newOrientation) {
+ if (newOrientation != mCurrentAppOrientation) {
+ mCurrentAppOrientation = newOrientation;
+ if (isDefaultDisplay) {
+ updateOrientationListenerLw();
+ }
+ }
+ }
+
+ /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
+ boolean isDefaultOrientationForced() {
+ return mForceDefaultOrientation;
+ }
+
+ public int getLandscapeRotation() {
+ return mLandscapeRotation;
+ }
+
+ public int getSeascapeRotation() {
+ return mSeascapeRotation;
+ }
+
+ public int getPortraitRotation() {
+ return mPortraitRotation;
+ }
+
+ public int getUpsideDownRotation() {
+ return mUpsideDownRotation;
+ }
+
+ public int getCurrentAppOrientation() {
+ return mCurrentAppOrientation;
+ }
+
+ public DisplayPolicy getDisplayPolicy() {
+ return mDisplayPolicy;
+ }
+
+ public WindowOrientationListener getOrientationListener() {
+ return mOrientationListener;
+ }
+
+ public int getUserRotation() {
+ return mUserRotation;
+ }
+
+ public int getUserRotationMode() {
+ return mUserRotationMode;
+ }
+
+ public void updateOrientationListener() {
+ synchronized (mLock) {
+ updateOrientationListenerLw();
+ }
+ }
+
+ /**
+ * Various use cases for invoking this function:
+ * <li>Screen turning off, should always disable listeners if already enabled.</li>
+ * <li>Screen turned on and current app has sensor based orientation, enable listeners
+ * if not already enabled.</li>
+ * <li>Screen turned on and current app does not have sensor orientation, disable listeners
+ * if already enabled.</li>
+ * <li>Screen turning on and current app has sensor based orientation, enable listeners
+ * if needed.</li>
+ * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
+ */
+ private void updateOrientationListenerLw() {
+ if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
+ // If sensor is turned off or nonexistent for some reason.
+ return;
+ }
+
+ final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
+ final boolean awake = mDisplayPolicy.isAwake();
+ final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
+ final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();
+
+ // Could have been invoked due to screen turning on or off or
+ // change of the currently visible window's orientation.
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly
+ + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation
+ + ", orientationSensorEnabled=" + mOrientationListener.mEnabled
+ + ", keyguardDrawComplete=" + keyguardDrawComplete
+ + ", windowManagerDrawComplete=" + windowManagerDrawComplete);
+
+ boolean disable = true;
+ // Note: We postpone the rotating of the screen until the keyguard as well as the
+ // window manager have reported a draw complete or the keyguard is going away in dismiss
+ // mode.
+ if (screenOnEarly && awake && ((keyguardDrawComplete && windowManagerDrawComplete))) {
+ if (needSensorRunning()) {
+ disable = false;
+ // Enable listener if not already enabled.
+ if (!mOrientationListener.mEnabled) {
+ // Don't clear the current sensor orientation if the keyguard is going away in
+ // dismiss mode. This allows window manager to use the last sensor reading to
+ // determine the orientation vs. falling back to the last known orientation if
+ // the sensor reading was cleared which can cause it to relaunch the app that
+ // will show in the wrong orientation first before correcting leading to app
+ // launch delays.
+ mOrientationListener.enable(true /* clearCurrentRotation */);
+ }
+ }
+ }
+ // Check if sensors need to be disabled.
+ if (disable && mOrientationListener.mEnabled) {
+ mOrientationListener.disable();
+ }
+ }
+
+ /**
+ * We always let the sensor be switched on by default except when
+ * the user has explicitly disabled sensor based rotation or when the
+ * screen is switched off.
+ */
+ private boolean needSensorRunning() {
+ if (mSupportAutoRotation) {
+ if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+ || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+ || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
+ // If the application has explicitly requested to follow the
+ // orientation, then we need to turn the sensor on.
+ return true;
+ }
+ }
+
+ final int dockMode = mDisplayPolicy.getDockMode();
+ if ((mDisplayPolicy.isCarDockEnablesAccelerometer()
+ && dockMode == Intent.EXTRA_DOCK_STATE_CAR)
+ || (mDisplayPolicy.isDeskDockEnablesAccelerometer()
+ && (dockMode == Intent.EXTRA_DOCK_STATE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK))) {
+ // Enable accelerometer if we are docked in a dock that enables accelerometer
+ // orientation management.
+ return true;
+ }
+
+ if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
+ // If the setting for using the sensor by default is enabled, then
+ // we will always leave it on. Note that the user could go to
+ // a window that forces an orientation that does not use the
+ // sensor and in theory we could turn it off... however, when next
+ // turning it on we won't have a good value for the current
+ // orientation for a little bit, which can cause orientation
+ // changes to lag, so we'd like to keep it always on. (It will
+ // still be turned off when the screen is off.)
+
+ // When locked we can provide rotation suggestions users can approve to change the
+ // current screen rotation. To do this the sensor needs to be running.
+ return mSupportAutoRotation &&
+ mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
+ }
+ return mSupportAutoRotation;
+ }
+
+ /**
+ * Given an orientation constant, returns the appropriate surface rotation,
+ * taking into account sensors, docking mode, rotation lock, and other factors.
+ *
+ * @param orientation An orientation constant, such as
+ * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
+ * @param lastRotation The most recently used rotation.
+ * @param defaultDisplay Flag indicating whether the rotation is computed for the default
+ * display. Currently for all non-default displays sensors, docking mode,
+ * rotation lock and other factors are ignored.
+ * @return The surface rotation to use.
+ */
+ int rotationForOrientation(int orientation, int lastRotation) {
+ if (DEBUG_ORIENTATION) {
+ Slog.v(TAG, "rotationForOrientation(orient="
+ + orientation + ", last=" + lastRotation
+ + "); user=" + mUserRotation + " "
+ + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+ ? "USER_ROTATION_LOCKED" : "")
+ );
+ }
+
+ if (mForceDefaultOrientation) {
+ return Surface.ROTATION_0;
+ }
+
+ int sensorRotation = mOrientationListener != null
+ ? mOrientationListener.getProposedRotation() // may be -1
+ : -1;
+ if (sensorRotation < 0) {
+ sensorRotation = lastRotation;
+ }
+
+ final int lidState = mDisplayPolicy.getLidState();
+ final int dockMode = mDisplayPolicy.getDockMode();
+ final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
+ final boolean carDockEnablesAccelerometer =
+ mDisplayPolicy.isCarDockEnablesAccelerometer();
+ final boolean deskDockEnablesAccelerometer =
+ mDisplayPolicy.isDeskDockEnablesAccelerometer();
+
+ final int preferredRotation;
+ if (!isDefaultDisplay) {
+ // For secondary displays we ignore things like displays sensors, docking mode and
+ // rotation lock, and always prefer a default rotation.
+ preferredRotation = Surface.ROTATION_0;
+ } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
+ // Ignore sensor when lid switch is open and rotation is forced.
+ preferredRotation = mLidOpenRotation;
+ } else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR
+ && (carDockEnablesAccelerometer || mCarDockRotation >= 0)) {
+ // Ignore sensor when in car dock unless explicitly enabled.
+ // This case can override the behavior of NOSENSOR, and can also
+ // enable 180 degree rotation while docked.
+ preferredRotation = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;
+ } else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+ && (deskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
+ // Ignore sensor when in desk dock unless explicitly enabled.
+ // This case can override the behavior of NOSENSOR, and can also
+ // enable 180 degree rotation while docked.
+ preferredRotation = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;
+ } else if (hdmiPlugged && mDemoHdmiRotationLock) {
+ // Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
+ // Note that the dock orientation overrides the HDMI orientation.
+ preferredRotation = mDemoHdmiRotation;
+ } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+ && mUndockedHdmiRotation >= 0) {
+ // Ignore sensor when plugged into HDMI and an undocked orientation has
+ // been specified in the configuration (only for legacy devices without
+ // full multi-display support).
+ // Note that the dock orientation overrides the HDMI orientation.
+ preferredRotation = mUndockedHdmiRotation;
+ } else if (mDemoRotationLock) {
+ // Ignore sensor when demo rotation lock is enabled.
+ // Note that the dock orientation and HDMI rotation lock override this.
+ preferredRotation = mDemoRotation;
+ } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
+ // While in VR, apps always prefer a portrait rotation. This does not change
+ // any apps that explicitly set landscape, but does cause sensors be ignored,
+ // and ignored any orientation lock that the user has set (this conditional
+ // should remain above the ORIENTATION_LOCKED conditional below).
+ preferredRotation = mPortraitRotation;
+ } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+ // Application just wants to remain locked in the last rotation.
+ preferredRotation = lastRotation;
+ } else if (!mSupportAutoRotation) {
+ // If we don't support auto-rotation then bail out here and ignore
+ // the sensor and any rotation lock settings.
+ preferredRotation = -1;
+ } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+ && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
+ // Otherwise, use sensor only if requested by the application or enabled
+ // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
+ if (mAllowAllRotations < 0) {
+ // Can't read this during init() because the context doesn't
+ // have display metrics at that time so we cannot determine
+ // tablet vs. phone then.
+ mAllowAllRotations = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
+ }
+ if (sensorRotation != Surface.ROTATION_180
+ || mAllowAllRotations == 1
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+ preferredRotation = sensorRotation;
+ } else {
+ preferredRotation = lastRotation;
+ }
+ } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+ // Apply rotation lock. Does not apply to NOSENSOR.
+ // The idea is that the user rotation expresses a weak preference for the direction
+ // of gravity and as NOSENSOR is never affected by gravity, then neither should
+ // NOSENSOR be affected by rotation lock (although it will be affected by docks).
+ preferredRotation = mUserRotation;
+ } else {
+ // No overriding preference.
+ // We will do exactly what the application asked us to do.
+ preferredRotation = -1;
+ }
+
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ // Return portrait unless overridden.
+ if (isAnyPortrait(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mPortraitRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ // Return landscape unless overridden.
+ if (isLandscapeOrSeascape(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mLandscapeRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ // Return reverse portrait unless overridden.
+ if (isAnyPortrait(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mUpsideDownRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ // Return seascape unless overridden.
+ if (isLandscapeOrSeascape(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mSeascapeRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ // Return either landscape rotation.
+ if (isLandscapeOrSeascape(preferredRotation)) {
+ return preferredRotation;
+ }
+ if (isLandscapeOrSeascape(lastRotation)) {
+ return lastRotation;
+ }
+ return mLandscapeRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ // Return either portrait rotation.
+ if (isAnyPortrait(preferredRotation)) {
+ return preferredRotation;
+ }
+ if (isAnyPortrait(lastRotation)) {
+ return lastRotation;
+ }
+ return mPortraitRotation;
+
+ default:
+ // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
+ // just return the preferred orientation we already calculated.
+ if (preferredRotation >= 0) {
+ return preferredRotation;
+ }
+ return Surface.ROTATION_0;
+ }
+ }
+
+ private boolean isLandscapeOrSeascape(int rotation) {
+ return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
+ }
+
+ private boolean isAnyPortrait(int rotation) {
+ return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
+ }
+
+ /**
+ * Given an orientation constant and a rotation, returns true if the rotation
+ * has compatible metrics to the requested orientation. For example, if
+ * the application requested landscape and got seascape, then the rotation
+ * has compatible metrics; if the application requested portrait and got landscape,
+ * then the rotation has incompatible metrics; if the application did not specify
+ * a preference, then anything goes.
+ *
+ * @param orientation An orientation constant, such as
+ * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
+ * @param rotation The rotation to check.
+ * @return True if the rotation is compatible with the requested orientation.
+ */
+ boolean rotationHasCompatibleMetrics(int orientation, int rotation) {
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ return isAnyPortrait(rotation);
+
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ return isLandscapeOrSeascape(rotation);
+
+ default:
+ return true;
+ }
+ }
+
+ private boolean isValidRotationChoice(final int preferredRotation) {
+ // Determine if the given app orientation is compatible with the provided rotation choice.
+ switch (mCurrentAppOrientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ // Works with any of the 4 rotations.
+ return preferredRotation >= 0;
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ // It's possible for the user pref to be set at 180 because of FULL_USER. This would
+ // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
+ // but never to go to 180.
+ return preferredRotation == mPortraitRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ // Works landscape or seascape.
+ return isLandscapeOrSeascape(preferredRotation);
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+ // Works with any rotation except upside down.
+ return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+ }
+
+ return false;
+ }
+
+ private boolean isRotationChoicePossible(int orientation) {
+ // Rotation choice is only shown when the user is in locked mode.
+ if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+ // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
+ // demo, hdmi, vr, etc mode.
+
+ // Determine if the rotation is currently forced.
+ if (mForceDefaultOrientation) {
+ return false; // Rotation is forced to default orientation.
+ }
+
+ final int lidState = mDisplayPolicy.getLidState();
+ if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
+ return false; // Rotation is forced mLidOpenRotation.
+ }
+
+ final int dockMode = mDisplayPolicy.getDockMode();
+ final boolean carDockEnablesAccelerometer = false;
+ if (dockMode == Intent.EXTRA_DOCK_STATE_CAR && !carDockEnablesAccelerometer) {
+ return false; // Rotation forced to mCarDockRotation.
+ }
+
+ final boolean deskDockEnablesAccelerometer =
+ mDisplayPolicy.isDeskDockEnablesAccelerometer();
+ if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
+ && !deskDockEnablesAccelerometer) {
+ return false; // Rotation forced to mDeskDockRotation.
+ }
+
+ final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();
+ if (hdmiPlugged && mDemoHdmiRotationLock) {
+ return false; // Rotation forced to mDemoHdmiRotation.
+
+ } else if (hdmiPlugged && dockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
+ && mUndockedHdmiRotation >= 0) {
+ return false; // Rotation forced to mUndockedHdmiRotation.
+
+ } else if (mDemoRotationLock) {
+ return false; // Rotation forced to mDemoRotation.
+
+ } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
+ return false; // Rotation forced to mPortraitRotation.
+
+ } else if (!mSupportAutoRotation) {
+ return false;
+ }
+
+ // Ensure that some rotation choice is possible for the given orientation.
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ // NOSENSOR description is ambiguous, in reality WM ignores user choice.
+ return true;
+ }
+
+ // Rotation is forced, should be controlled by system.
+ return false;
+ }
+
+ /** Notify the StatusBar that system rotation suggestion has changed. */
+ private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
+ if (mStatusBarManagerInternal == null) {
+ mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
+ }
+ if (mStatusBarManagerInternal != null) {
+ mStatusBarManagerInternal.onProposedRotationChanged(rotation, isValid);
+ }
+ }
+
+ private static String allowAllRotationsToString(int allowAll) {
+ switch (allowAll) {
+ case -1:
+ return "unknown";
+ case 0:
+ return "false";
+ case 1:
+ return "true";
+ default:
+ return Integer.toString(allowAll);
+ }
+ }
+
+ public void onUserSwitch() {
+ if (mSettingsObserver != null) {
+ mSettingsObserver.onChange(false);
+ }
+ }
+
+ /** Return whether the rotation settings has changed. */
+ private boolean updateSettings() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ boolean shouldUpdateRotation = false;
+
+ synchronized (mLock) {
+ boolean shouldUpdateOrientationListener = false;
+
+ // Configure rotation suggestions.
+ final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT,
+ UserHandle.USER_CURRENT);
+ if (mShowRotationSuggestions != showRotationSuggestions) {
+ mShowRotationSuggestions = showRotationSuggestions;
+ shouldUpdateOrientationListener = true;
+ }
+
+ // Configure rotation lock.
+ final int userRotation = Settings.System.getIntForUser(resolver,
+ Settings.System.USER_ROTATION, Surface.ROTATION_0,
+ UserHandle.USER_CURRENT);
+ if (mUserRotation != userRotation) {
+ mUserRotation = userRotation;
+ shouldUpdateRotation = true;
+ }
+
+ final int userRotationMode = Settings.System.getIntForUser(resolver,
+ Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0
+ ? WindowManagerPolicy.USER_ROTATION_FREE
+ : WindowManagerPolicy.USER_ROTATION_LOCKED;
+ if (mUserRotationMode != userRotationMode) {
+ mUserRotationMode = userRotationMode;
+ shouldUpdateOrientationListener = true;
+ shouldUpdateRotation = true;
+ }
+
+ if (shouldUpdateOrientationListener) {
+ updateOrientationListenerLw(); // Enable or disable the orientation listener.
+ }
+ }
+
+ return shouldUpdateRotation;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DisplayRotation");
+ pw.println(prefix + " mCurrentAppOrientation="
+ + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
+ pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
+ pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
+ pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
+ pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
+
+ pw.print(prefix + " mSupportAutoRotation=" + mSupportAutoRotation);
+ if (mOrientationListener != null) {
+ pw.print(" mOrientationSensorEnabled=" + mOrientationListener.mEnabled);
+ }
+ pw.println();
+
+ pw.print(prefix + " mCarDockRotation=" + Surface.rotationToString(mCarDockRotation));
+ pw.println(" mDeskDockRotation=" + Surface.rotationToString(mDeskDockRotation));
+ pw.print(prefix + " mUserRotationMode="
+ + WindowManagerPolicy.userRotationModeToString(mUserRotationMode));
+ pw.print(" mUserRotation=" + Surface.rotationToString(mUserRotation));
+ pw.println(" mAllowAllRotations=" + allowAllRotationsToString(mAllowAllRotations));
+
+ pw.print(prefix + " mDemoHdmiRotation=" + Surface.rotationToString(mDemoHdmiRotation));
+ pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
+ pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
+ pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+ }
+
+ private class OrientationListener extends WindowOrientationListener {
+ final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
+ boolean mEnabled;
+
+ OrientationListener(Context context, Handler handler) {
+ super(context, handler);
+ }
+
+ private class UpdateRunnable implements Runnable {
+ final int mRotation;
+
+ UpdateRunnable(int rotation) {
+ mRotation = rotation;
+ }
+
+ @Override
+ public void run() {
+ // Send interaction hint to improve redraw performance.
+ mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
+ if (isRotationChoicePossible(mCurrentAppOrientation)) {
+ final boolean isValid = isValidRotationChoice(mRotation);
+ sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
+ } else {
+ mService.updateRotation(false /* alwaysSendConfiguration */,
+ false /* forceRelayout */);
+ }
+ }
+ }
+
+ @Override
+ public void onProposedRotationChanged(int rotation) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
+ Runnable r = mRunnableCache.get(rotation, null);
+ if (r == null) {
+ r = new UpdateRunnable(rotation);
+ mRunnableCache.put(rotation, r);
+ }
+ getHandler().post(r);
+ }
+
+ @Override
+ public void enable(boolean clearCurrentRotation) {
+ super.enable(clearCurrentRotation);
+ mEnabled = true;
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
+ }
+
+ @Override
+ public void disable() {
+ super.disable();
+ mEnabled = false;
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
+ }
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_ROTATION_SUGGESTIONS), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.ACCELEROMETER_ROTATION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.USER_ROTATION), false, this,
+ UserHandle.USER_ALL);
+ updateSettings();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (updateSettings()) {
+ mService.updateRotation(true /* alwaysSendConfiguration */,
+ false /* forceRelayout */);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index d53a21b96250..c4beb553e0ec 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -399,7 +399,8 @@ final class InputMonitor {
this.inDrag = inDrag;
wallpaperController = mService.mRoot.mWallpaperController;
- mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
+ // TODO(b/112081256): Use independent InputMonitor for each display.
+ mService.mRoot/*.getDisplayContent(mDisplayId)*/.forAllWindows(this,
true /* traverseTopToBottom */);
if (mAddWallpaperInputConsumerHandle) {
// No visible wallpaper found, add the wallpaper input consumer at the end.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3df4eb7fbf38..ca4fa648e543 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -134,6 +134,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
+ private final Consumer<DisplayContent> mDisplayContentConfigChangesConsumer =
+ mService.mPolicy::onConfigurationChanged;
+
private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
if (w.mHasSurface) {
try {
@@ -379,7 +382,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
prepareFreezingTaskBounds();
super.onConfigurationChanged(newParentConfig);
- mService.mPolicy.onConfigurationChanged();
+ forAllDisplays(mDisplayContentConfigChangesConsumer);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9b792e3f2d13..ea68e17732af 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2480,10 +2480,7 @@ public class WindowManagerService extends IWindowManager.Stub
dc.setLastOrientation(req);
//send a message to Policy indicating orientation change to take
//action like disabling/enabling sensors etc.,
- // TODO(multi-display): Implement policy for secondary displays.
- if (dc.isDefaultDisplay) {
- mPolicy.setCurrentOrientationLw(req);
- }
+ dc.getDisplayRotation().setCurrentOrientation(req);
return dc.updateRotationUnchecked(forceUpdate);
}
return false;
@@ -2492,27 +2489,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // If this is true we have updated our desired orientation, but not yet
- // changed the real orientation our applied our screen rotation animation.
- // For example, because a previous screen rotation was in progress.
- boolean rotationNeedsUpdateLocked() {
- // TODO(multi-display): Check for updates on all displays. Need to have per-display policy
- // to implement WindowManagerPolicy#rotationForOrientationLw() correctly.
- final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
- final int lastOrientation = defaultDisplayContent.getLastOrientation();
- final int oldRotation = defaultDisplayContent.getRotation();
- final boolean oldAltOrientation = defaultDisplayContent.getAltOrientation();
-
- final int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
- true /* defaultDisplay */);
- boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
- lastOrientation, rotation);
- if (oldRotation == rotation && oldAltOrientation == altOrientation) {
- return false;
- }
- return true;
- }
-
@Override
public int[] setNewDisplayOverrideConfiguration(Configuration overrideConfig, int displayId) {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "setNewDisplayOverrideConfiguration()")) {
@@ -3841,28 +3817,31 @@ public class WindowManagerService extends IWindowManager.Stub
long origId = Binder.clearCallingIdentity();
try {
- // TODO(multi-display): Update rotation for different displays separately.
- final boolean rotationChanged;
- final int displayId;
synchronized (mWindowMap) {
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
- rotationChanged = displayContent.updateRotationUnchecked();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- if (!rotationChanged || forceRelayout) {
- displayContent.setLayoutNeeded();
+ boolean layoutNeeded = false;
+ final int displayCount = mRoot.mChildren.size();
+ for (int i = 0; i < displayCount; ++i) {
+ final DisplayContent displayContent = mRoot.mChildren.get(i);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
+ final boolean rotationChanged = displayContent.updateRotationUnchecked();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+
+ if (!rotationChanged || forceRelayout) {
+ displayContent.setLayoutNeeded();
+ layoutNeeded = true;
+ }
+ if (rotationChanged || alwaysSendConfiguration) {
+ mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+ .sendToTarget();
+ }
+ }
+
+ if (layoutNeeded) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"updateRotation: performSurfacePlacement");
mWindowPlacerLocked.performSurfacePlacement();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- displayId = displayContent.getDisplayId();
- }
-
- if (rotationChanged || alwaysSendConfiguration) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
- sendNewConfiguration(displayId);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -3871,6 +3850,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public WindowManagerPolicy.DisplayContentInfo getDefaultDisplayContentInfo() {
+ synchronized (mWindowMap) {
+ return getDefaultDisplayContentLocked();
+ }
+ }
+
+ @Override
public int getDefaultDisplayRotation() {
synchronized (mWindowMap) {
return getDefaultDisplayContentLocked().getRotation();
@@ -5792,7 +5778,7 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- mPolicy.isDefaultOrientationForced(), isSecure,
+ displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
@@ -6082,23 +6068,26 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) {
synchronized (mWindowMap) {
- mRoot.forAllDisplays(dc -> {
- dc.getInputMonitor().createInputConsumer(token, name, inputChannel,
+ // TODO(b/112049699): Fix this for multiple displays. There is only one inputChannel
+ // here to accept the return value.
+ DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ if (display != null) {
+ display.getInputMonitor().createInputConsumer(token, name, inputChannel,
Binder.getCallingPid(), Binder.getCallingUserHandle());
- });
+ }
}
}
@Override
public boolean destroyInputConsumer(String name) {
synchronized (mWindowMap) {
- AtomicBoolean retValue = new AtomicBoolean(true);
- mRoot.forAllDisplays(dc -> {
- if (!dc.getInputMonitor().destroyInputConsumer(name)) {
- retValue.set(false);
- }
- });
- return retValue.get();
+ // TODO(b/112049699): Fix this for multiple displays. For consistency with
+ // createInputConsumer above.
+ DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ if (display != null) {
+ return display.getInputMonitor().destroyInputConsumer(name);
+ }
+ return false;
}
}
@@ -6761,8 +6750,10 @@ public class WindowManagerService extends IWindowManager.Stub
public void onOverlayChanged() {
synchronized (mWindowMap) {
- mPolicy.onOverlayChangedLw();
- getDefaultDisplayContentLocked().updateDisplayInfo();
+ mRoot.forAllDisplays(displayContent -> {
+ mPolicy.onOverlayChangedLw(displayContent);
+ displayContent.updateDisplayInfo();
+ });
requestTraversal();
}
}
@@ -7090,19 +7081,24 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void dontOverrideDisplayInfo(int displayId) {
- synchronized (mWindowMap) {
- final DisplayContent dc = getDisplayContentOrCreate(displayId);
- if (dc == null) {
- throw new IllegalArgumentException(
- "Trying to register a non existent display.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mWindowMap) {
+ final DisplayContent dc = getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ throw new IllegalArgumentException(
+ "Trying to register a non existent display.");
+ }
+ // We usually set the override info in DisplayManager so that we get consistent
+ // values when displays are changing. However, we don't do this for displays that
+ // serve as containers for ActivityViews because we don't want letter-/pillar-boxing
+ // during resize.
+ dc.mShouldOverrideDisplayConfiguration = false;
+ mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId,
+ null /* info */);
}
- // We usually set the override info in DisplayManager so that we get consistent
- // values when displays are changing. However, we don't do this for displays that
- // serve as containers for ActivityViews because we don't want letter-/pillar-boxing
- // during resize.
- dc.mShouldOverrideDisplayConfiguration = false;
- mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId,
- null /* info */);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -7141,11 +7137,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
finishSeamlessRotation();
- final DisplayContent displayContent = w.getDisplayContent();
- if (displayContent.updateRotationUnchecked()) {
- mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
- .sendToTarget();
- }
+ w.getDisplayContent().updateRotationAndSendNewConfigIfNeeded();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index aced8e46ab10..08decdfc13e5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -569,7 +569,7 @@ class WindowSurfacePlacer {
// animation after the old one finally finishes. It's better to defer the
// app transition.
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
- mService.rotationNeedsUpdateLocked()) {
+ mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) {
if (DEBUG_APP_TRANSITIONS) {
Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
}
diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
new file mode 100644
index 000000000000..dd0a58d5e98e
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class KeyValueBackupJobTest {
+ private Context mContext;
+ private BackupManagerConstants mConstants;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+ mConstants = new BackupManagerConstants(Handler.getMain(), mContext.getContentResolver());
+ mConstants.start();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mConstants.stop();
+ KeyValueBackupJob.cancel(mContext);
+ }
+
+ @Test
+ public void testIsScheduled_beforeScheduling_returnsFalse() {
+ boolean isScheduled = KeyValueBackupJob.isScheduled();
+
+ assertThat(isScheduled).isFalse();
+ }
+
+ @Test
+ public void testIsScheduled_afterScheduling_returnsTrue() {
+ KeyValueBackupJob.schedule(mContext, mConstants);
+
+ boolean isScheduled = KeyValueBackupJob.isScheduled();
+
+ assertThat(isScheduled).isTrue();
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index 8fab19793914..1f6ac8d00ba1 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -54,6 +54,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
+import static org.robolectric.shadow.api.Shadow.extract;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Collections.emptyList;
@@ -79,6 +80,7 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.DeadObjectException;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
@@ -101,6 +103,7 @@ import com.android.server.backup.transport.TransportClient;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
import com.android.server.testing.SystemLoaderPackages;
+import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowBackupDataInput;
import com.android.server.testing.shadows.ShadowBackupDataOutput;
import com.android.server.testing.shadows.ShadowEventLog;
@@ -143,10 +146,11 @@ import java.util.stream.Stream;
manifest = Config.NONE,
sdk = 26,
shadows = {
+ FrameworkShadowLooper.class,
ShadowBackupDataInput.class,
ShadowBackupDataOutput.class,
ShadowEventLog.class,
- ShadowQueuedWork.class
+ ShadowQueuedWork.class,
})
@SystemLoaderPackages({"com.android.server.backup", "android.app.backup"})
@SystemLoaderClasses({IBackupTransport.class, IBackupAgent.class, PackageInfo.class})
@@ -171,6 +175,7 @@ public class PerformBackupTaskTest {
private File mDataDir;
private Application mApplication;
private ShadowApplication mShadowApplication;
+ private FrameworkShadowLooper mShadowMainLooper;
private Context mContext;
@Before
@@ -183,6 +188,8 @@ public class PerformBackupTaskTest {
mShadowApplication = shadowOf(mApplication);
mContext = mApplication;
+ mShadowMainLooper = extract(Looper.getMainLooper());
+
File cacheDir = mApplication.getCacheDir();
// Corresponds to /data/backup
mBaseStateDir = new File(cacheDir, "base_state");
@@ -337,7 +344,7 @@ public class PerformBackupTaskTest {
runTask(task);
- assertEventLogged(EventLogTags.BACKUP_START, mTransport.transportDirName);
+ assertEventLogged(EventLogTags.BACKUP_START, mTransport.transportName);
}
@Test
@@ -1571,11 +1578,10 @@ public class PerformBackupTaskTest {
}
private void runTask(PerformBackupTask task) {
- Message message = mBackupHandler.obtainMessage(BackupHandler.MSG_BACKUP_RESTORE_STEP, task);
- mBackupHandler.sendMessage(message);
- while (mShadowBackupLooper.getScheduler().areAnyRunnable()) {
- mShadowBackupLooper.runToEndOfTasks();
- }
+ // Pretend we are not on the main-thread to prevent RemoteCall from complaining
+ mShadowMainLooper.setCurrentThread(false);
+ task.run();
+ mShadowMainLooper.reset();
assertTaskPostConditions();
}
@@ -1699,7 +1705,7 @@ public class PerformBackupTaskTest {
String transportDirName,
boolean nonIncremental,
PackageData... packages) {
- ArrayList<BackupRequest> backupRequests =
+ ArrayList<BackupRequest> keyValueBackupRequests =
Stream.of(packages)
.map(packageData -> packageData.packageName)
.map(BackupRequest::new)
@@ -1713,7 +1719,7 @@ public class PerformBackupTaskTest {
mBackupManagerService,
transportClient,
transportDirName,
- backupRequests,
+ keyValueBackupRequests,
mOldJournal,
mObserver,
mMonitor,
diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
new file mode 100644
index 000000000000..aec207d2fea6
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.util.concurrent.CompletableFuture;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class FutureBackupCallbackTest {
+ @Test
+ public void testOperationComplete_completesFuture() throws Exception {
+ CompletableFuture<RemoteResult> future = new CompletableFuture<>();
+ FutureBackupCallback callback = new FutureBackupCallback(future);
+
+ callback.operationComplete(7);
+
+ assertThat(future.get()).isEqualTo(RemoteResult.successful(7));
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
new file mode 100644
index 000000000000..55db6160481a
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
+import static com.android.server.backup.testing.TestUtils.uncheck;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupCallback;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.testing.TestUtils.ThrowingRunnable;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class RemoteCallTest {
+ /** A {@link RemoteCallable} that calls the callback immediately. */
+ private final RemoteCallable<IBackupCallback> IMMEDIATE_CALLABLE =
+ callback -> callback.operationComplete(0);
+
+ @Mock private RemoteCallable<IBackupCallback> mCallable;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testCall_whenCancelledAndImmediateCallableAndTimeOut0_returnsCancel()
+ throws Exception {
+ RemoteCall remoteCall = new RemoteCall(true, IMMEDIATE_CALLABLE, 0);
+
+ RemoteResult result = runInWorkerThread(remoteCall::call);
+
+ assertThat(result).isEqualTo(RemoteResult.FAILED_CANCELLED);
+ }
+
+ @Test
+ public void testCall_whenCancelledAndImmediateCallableAndTimeOut0_doesNotCallCallable()
+ throws Exception {
+ RemoteCall remoteCall = new RemoteCall(true, IMMEDIATE_CALLABLE, 0);
+
+ runInWorkerThread(remoteCall::call);
+
+ verify(mCallable, never()).call(any());
+ }
+
+ @Test
+ public void testCall_whenImmediateCallableAndTimeOut0AndCancelIsCalledBeforeCall_returnsCancel()
+ throws Exception {
+ RemoteCall remoteCall = new RemoteCall(IMMEDIATE_CALLABLE, 0);
+ remoteCall.cancel();
+
+ RemoteResult result = runInWorkerThread(remoteCall::call);
+
+ assertThat(result).isEqualTo(RemoteResult.FAILED_CANCELLED);
+ }
+
+ @Test
+ public void
+ testCall_whenImmediateCallableAndTimeOut0AndCancelIsCalledBeforeCall_doesNotCallCallable()
+ throws Exception {
+ RemoteCall remoteCall = new RemoteCall(IMMEDIATE_CALLABLE, 0);
+ remoteCall.cancel();
+
+ runInWorkerThread(remoteCall::call);
+
+ verify(mCallable, never()).call(any());
+ }
+
+ @Test
+ public void testCall_whenImmediateCallableAndTimeOut0_returnsTimeOut() throws Exception {
+ RemoteCall remoteCall = new RemoteCall(IMMEDIATE_CALLABLE, 0);
+
+ RemoteResult result = runInWorkerThread(remoteCall::call);
+
+ assertThat(result).isEqualTo(RemoteResult.FAILED_TIMED_OUT);
+ }
+
+ @Test
+ public void testCall_whenTimeOut0_doesNotCallCallable() throws Exception {
+ RemoteCall remoteCall = new RemoteCall(mCallable, 0);
+
+ runInWorkerThread(remoteCall::call);
+
+ verify(mCallable, never()).call(any());
+ }
+
+ @Test
+ public void testCall_whenTimesOutBeforeCallbackIsCalled_returnsTimeOut() throws Exception {
+ ConditionVariable scheduled = new ConditionVariable(false);
+ RemoteCall remoteCall =
+ new RemoteCall(
+ callback -> {
+ postDelayed(
+ Handler.getMain(), () -> callback.operationComplete(0), 1000);
+ scheduled.open();
+ },
+ 500);
+
+ Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+ // Method runToEndOfTasks() will execute what was posted to the main handler, which is the
+ // completion of the callback and the time-out (that was scheduled by RemoteCall). But to be
+ // able to execute everything we have to ensure that runToEndOfTasks() is called *after*
+ // everything has been scheduled, that's why we use the condition variable scheduled, that
+ // is set to true (i.e. opened) when everything is scheduled, allowing us to run the tasks.
+ scheduled.block();
+ runToEndOfTasks(Looper.getMainLooper());
+ assertThat(result.get()).isEqualTo(RemoteResult.FAILED_TIMED_OUT);
+ }
+
+ @Test
+ public void testCall_whenTimesOutBeforeCancelIsCalled_returnsTimeOut() throws Exception {
+ ConditionVariable scheduled = new ConditionVariable(false);
+ RemoteCall remoteCall = new RemoteCall(callback -> scheduled.open(), 500);
+
+ Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+ scheduled.block();
+ runToEndOfTasks(Looper.getMainLooper());
+ remoteCall.cancel();
+ assertThat(result.get()).isEqualTo(RemoteResult.FAILED_TIMED_OUT);
+ }
+
+ @Test
+ public void testCall_whenCallbackIsCalledBeforeTimeOut_returnsSuccess() throws Exception {
+ ConditionVariable scheduled = new ConditionVariable(false);
+ RemoteCall remoteCall =
+ new RemoteCall(
+ callback -> {
+ postDelayed(
+ Handler.getMain(), () -> callback.operationComplete(3), 500);
+ scheduled.open();
+ },
+ 1000);
+
+ Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+ scheduled.block();
+ runToEndOfTasks(Looper.getMainLooper());
+ assertThat(result.get()).isEqualTo(RemoteResult.successful(3));
+ }
+
+ @Test
+ public void testCall_whenCallbackIsCalledBeforeCancel_returnsSuccess() throws Exception {
+ CompletableFuture<IBackupCallback> callbackFuture = new CompletableFuture<>();
+ RemoteCall remoteCall = new RemoteCall(callbackFuture::complete, 1000);
+
+ Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+ // callbackFuture.get() will return when callable is executed (i.e. inside
+ // remoteCall.call()), at which point we can complete it.
+ IBackupCallback callback = callbackFuture.get();
+ callback.operationComplete(3);
+ remoteCall.cancel();
+ assertThat(result.get()).isEqualTo(RemoteResult.successful(3));
+ }
+
+ @Test
+ public void testCall_whenCancelIsCalledBeforeCallbackButAfterCall_returnsCancel()
+ throws Exception {
+ CompletableFuture<IBackupCallback> callbackFuture = new CompletableFuture<>();
+ RemoteCall remoteCall = new RemoteCall(callbackFuture::complete, 1000);
+
+ Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+ IBackupCallback callback = callbackFuture.get();
+ remoteCall.cancel();
+ callback.operationComplete(3);
+ assertThat(result.get()).isEqualTo(RemoteResult.FAILED_CANCELLED);
+ }
+
+ @Test
+ public void testCall_whenCancelIsCalledBeforeTimeOutButAfterCall_returnsCancel()
+ throws Exception {
+ ConditionVariable scheduled = new ConditionVariable(false);
+ RemoteCall remoteCall = new RemoteCall(callback -> scheduled.open(), 1000);
+
+ Future<RemoteResult> result = runInWorkerThreadAsync(remoteCall::call);
+
+ scheduled.block();
+ remoteCall.cancel();
+ runToEndOfTasks(Looper.getMainLooper());
+ assertThat(result.get()).isEqualTo(RemoteResult.FAILED_CANCELLED);
+ }
+
+ private static <T> Future<T> runInWorkerThreadAsync(Callable<T> supplier) {
+ CompletableFuture<T> future = new CompletableFuture<>();
+ new Thread(() -> future.complete(uncheck(supplier)), "test-worker-thread").start();
+ return future;
+ }
+
+ private static <T> T runInWorkerThread(Callable<T> supplier) throws Exception {
+ return runInWorkerThreadAsync(supplier).get();
+ }
+
+ /** Unchecked version of {@link Handler#postDelayed(Runnable, long)}. */
+ private static void postDelayed(Handler handler, ThrowingRunnable runnable, long delayMillis) {
+ handler.postDelayed(() -> uncheck(runnable), delayMillis);
+ }
+
+ /** Unchecked version of {@link Handler#post(Runnable)}. */
+ private static void post(Handler handler, ThrowingRunnable runnable) {
+ handler.post(() -> uncheck(runnable));
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
new file mode 100644
index 000000000000..f1c4f27bc38e
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class RemoteResultTest {
+ @Test
+ public void testSucceeded_whenSuccessfulResult_returnsTrue() {
+ RemoteResult result = RemoteResult.successful(3);
+
+ boolean succeeded = result.succeeded();
+
+ assertThat(succeeded).isTrue();
+ }
+
+ @Test
+ public void testSucceeded_whenFailedResults_returnsFalse() {
+ boolean timeOutSucceeded = RemoteResult.FAILED_TIMED_OUT.succeeded();
+ boolean cancelledSucceeded = RemoteResult.FAILED_CANCELLED.succeeded();
+ boolean threadInterruptedSucceeded = RemoteResult.FAILED_THREAD_INTERRUPTED.succeeded();
+
+ assertThat(timeOutSucceeded).isFalse();
+ assertThat(cancelledSucceeded).isFalse();
+ assertThat(threadInterruptedSucceeded).isFalse();
+ }
+
+ @Test
+ public void testGet_whenSuccessfulResult_returnsValue() {
+ RemoteResult result = RemoteResult.successful(7);
+
+ long value = result.get();
+
+ assertThat(value).isEqualTo(7);
+ }
+
+ @Test
+ public void testGet_whenFailedResult_throws() {
+ RemoteResult result = RemoteResult.FAILED_TIMED_OUT;
+
+ expectThrows(IllegalStateException.class, result::get);
+ }
+
+ @Test
+ public void testToString() {
+ assertThat(RemoteResult.successful(3).toString()).isEqualTo("RemoteResult{3}");
+ assertThat(RemoteResult.FAILED_TIMED_OUT.toString())
+ .isEqualTo("RemoteResult{FAILED_TIMED_OUT}");
+ assertThat(RemoteResult.FAILED_CANCELLED.toString())
+ .isEqualTo("RemoteResult{FAILED_CANCELLED}");
+ assertThat(RemoteResult.FAILED_THREAD_INTERRUPTED.toString())
+ .isEqualTo("RemoteResult{FAILED_THREAD_INTERRUPTED}");
+ }
+
+ @Test
+ public void testEquals() {
+ assertThat(RemoteResult.successful(3).equals(RemoteResult.successful(3))).isTrue();
+ assertThat(RemoteResult.successful(3).equals(RemoteResult.successful(7))).isFalse();
+ assertThat(RemoteResult.successful(-1).equals(RemoteResult.successful(1))).isFalse();
+ assertThat(RemoteResult.successful(Long.MAX_VALUE).equals(RemoteResult.successful(-1)))
+ .isFalse();
+ assertThat(RemoteResult.successful(3).equals(RemoteResult.FAILED_TIMED_OUT)).isFalse();
+ assertThat(RemoteResult.successful(3).equals("3")).isFalse();
+ assertThat(RemoteResult.successful(3).equals(null)).isFalse();
+ assertThat(RemoteResult.FAILED_TIMED_OUT.equals(RemoteResult.FAILED_TIMED_OUT)).isTrue();
+ assertThat(RemoteResult.FAILED_TIMED_OUT.equals(RemoteResult.FAILED_CANCELLED)).isFalse();
+ }
+
+ /** @see Object#hashCode() */
+ @Test
+ public void testHashCode() {
+ RemoteResult result3 = RemoteResult.successful(3);
+ assertThat(result3.hashCode()).isEqualTo(result3.hashCode());
+ assertThat(result3.hashCode()).isEqualTo(RemoteResult.successful(3).hashCode());
+ assertThat(RemoteResult.FAILED_TIMED_OUT.hashCode())
+ .isEqualTo(RemoteResult.FAILED_TIMED_OUT.hashCode());
+ assertThat(RemoteResult.FAILED_CANCELLED.hashCode())
+ .isEqualTo(RemoteResult.FAILED_CANCELLED.hashCode());
+ assertThat(RemoteResult.FAILED_THREAD_INTERRUPTED.hashCode())
+ .isEqualTo(RemoteResult.FAILED_THREAD_INTERRUPTED.hashCode());
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
new file mode 100644
index 000000000000..e0d3c0c09eb8
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.remote;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class ServiceBackupCallbackTest {
+ @Test
+ public void testOperationComplete_callsBackupManagerOpComplete() throws Exception {
+ IBackupManager backupManager = mock(IBackupManager.class);
+ ServiceBackupCallback callback = new ServiceBackupCallback(backupManager, 0x68e);
+
+ callback.operationComplete(7);
+
+ verify(backupManager).opComplete(0x68e, 7);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 23d4ba494396..084f27f94fbf 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -16,6 +16,8 @@
package com.android.server.backup.testing;
+import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -46,8 +48,6 @@ import org.mockito.stubbing.Answer;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowBinder;
import org.robolectric.shadows.ShadowLog;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
@@ -79,14 +79,7 @@ public class BackupManagerServiceTestUtils {
baseStateDir,
dataDir,
transportManager);
- ShadowLooper shadowBackupLooper = shadowOf(backupThread.getLooper());
- shadowBackupLooper.runToEndOfTasks();
- // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
- // above does NOT advance the handlers' clock, hence whenever a handler post messages with
- // specific time to the looper the time of those messages will be before the looper's time.
- // To fix this we advance SystemClock as well since that is from where the handlers read
- // time.
- ShadowSystemClock.setCurrentTimeMillis(shadowBackupLooper.getScheduler().getCurrentTime());
+ runToEndOfTasks(backupThread.getLooper());
return backupManagerService;
}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
index b657dd0186bf..134cfd8e16ad 100644
--- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -18,15 +18,33 @@ package com.android.server.backup.testing;
import static com.google.common.truth.Truth.assertThat;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.os.Looper;
+
import com.android.server.testing.shadows.ShadowEventLog;
import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowSystemClock;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
public class TestUtils {
+ /** Version of {@link ShadowLooper#runToEndOfTasks()} that also advances the system clock. */
+ public static void runToEndOfTasks(Looper looper) {
+ ShadowLooper shadowLooper = shadowOf(looper);
+ shadowLooper.runToEndOfTasks();
+ // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
+ // above does NOT advance the handlers' clock, hence whenever a handler post messages with
+ // specific time to the looper the time of those messages will be before the looper's time.
+ // To fix this we advance SystemClock as well since that is from where the handlers read
+ // time.
+ ShadowSystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
+ }
+
/** Reset logcat with {@link ShadowLog#reset()} before the test case. */
public static void assertLogcatAtMost(String tag, int level) {
assertThat(ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 5e7a2522f025..0dc513003fb3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -33,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
+import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -163,6 +164,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
+ @Ignore("b/80297700")
@Test
public void handleSetSystemAudioMode_setOn_orignalOff() {
mMusicMute = true;
@@ -190,6 +192,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mMusicMute).isFalse();
}
+ @Ignore("b/80297700")
@Test
public void handleSystemAudioModeRequest_turnOffByTv() {
assertThat(mMusicMute).isFalse();
@@ -215,6 +218,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mMusicMute).isTrue();
}
+ @Ignore("b/80297700")
@Test
public void onStandbyAudioSystem_currentSystemAudioControlOn() {
// Set system audio control on first
@@ -298,6 +302,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
}
+ @Ignore("b/80297700")
@Test
public void terminateSystemAudioMode_systemAudioModeOn() {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 1d378020fa4f..ef87f9d7d3bc 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -55,6 +55,7 @@ import android.view.accessibility.IAccessibilityManager;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowTestUtils.TestDisplayContent;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
@@ -123,7 +124,6 @@ public class PhoneWindowManagerTestBase {
mNavigationBar.attrs.gravity = Gravity.BOTTOM;
mPolicy.addWindow(mNavigationBar);
- mPolicy.mHasNavigationBar = true;
mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
}
@@ -245,12 +245,10 @@ public class PhoneWindowManagerTestBase {
policy[0].mAccessibilityManager = new AccessibilityManager(context,
mock(IAccessibilityManager.class), UserHandle.USER_CURRENT);
policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class);
- policy[0].mNavigationBarCanMove = true;
- policy[0].mPortraitRotation = ROTATION_0;
- policy[0].mLandscapeRotation = ROTATION_90;
- policy[0].mUpsideDownRotation = ROTATION_180;
- policy[0].mSeascapeRotation = ROTATION_270;
- policy[0].onConfigurationChanged();
+
+ final TestDisplayContent displayContent = TestDisplayContent.create(context);
+ policy[0].setDefaultDisplay(displayContent);
+ policy[0].onConfigurationChanged(displayContent);
});
return policy[0];
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index f6599dcaf87e..e8d8022e0bc0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -42,6 +42,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
/**
* Tests for the {@link AppWindowToken} class.
*
@@ -162,6 +166,9 @@ public class AppWindowTokenTests extends WindowTestsBase {
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
+ final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
+ mDisplayContent.setDisplayRotation(spiedRotation);
+
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
@@ -169,20 +176,21 @@ public class AppWindowTokenTests extends WindowTestsBase {
mToken.addWindow(appWindow);
// Set initial orientation and update.
- performRotation(Surface.ROTATION_90);
+ performRotation(spiedRotation, Surface.ROTATION_90);
appWindow.resizeReported = false;
// Update the rotation to perform 180 degree rotation and check that resize was reported.
- performRotation(Surface.ROTATION_270);
+ performRotation(spiedRotation, Surface.ROTATION_270);
assertTrue(appWindow.resizeReported);
+
appWindow.removeImmediately();
}
- private void performRotation(int rotationToReport) {
- ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = rotationToReport;
+ private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
+ doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
sWm.updateRotation(false, false);
- // Simulate animator finishing orientation change
- sWm.mRoot.mOrientationChangeComplete = true;
+ // Prevent the next rotation from being deferred by animation.
+ sWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index a2ccee46e0c9..bfc99c85d6e1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -255,16 +255,32 @@ public class ScreenDecorWindowTests {
* Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
*/
private void assertTopInsetEquals(Activity activity, int expected) throws Exception {
- waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+ waitForTopInsetEqual(activity, expected);
assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
}
+ private void waitForTopInsetEqual(Activity activity, int expected) {
+ waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+ }
+
/**
* Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
* waiting as needed.
*/
private void assertInsetGreaterOrEqual(Activity activity, int side, int expected)
throws Exception {
+ waitForInsetGreaterOrEqual(activity, side, expected);
+
+ final WindowInsets insets = getInsets(activity);
+ switch (side) {
+ case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
+ case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
+ case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
+ case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
+ }
+ }
+
+ private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) {
waitFor(() -> {
final WindowInsets insets = getInsets(activity);
switch (side) {
@@ -275,14 +291,6 @@ public class ScreenDecorWindowTests {
default: return true;
}
});
-
- final WindowInsets insets = getInsets(activity);
- switch (side) {
- case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
- case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
- case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
- case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
- }
}
/** Asserts that the first entry is greater than or equal to the second entry. */
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index e4f181be857c..735e2845bb20 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -31,7 +31,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import android.view.DisplayCutout;
import android.view.IWindow;
import android.view.IWindowManager;
@@ -73,14 +72,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
- @Override
- public boolean isDefaultOrientationForced() {
- return false;
- }
-
- @Override
- public void setInitialDisplaySize(Display display, int width, int height, int density) {
-
+ public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
}
@Override
@@ -389,22 +381,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
public void onKeyguardOccludedChangedLw(boolean occluded) {
}
- @Override
- public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
- return rotationToReport;
- }
-
- @Override
- public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation) {
- return true;
- }
-
- @Override
- public void setRotationLw(int rotation) {
-
- }
-
- @Override
public void setSafeMode(boolean safeMode) {
}
@@ -440,11 +416,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void setCurrentOrientationLw(int newOrientation) {
-
- }
-
- @Override
public boolean performHapticFeedbackLw(WindowState win, int effectId,
boolean always) {
return false;
@@ -562,12 +533,13 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void onConfigurationChanged() {
+ public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
}
@Override
- public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) {
+ public boolean shouldRotateSeamlessly(DisplayRotation displayRotation, int oldRotation,
+ int newRotation) {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 4864332b3714..431d1a7a237a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -121,13 +121,52 @@ public class WindowFrameTests extends WindowTestsBase {
mStubStack = new TaskStack(sWm, 0, null);
}
- public void assertRect(Rect rect, int left, int top, int right, int bottom) {
+ // Do not use this function directly in the tests below. Instead, use more explicit function
+ // such as assertFlame().
+ private void assertRect(Rect rect, int left, int top, int right, int bottom) {
assertEquals(left, rect.left);
assertEquals(top, rect.top);
assertEquals(right, rect.right);
assertEquals(bottom, rect.bottom);
}
+ private void assertContentInset(WindowState w, int left, int top, int right, int bottom) {
+ assertRect(w.mContentInsets, left, top, right, bottom);
+ }
+
+ private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) {
+ assertRect(w.mVisibleInsets, left, top, right, bottom);
+ }
+
+ private void assertStableInset(WindowState w, int left, int top, int right, int bottom) {
+ assertRect(w.mStableInsets, left, top, right, bottom);
+ }
+
+ private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
+ assertRect(w.getFrameLw(), left, top, right, bottom);
+ }
+
+ private void assertContentFrame(WindowState w, Rect expectedRect) {
+ assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+ expectedRect.bottom);
+ }
+
+ private void assertVisibleFrame(WindowState w, Rect expectedRect) {
+ assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+ expectedRect.bottom);
+ }
+
+ private void assertStableFrame(WindowState w, Rect expectedRect) {
+ assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+ expectedRect.bottom);
+ }
+
+ private void assertPolicyCrop(WindowStateWithTask w, int left, int top, int right, int bottom) {
+ Rect policyCrop = new Rect();
+ w.calculatePolicyCrop(policyCrop);
+ assertRect(policyCrop, left, top, right, bottom);
+ }
+
@Test
public void testLayoutInFullscreenTaskInsets() throws Exception {
Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
@@ -163,14 +202,13 @@ public class WindowFrameTests extends WindowTestsBase {
// and stable frames work the same way.
final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(),0, 0, 1000, 1000);
- assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
- assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
- assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
- // The frames remain as passed in shrunk to the window frame
- assertTrue(cf.equals(w.getContentFrameLw()));
- assertTrue(vf.equals(w.getVisibleFrameLw()));
- assertTrue(sf.equals(w.getStableFrameLw()));
+ assertFrame(w, 0, 0, 1000, 1000);
+ assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
+ assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
+ assertStableInset(w, leftStableInset, 0, rightStableInset, 0);
+ assertContentFrame(w, cf);
+ assertVisibleFrame(w, vf);
+ assertStableFrame(w, sf);
// On the other hand getFrame() doesn't extend past cf we won't get any insets
w.mAttrs.x = 100;
w.mAttrs.y = 100;
@@ -178,12 +216,12 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = 100;
w.mRequestedHeight = 100;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 100, 100, 200, 200);
- assertRect(w.mContentInsets, 0, 0, 0, 0);
+ assertFrame(w, 100, 100, 200, 200);
+ assertContentInset(w, 0, 0, 0, 0);
// In this case the frames are shrunk to the window frame.
- assertTrue(w.getFrameLw().equals(w.getContentFrameLw()));
- assertTrue(w.getFrameLw().equals(w.getVisibleFrameLw()));
- assertTrue(w.getFrameLw().equals(w.getStableFrameLw()));
+ assertContentFrame(w, w.getFrameLw());
+ assertVisibleFrame(w, w.getFrameLw());
+ assertStableFrame(w, w.getFrameLw());
}
@Test
@@ -200,7 +238,7 @@ public class WindowFrameTests extends WindowTestsBase {
// so we expect it to fill the entire available frame.
final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
+ assertFrame(w, 0, 0, 1000, 1000);
// It can select various widths and heights within the bounds.
// Strangely the window attribute width is ignored for normal windows
@@ -210,14 +248,14 @@ public class WindowFrameTests extends WindowTestsBase {
w.computeFrameLw(windowFrames);
// Explicit width and height without requested width/height
// gets us nothing.
- assertRect(w.getFrameLw(), 0, 0, 0, 0);
+ assertFrame(w, 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.computeFrameLw(windowFrames);
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
- assertRect(w.getFrameLw(), 0, 0, 300, 300);
+ assertFrame(w, 0, 0, 300, 300);
// With FLAG_SCALED though, requestedWidth/height is used to control
// the unscaled surface size, and mAttrs.width/height becomes the
@@ -228,14 +266,14 @@ public class WindowFrameTests extends WindowTestsBase {
w.mAttrs.width = 100;
w.mAttrs.height = 100;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 0, 0, 100, 100);
+ assertFrame(w, 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
+ assertFrame(w, 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
w.mAttrs.x = 300;
@@ -243,7 +281,7 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
+ assertFrame(w, 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
// to gravity.
@@ -253,16 +291,16 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 700, 0, 1000, 300);
+ assertFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 700, 700, 1000, 1000);
+ assertFrame(w, 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), 600, 600, 900, 900);
+ assertFrame(w, 600, 600, 900, 900);
}
@Test
@@ -286,24 +324,23 @@ public class WindowFrameTests extends WindowTestsBase {
w.computeFrameLw(windowFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
- assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
- assertRect(w.mContentInsets, 0, 0, 0, 0);
+ assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+ assertContentFrame(w, taskBounds);
+ assertContentInset(w, 0, 0, 0, 0);
pf.set(0, 0, logicalWidth, logicalHeight);
// We still produce insets against the containing frame the same way.
final int cfRight = logicalWidth / 2;
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
+ assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
int contentInsetRight = taskRight - cfRight;
int contentInsetBottom = taskBottom - cfBottom;
- assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
- assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
- taskBottom - contentInsetBottom);
+ assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
+ assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
+ taskBottom - contentInsetBottom));
pf.set(0, 0, logicalWidth, logicalHeight);
// However if we set temp inset bounds, the insets will be computed
@@ -314,15 +351,14 @@ public class WindowFrameTests extends WindowTestsBase {
final int insetRight = insetLeft + (taskRight - taskLeft);
final int insetBottom = insetTop + (taskBottom - taskTop);
task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw(windowFrames);
- assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
+ assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
- assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
- assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
- taskBottom - contentInsetBottom);
+ assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
+ assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
+ taskBottom - contentInsetBottom));
}
@Test
@@ -344,20 +380,16 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect vf = cf;
final Rect sf = vf;
// We use a decor content frame with insets to produce cropping.
- Rect dcf = cf;
-
- final Rect policyCrop = new Rect();
+ Rect dcf = new Rect(cf);
final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
w.computeFrameLw(windowFrames);
- w.calculatePolicyCrop(policyCrop);
- assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
+ assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
w.computeFrameLw(windowFrames);
- w.calculatePolicyCrop(policyCrop);
- assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
+ assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
// Now we set up a window which doesn't fill the entire decor frame.
// Normally it would be cropped to it's frame but in the case of docked resizing
@@ -372,16 +404,14 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedHeight = logicalHeight / 2;
w.computeFrameLw(windowFrames);
- w.calculatePolicyCrop(policyCrop);
// Normally the crop is shrunk from the decor frame
// to the computed window frame.
- assertRect(policyCrop, 0, 0, logicalWidth / 2, logicalHeight / 2);
+ assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
w.mDockedResizingForTest = true;
- w.calculatePolicyCrop(policyCrop);
// But if we are docked resizing it won't be, however we will still be
// shrunk to the decor frame and the display.
- assertRect(policyCrop, 0, 0,
+ assertPolicyCrop(w, 0, 0,
Math.min(pf.width(), displayInfo.logicalWidth),
Math.min(pf.height(), displayInfo.logicalHeight));
}
@@ -409,9 +439,9 @@ public class WindowFrameTests extends WindowTestsBase {
w.computeFrameLw(windowFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
- assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
- assertRect(w.mContentInsets, 0, 0, 0, 0);
+ assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+ assertContentFrame(w, taskBounds);
+ assertContentInset(w, 0, 0, 0, 0);
// Now simulate switch to fullscreen for letterboxed app.
final int xInset = logicalWidth / 10;
@@ -422,12 +452,11 @@ public class WindowFrameTests extends WindowTestsBase {
w.mAppToken.onOverrideConfigurationChanged(config);
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
-
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw(windowFrames);
- assertEquals(cf, w.getFrameLw());
- assertEquals(cf, w.getContentFrameLw());
- assertRect(w.mContentInsets, 0, 0, 0, 0);
+ assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+ assertContentFrame(w, cf);
+ assertContentInset(w, 0, 0, 0, 0);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index a48a3b9f8022..a1b164065696 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -22,8 +22,10 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.view.Display;
import android.view.IApplicationToken;
import android.view.IWindow;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
@@ -60,6 +62,37 @@ public class WindowTestUtils {
return service;
}
+ /** An extension of {@link DisplayContent} to gain package scoped access. */
+ public static class TestDisplayContent extends DisplayContent {
+
+ private TestDisplayContent(Display display, WindowManagerService service,
+ WallpaperController wallpaperController, DisplayWindowController controller) {
+ super(display, service, wallpaperController, controller);
+ }
+
+ /** Create a mocked default {@link DisplayContent}. */
+ public static TestDisplayContent create(Context context) {
+ final TestDisplayContent displayContent = mock(TestDisplayContent.class);
+ displayContent.isDefaultDisplay = true;
+
+ final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+ when(displayPolicy.navigationBarCanMove()).thenReturn(true);
+ when(displayPolicy.hasNavigationBar()).thenReturn(true);
+
+ final DisplayRotation displayRotation = new DisplayRotation(
+ mock(WindowManagerService.class), displayContent, displayPolicy,
+ context, new Object());
+ displayRotation.mPortraitRotation = Surface.ROTATION_0;
+ displayRotation.mLandscapeRotation = Surface.ROTATION_90;
+ displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
+ displayRotation.mSeascapeRotation = Surface.ROTATION_270;
+
+ when(displayContent.getDisplayRotation()).thenReturn(displayRotation);
+
+ return displayContent;
+ }
+ }
+
/**
* Creates a mock instance of {@link StackWindowController}.
*/
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 742ad65f159a..f255d49e198f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -95,6 +95,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertEquals(getUserSentiment(i), ranking.getUserSentiment());
assertEquals(getHidden(i), ranking.isSuspended());
assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
+ assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
}
}
@@ -112,6 +113,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
Bundle userSentiment = new Bundle();
Bundle mHidden = new Bundle();
Bundle smartActions = new Bundle();
+ Bundle smartReplies = new Bundle();
for (int i = 0; i < mKeys.length; i++) {
String key = mKeys[i];
@@ -130,12 +132,13 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
userSentiment.putInt(key, getUserSentiment(i));
mHidden.putBoolean(key, getHidden(i));
smartActions.putParcelableArrayList(key, getSmartActions(key, i));
+ smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
}
NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
interceptedKeys.toArray(new String[0]), visibilityOverrides,
suppressedVisualEffects, importance, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
- smartActions);
+ smartActions, smartReplies);
return update;
}
@@ -216,6 +219,14 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
return actions;
}
+ private ArrayList<CharSequence> getSmartReplies(String key, int index) {
+ ArrayList<CharSequence> choices = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ choices.add("choice_" + key + "_" + i);
+ }
+ return choices;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 66285c3702f8..0cbb1aa8ee48 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -111,6 +111,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
+import android.util.Log;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
@@ -199,6 +200,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
int countSystemChecks = 0;
+ boolean isSystemUid = true;
public TestableNotificationManagerService(Context context) {
super(context);
@@ -207,13 +209,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Override
protected boolean isCallingUidSystem() {
countSystemChecks++;
- return true;
+ return isSystemUid;
}
@Override
protected boolean isCallerSystemOrPhone() {
countSystemChecks++;
- return true;
+ return isSystemUid;
}
@Override
@@ -651,6 +653,79 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertNull(mService.getNotificationRecord(sbn.getKey()));
}
+ /**
+ * Confirm the system user on automotive devices can use car categories
+ */
+ @Test
+ public void testEnqueuedRestrictedNotifications_asSystem() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
+ .thenReturn(true);
+ List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
+ Notification.CATEGORY_CAR_WARNING,
+ Notification.CATEGORY_CAR_INFORMATION);
+ int id = 0;
+ for (String category: categories) {
+ final StatusBarNotification sbn =
+ generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+ sbn.getNotification().category = category;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ }
+ waitForIdle();
+ assertEquals(categories.size(), mBinderService.getActiveNotifications(PKG).length);
+ }
+
+
+ /**
+ * Confirm restricted notification categories only apply to automotive.
+ */
+ @Test
+ public void testEnqueuedRestrictedNotifications_notAutomotive() throws Exception {
+ mService.isSystemUid = false;
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
+ .thenReturn(false);
+ List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
+ Notification.CATEGORY_CAR_WARNING,
+ Notification.CATEGORY_CAR_INFORMATION);
+ int id = 0;
+ for (String category: categories) {
+ final StatusBarNotification sbn =
+ generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+ sbn.getNotification().category = category;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ }
+ waitForIdle();
+ assertEquals(categories.size(), mBinderService.getActiveNotifications(PKG).length);
+ }
+
+ /**
+ * Confirm if a non-system user tries to use the car categories on a automotive device that
+ * they will get a security exception
+ */
+ @Test
+ public void testEnqueuedRestrictedNotifications_badUser() throws Exception {
+ mService.isSystemUid = false;
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0))
+ .thenReturn(true);
+ List<String> categories = Arrays.asList(Notification.CATEGORY_CAR_EMERGENCY,
+ Notification.CATEGORY_CAR_WARNING,
+ Notification.CATEGORY_CAR_INFORMATION);
+ for (String category: categories) {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().category = category;
+ try {
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ fail("Calls from non system apps should not allow use of restricted categories");
+ } catch (SecurityException e) {
+ // pass
+ }
+ }
+ waitForIdle();
+ assertEquals(0, mBinderService.getActiveNotifications(PKG).length);
+ }
+
@Test
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 22958d4aed5e..62fc9c4234c7 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -843,6 +843,7 @@ public abstract class Connection extends Conferenceable {
public void onRemoteRttRequest(Connection c) {}
/** @hide */
public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) {}
+ public void onConnectionTimeReset(Connection c) {}
}
/**
@@ -2372,6 +2373,16 @@ public abstract class Connection extends Conferenceable {
}
/**
+ * @hide
+ * Resets the cdma connection time.
+ */
+ public final void resetConnectionTime() {
+ for (Listener l : mListeners) {
+ l.onConnectionTimeReset(this);
+ }
+ }
+
+ /**
* Returns the connections or conferences with which this connection can be conferenced.
*/
public final List<Conferenceable> getConferenceables() {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 2291090f9daf..61adcdd9a4d4 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1474,6 +1474,13 @@ public abstract class ConnectionService extends Service {
mAdapter.onPhoneAccountChanged(id, pHandle);
}
}
+
+ public void onConnectionTimeReset(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.resetConnectionTime(id);
+ }
+ }
};
/** {@inheritDoc} */
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 0d319bbc1d2a..520e7eda6f69 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -255,6 +255,18 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
/**
+ * Resets the cdma connection time.
+ */
+ void resetConnectionTime(String callId) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.resetConnectionTime(callId, Log.getExternalSession());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
* Indicates that the call no longer exists. Can be used with either a call or a conference
* call.
*
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3e1bf7790304..78d65e643abc 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -610,6 +610,11 @@ final class ConnectionServiceAdapterServant {
public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_RELEASED).sendToTarget();
}
+
+ @Override
+ public void resetConnectionTime(String callId, Session.Info sessionInfo) {
+ // Do nothing
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bb4b483de326..9821dcbce85e 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -466,6 +466,11 @@ final class RemoteConnectionService {
Log.w(this, "onRemoteRttRequest called on a remote conference");
}
}
+
+ @Override
+ public void resetConnectionTime(String callId, Session.Info sessionInfo) {
+ // Do nothing
+ }
};
private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index be474bd467ca..0157a5863363 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -121,4 +121,6 @@ oneway interface IConnectionServiceAdapter {
in Session.Info sessionInfo);
void onConnectionServiceFocusReleased(in Session.Info sessionInfo);
+
+ void resetConnectionTime(String callIdi, in Session.Info sessionInfo);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9e23c5cbbecb..b2eb5e03db31 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1553,7 +1553,8 @@ public class TelephonyManager {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
- return telephony.getNeighboringCellInfo(mContext.getOpPackageName());
+ return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
+ mContext.getApplicationInfo().targetSdkVersion);
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
diff --git a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java b/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
index 56969a8fb612..a9847ba61cf6 100644
--- a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.common;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -73,6 +74,7 @@ public class CapInfo implements Parcelable {
/**
* Constructor for the CapInfo class.
*/
+ @UnsupportedAppUsage
public CapInfo() {
};
@@ -80,6 +82,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether IM is supported.
*/
+ @UnsupportedAppUsage
public boolean isImSupported() {
return mImSupported;
}
@@ -87,6 +90,7 @@ public class CapInfo implements Parcelable {
/**
* Sets IM as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setImSupported(boolean imSupported) {
this.mImSupported = imSupported;
}
@@ -94,6 +98,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether FT Thumbnail is supported.
*/
+ @UnsupportedAppUsage
public boolean isFtThumbSupported() {
return mFtThumbSupported;
}
@@ -101,6 +106,7 @@ public class CapInfo implements Parcelable {
/**
* Sets FT thumbnail as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setFtThumbSupported(boolean ftThumbSupported) {
this.mFtThumbSupported = ftThumbSupported;
}
@@ -110,6 +116,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether FT Store and Forward is supported
*/
+ @UnsupportedAppUsage
public boolean isFtSnFSupported() {
return mFtSnFSupported;
}
@@ -117,6 +124,7 @@ public class CapInfo implements Parcelable {
/**
* Sets FT Store and Forward as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setFtSnFSupported(boolean ftSnFSupported) {
this.mFtSnFSupported = ftSnFSupported;
}
@@ -124,6 +132,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether File transfer HTTP is supported.
*/
+ @UnsupportedAppUsage
public boolean isFtHttpSupported() {
return mFtHttpSupported;
}
@@ -131,6 +140,7 @@ public class CapInfo implements Parcelable {
/**
* Sets File transfer HTTP as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setFtHttpSupported(boolean ftHttpSupported) {
this.mFtHttpSupported = ftHttpSupported;
}
@@ -138,6 +148,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether FT is supported.
*/
+ @UnsupportedAppUsage
public boolean isFtSupported() {
return mFtSupported;
}
@@ -145,6 +156,7 @@ public class CapInfo implements Parcelable {
/**
* Sets FT as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setFtSupported(boolean ftSupported) {
this.mFtSupported = ftSupported;
}
@@ -152,6 +164,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether IS is supported.
*/
+ @UnsupportedAppUsage
public boolean isIsSupported() {
return mIsSupported;
}
@@ -159,6 +172,7 @@ public class CapInfo implements Parcelable {
/**
* Sets IS as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setIsSupported(boolean isSupported) {
this.mIsSupported = isSupported;
}
@@ -166,6 +180,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether video sharing is supported during a CS call.
*/
+ @UnsupportedAppUsage
public boolean isVsDuringCSSupported() {
return mVsDuringCSSupported;
}
@@ -174,6 +189,7 @@ public class CapInfo implements Parcelable {
* Sets video sharing as supported or not supported during a CS
* call.
*/
+ @UnsupportedAppUsage
public void setVsDuringCSSupported(boolean vsDuringCSSupported) {
this.mVsDuringCSSupported = vsDuringCSSupported;
}
@@ -182,6 +198,7 @@ public class CapInfo implements Parcelable {
* Checks whether video sharing outside a voice call is
* supported.
*/
+ @UnsupportedAppUsage
public boolean isVsSupported() {
return mVsSupported;
}
@@ -189,6 +206,7 @@ public class CapInfo implements Parcelable {
/**
* Sets video sharing as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setVsSupported(boolean vsSupported) {
this.mVsSupported = vsSupported;
}
@@ -196,6 +214,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether social presence is supported.
*/
+ @UnsupportedAppUsage
public boolean isSpSupported() {
return mSpSupported;
}
@@ -203,6 +222,7 @@ public class CapInfo implements Parcelable {
/**
* Sets social presence as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setSpSupported(boolean spSupported) {
this.mSpSupported = spSupported;
}
@@ -211,6 +231,7 @@ public class CapInfo implements Parcelable {
* Checks whether capability discovery via presence is
* supported.
*/
+ @UnsupportedAppUsage
public boolean isCdViaPresenceSupported() {
return mCdViaPresenceSupported;
}
@@ -219,6 +240,7 @@ public class CapInfo implements Parcelable {
* Sets capability discovery via presence as supported or not
* supported.
*/
+ @UnsupportedAppUsage
public void setCdViaPresenceSupported(boolean cdViaPresenceSupported) {
this.mCdViaPresenceSupported = cdViaPresenceSupported;
}
@@ -226,6 +248,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether IP voice call is supported.
*/
+ @UnsupportedAppUsage
public boolean isIpVoiceSupported() {
return mIpVoiceSupported;
}
@@ -233,6 +256,7 @@ public class CapInfo implements Parcelable {
/**
* Sets IP voice call as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setIpVoiceSupported(boolean ipVoiceSupported) {
this.mIpVoiceSupported = ipVoiceSupported;
}
@@ -240,6 +264,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether IP video call is supported.
*/
+ @UnsupportedAppUsage
public boolean isIpVideoSupported() {
return mIpVideoSupported;
}
@@ -247,6 +272,7 @@ public class CapInfo implements Parcelable {
/**
* Sets IP video call as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setIpVideoSupported(boolean ipVideoSupported) {
this.mIpVideoSupported = ipVideoSupported;
}
@@ -255,6 +281,7 @@ public class CapInfo implements Parcelable {
* Checks whether Geo location Pull using File Transfer is
* supported.
*/
+ @UnsupportedAppUsage
public boolean isGeoPullFtSupported() {
return mGeoPullFtSupported;
}
@@ -263,6 +290,7 @@ public class CapInfo implements Parcelable {
* Sets Geo location Pull using File Transfer as supported or
* not supported.
*/
+ @UnsupportedAppUsage
public void setGeoPullFtSupported(boolean geoPullFtSupported) {
this.mGeoPullFtSupported = geoPullFtSupported;
}
@@ -270,6 +298,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether Geo Pull is supported.
*/
+ @UnsupportedAppUsage
public boolean isGeoPullSupported() {
return mGeoPullSupported;
}
@@ -277,6 +306,7 @@ public class CapInfo implements Parcelable {
/**
* Sets Geo Pull as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setGeoPullSupported(boolean geoPullSupported) {
this.mGeoPullSupported = geoPullSupported;
}
@@ -284,6 +314,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether Geo Push is supported.
*/
+ @UnsupportedAppUsage
public boolean isGeoPushSupported() {
return mGeoPushSupported;
}
@@ -291,6 +322,7 @@ public class CapInfo implements Parcelable {
/**
* Sets Geo Push as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setGeoPushSupported(boolean geoPushSupported) {
this.mGeoPushSupported = geoPushSupported;
}
@@ -298,6 +330,7 @@ public class CapInfo implements Parcelable {
/**
* Checks whether short messaging is supported.
*/
+ @UnsupportedAppUsage
public boolean isSmSupported() {
return mSmSupported;
}
@@ -305,6 +338,7 @@ public class CapInfo implements Parcelable {
/**
* Sets short messaging as supported or not supported.
*/
+ @UnsupportedAppUsage
public void setSmSupported(boolean smSupported) {
this.mSmSupported = smSupported;
}
@@ -312,18 +346,22 @@ public class CapInfo implements Parcelable {
/**
* Checks whether store/forward and group chat are supported.
*/
+ @UnsupportedAppUsage
public boolean isFullSnFGroupChatSupported() {
return mFullSnFGroupChatSupported;
}
+ @UnsupportedAppUsage
public boolean isRcsIpVoiceCallSupported() {
return mRcsIpVoiceCallSupported;
}
+ @UnsupportedAppUsage
public boolean isRcsIpVideoCallSupported() {
return mRcsIpVideoCallSupported;
}
+ @UnsupportedAppUsage
public boolean isRcsIpVideoOnlyCallSupported() {
return mRcsIpVideoOnlyCallSupported;
}
@@ -331,16 +369,20 @@ public class CapInfo implements Parcelable {
/**
* Sets store/forward and group chat supported or not supported.
*/
+ @UnsupportedAppUsage
public void setFullSnFGroupChatSupported(boolean fullSnFGroupChatSupported) {
this.mFullSnFGroupChatSupported = fullSnFGroupChatSupported;
}
+ @UnsupportedAppUsage
public void setRcsIpVoiceCallSupported(boolean rcsIpVoiceCallSupported) {
this.mRcsIpVoiceCallSupported = rcsIpVoiceCallSupported;
}
+ @UnsupportedAppUsage
public void setRcsIpVideoCallSupported(boolean rcsIpVideoCallSupported) {
this.mRcsIpVideoCallSupported = rcsIpVideoCallSupported;
}
+ @UnsupportedAppUsage
public void setRcsIpVideoOnlyCallSupported(boolean rcsIpVideoOnlyCallSupported) {
this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
}
@@ -351,17 +393,20 @@ public class CapInfo implements Parcelable {
}
/** Sets the list of supported extensions. */
+ @UnsupportedAppUsage
public void setExts(String[] exts) {
this.mExts = exts;
}
/** Gets the time stamp for when to query again. */
+ @UnsupportedAppUsage
public long getCapTimestamp() {
return mCapTimestamp;
}
/** Sets the time stamp for when to query again. */
+ @UnsupportedAppUsage
public void setCapTimestamp(long capTimestamp) {
this.mCapTimestamp = capTimestamp;
}
diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
index ad9b669e5f2b..3921cfbbfce7 100644
--- a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
+++ b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.common;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -71,12 +72,14 @@ public class StatusCode implements Parcelable {
* Constructor for the StatusCode class.
* @hide
*/
+ @UnsupportedAppUsage
public StatusCode() {}
/**
* Gets the status code.
* @hide
*/
+ @UnsupportedAppUsage
public int getStatusCode() {
return mStatusCode;
}
@@ -85,6 +88,7 @@ public class StatusCode implements Parcelable {
* Sets the status code.
* @hide
*/
+ @UnsupportedAppUsage
public void setStatusCode(int nStatusCode) {
this.mStatusCode = nStatusCode;
}
diff --git a/telephony/java/com/android/ims/internal/uce/common/UceLong.java b/telephony/java/com/android/ims/internal/uce/common/UceLong.java
index fd07fe8d2210..720789918e45 100644
--- a/telephony/java/com/android/ims/internal/uce/common/UceLong.java
+++ b/telephony/java/com/android/ims/internal/uce/common/UceLong.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.common;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -32,6 +33,7 @@ public class UceLong implements Parcelable {
* Constructor for the UceLong class.
* @hide
*/
+ @UnsupportedAppUsage
public UceLong() {
};
@@ -39,6 +41,7 @@ public class UceLong implements Parcelable {
* Gets the long value.
* @hide
*/
+ @UnsupportedAppUsage
public long getUceLong() {
return mUceLong;
}
@@ -47,6 +50,7 @@ public class UceLong implements Parcelable {
* Sets the long value.
* @hide
*/
+ @UnsupportedAppUsage
public void setUceLong(long uceLong) {
this.mUceLong = uceLong;
}
@@ -54,6 +58,7 @@ public class UceLong implements Parcelable {
/** Get the client ID as integer value.
* @hide
*/
+ @UnsupportedAppUsage
public int getClientId() {
return mClientId;
}
@@ -62,6 +67,7 @@ public class UceLong implements Parcelable {
* Set the client ID as integer value.
* @hide
*/
+ @UnsupportedAppUsage
public void setClientId(int nClientId) {
this.mClientId = nClientId;
}
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
index c570f495a4ac..bcb9f2d6284d 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
@@ -15,6 +15,7 @@
*/
package com.android.ims.internal.uce.options;
+import android.annotation.UnsupportedAppUsage;
import com.android.ims.internal.uce.common.CapInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,10 +31,12 @@ public class OptionsCapInfo implements Parcelable {
return new OptionsCapInfo();
}
+ @UnsupportedAppUsage
public String getSdp() {
return mSdp;
}
+ @UnsupportedAppUsage
public void setSdp(String sdp) {
this.mSdp = sdp;
}
@@ -41,16 +44,19 @@ public class OptionsCapInfo implements Parcelable {
/**
* Constructor for the OptionsCapInfo class.
*/
+ @UnsupportedAppUsage
public OptionsCapInfo() {
mCapInfo = new CapInfo();
};
+ @UnsupportedAppUsage
public CapInfo getCapInfo() {
return mCapInfo;
}
/**
* Sets the CapInfo
*/
+ @UnsupportedAppUsage
public void setCapInfo(CapInfo capInfo) {
this.mCapInfo = capInfo;
}
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
index 35f769cdc072..14c64ac105bb 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
@@ -17,6 +17,7 @@
package com.android.ims.internal.uce.options;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -55,6 +56,7 @@ public class OptionsCmdId implements Parcelable {
* Sets the command ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setCmdId(int nCmdId) {
this.mCmdId = nCmdId;
}
@@ -63,6 +65,7 @@ public class OptionsCmdId implements Parcelable {
* Constructor for the OptionsCDCmdId class.
* @hide
*/
+ @UnsupportedAppUsage
public OptionsCmdId(){};
/** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
index dab191cfb364..4af3e6e3cea4 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
@@ -19,6 +19,7 @@ package com.android.ims.internal.uce.options;
import com.android.ims.internal.uce.common.StatusCode;
import com.android.ims.internal.uce.common.CapInfo;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +42,7 @@ public class OptionsCmdStatus implements Parcelable {
* Sets the command ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setCmdId(OptionsCmdId cmdId) {
this.mCmdId = cmdId;
}
@@ -56,6 +58,7 @@ public class OptionsCmdStatus implements Parcelable {
/**
Sets the user data.
@hide */
+ @UnsupportedAppUsage
public void setUserData(int userData) {
this.mUserData = userData;
}
@@ -72,6 +75,7 @@ public class OptionsCmdStatus implements Parcelable {
* Sets the status code.
* @hide
*/
+ @UnsupportedAppUsage
public void setStatus(StatusCode status) {
this.mStatus = status;
}
@@ -80,6 +84,7 @@ public class OptionsCmdStatus implements Parcelable {
* Constructor for the OptionsCmdStatus class.
* @hide
*/
+ @UnsupportedAppUsage
public OptionsCmdStatus() {
mStatus = new StatusCode();
mCapInfo = new CapInfo();
@@ -96,6 +101,7 @@ public class OptionsCmdStatus implements Parcelable {
* Sets the CapInfo
* @hide
*/
+ @UnsupportedAppUsage
public void setCapInfo(CapInfo capInfo) {
this.mCapInfo = capInfo;
}
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index 0b9dd21ffe3f..c5f333d35ba5 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.options;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +42,7 @@ public class OptionsSipResponse implements Parcelable {
* Sets the Options command ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setCmdId(OptionsCmdId cmdId) {
this.mCmdId = cmdId;
}
@@ -57,6 +59,7 @@ public class OptionsSipResponse implements Parcelable {
* Sets the request ID
* @hide
*/
+ @UnsupportedAppUsage
public void setRequestId(int requestId) {
this.mRequestId = requestId;
}
@@ -73,6 +76,7 @@ public class OptionsSipResponse implements Parcelable {
* Sets the SIP response code.
* @hide
*/
+ @UnsupportedAppUsage
public void setSipResponseCode(int sipResponseCode) {
this.mSipResponseCode = sipResponseCode;
}
@@ -89,6 +93,7 @@ public class OptionsSipResponse implements Parcelable {
* Sets the SIP response code reason phrase.
* @hide
*/
+ @UnsupportedAppUsage
public void setReasonPhrase(String reasonPhrase) {
this.mReasonPhrase = reasonPhrase;
}
@@ -105,6 +110,7 @@ public class OptionsSipResponse implements Parcelable {
* Sets the SIP retryAfter sec value
* @hide
*/
+ @UnsupportedAppUsage
public void setRetryAfter(int retryAfter) {
this.mRetryAfter = retryAfter;
}
@@ -113,6 +119,7 @@ public class OptionsSipResponse implements Parcelable {
* Constructor for the OptionsSipResponse class.
* @hide
*/
+ @UnsupportedAppUsage
public OptionsSipResponse() {
mCmdId = new OptionsCmdId();
};
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
index 60fc2260304f..745df5b71aa7 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
@@ -18,6 +18,7 @@ package com.android.ims.internal.uce.presence;
import com.android.ims.internal.uce.common.CapInfo;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,12 +27,14 @@ import android.os.Parcelable;
public class PresCapInfo implements Parcelable {
private CapInfo mCapInfo;
+ @UnsupportedAppUsage
private String mContactUri = "";
/**
* Gets the UCE capability information.
* @hide
*/
+ @UnsupportedAppUsage
public CapInfo getCapInfo() {
return mCapInfo;
}
@@ -48,6 +51,7 @@ public class PresCapInfo implements Parcelable {
* Gets the contact URI.
* @hide
*/
+ @UnsupportedAppUsage
public String getContactUri() {
return mContactUri;
}
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java b/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
index 395f3e87fc29..41020ec5b49b 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -57,6 +58,7 @@ public class PresCmdId implements Parcelable {
* Sets the command ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setCmdId(int nCmdId) {
this.mCmdId = nCmdId;
}
@@ -66,6 +68,7 @@ public class PresCmdId implements Parcelable {
* Constructor for the PresCmdId class.
* @hide
*/
+ @UnsupportedAppUsage
public PresCmdId(){};
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
index a5b498bd9105..ff8069c569ff 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
@@ -18,6 +18,7 @@ package com.android.ims.internal.uce.presence;
import com.android.ims.internal.uce.common.StatusCode;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,6 +43,7 @@ public class PresCmdStatus implements Parcelable{
* Sets the command ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setCmdId(PresCmdId cmdId) {
this.mCmdId = cmdId;
}
@@ -58,6 +60,7 @@ public class PresCmdStatus implements Parcelable{
* Sets the user data.
* @hide
*/
+ @UnsupportedAppUsage
public void setUserData(int userData) {
this.mUserData = userData;
}
@@ -73,6 +76,7 @@ public class PresCmdStatus implements Parcelable{
* Sets the status code.
* @hide
*/
+ @UnsupportedAppUsage
public void setStatus(StatusCode status) {
this.mStatus = status;
}
@@ -89,6 +93,7 @@ public class PresCmdStatus implements Parcelable{
* Sets the request ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setRequestId(int requestId) {
this.mRequestId = requestId;
}
@@ -97,6 +102,7 @@ public class PresCmdStatus implements Parcelable{
* Constructor for the PresCmdStatus class.
* @hide
*/
+ @UnsupportedAppUsage
public PresCmdStatus() {
mStatus = new StatusCode();
};
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index 3e8531a73421..87193e36ce90 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -65,6 +66,7 @@ public class PresPublishTriggerType implements Parcelable {
* Sets the publish trigger type.
* @hide
*/
+ @UnsupportedAppUsage
public void setPublishTrigeerType(int nPublishTriggerType) {
this.mPublishTriggerType = nPublishTriggerType;
}
@@ -74,6 +76,7 @@ public class PresPublishTriggerType implements Parcelable {
* Constructor for the PresPublishTriggerType class.
* @hide
*/
+ @UnsupportedAppUsage
public PresPublishTriggerType(){};
/** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
index a073a234d1ec..237c9994451a 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,6 +39,7 @@ public class PresResInfo implements Parcelable {
* Sets the Presence service resource instance information.
* @hide
*/
+ @UnsupportedAppUsage
public void setInstanceInfo(PresResInstanceInfo instanceInfo) {
this.mInstanceInfo = instanceInfo;
}
@@ -54,6 +56,7 @@ public class PresResInfo implements Parcelable {
* Sets the resource URI.
* @hide
*/
+ @UnsupportedAppUsage
public void setResUri(String resUri) {
this.mResUri = resUri;
}
@@ -70,6 +73,7 @@ public class PresResInfo implements Parcelable {
* Sets the display name.
* @hide
*/
+ @UnsupportedAppUsage
public void setDisplayName(String displayName) {
this.mDisplayName = displayName;
}
@@ -79,6 +83,7 @@ public class PresResInfo implements Parcelable {
* Constructor for the PresResInstanceInfo class.
* @hide
*/
+ @UnsupportedAppUsage
public PresResInfo() {
mInstanceInfo = new PresResInstanceInfo();
};
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 430cff196d33..29699ea6c802 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Arrays;
@@ -59,6 +60,7 @@ public class PresResInstanceInfo implements Parcelable{
* Sets the resource instance state.
* @hide
*/
+ @UnsupportedAppUsage
public void setResInstanceState(int nResInstanceState) {
this.mResInstanceState = nResInstanceState;
}
@@ -75,6 +77,7 @@ public class PresResInstanceInfo implements Parcelable{
* Sets the resource ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setResId(String resourceId) {
this.mId = resourceId;
}
@@ -93,6 +96,7 @@ public class PresResInstanceInfo implements Parcelable{
* code.
* @hide
*/
+ @UnsupportedAppUsage
public void setReason(String reason) {
this.mReason = reason;
}
@@ -109,6 +113,7 @@ public class PresResInstanceInfo implements Parcelable{
* Sets the entity URI.
* @hide
*/
+ @UnsupportedAppUsage
public void setPresentityUri(String presentityUri) {
this.mPresentityUri = presentityUri;
}
@@ -125,6 +130,7 @@ public class PresResInstanceInfo implements Parcelable{
* Sets the tuple information.
* @hide
*/
+ @UnsupportedAppUsage
public void setTupleInfo(PresTupleInfo[] tupleInfo) {
this.mTupleInfoArray = new PresTupleInfo[tupleInfo.length];
this.mTupleInfoArray = tupleInfo;
@@ -135,6 +141,7 @@ public class PresResInstanceInfo implements Parcelable{
* Constructor for the PresResInstanceInfo class.
* @hide
*/
+ @UnsupportedAppUsage
public PresResInstanceInfo(){
};
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
index 987dd775f86a..ab46e4b6295e 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,6 +61,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the URI.
* @hide
*/
+ @UnsupportedAppUsage
public void setUri(String uri) {
this.mUri = uri;
}
@@ -76,6 +78,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the version.
* @hide
*/
+ @UnsupportedAppUsage
public void setVersion(int version) {
this.mVersion = version;
}
@@ -92,6 +95,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the RLMI state.
* @hide
*/
+ @UnsupportedAppUsage
public void setFullState(boolean fullState) {
this.mFullState = fullState;
}
@@ -108,6 +112,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the RLMI list name.
* @hide
*/
+ @UnsupportedAppUsage
public void setListName(String listName) {
this.mListName = listName;
}
@@ -124,6 +129,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the subscription request ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setRequestId(int requestId) {
this.mRequestId = requestId;
}
@@ -140,6 +146,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the presence subscription state.
* @hide
*/
+ @UnsupportedAppUsage
public void setPresSubscriptionState(PresSubscriptionState presSubscriptionState) {
this.mPresSubscriptionState = presSubscriptionState;
}
@@ -156,6 +163,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the presence subscription expiration time.
* @hide
*/
+ @UnsupportedAppUsage
public void setSubscriptionExpireTime(int subscriptionExpireTime) {
this.mSubscriptionExpireTime = subscriptionExpireTime;
}
@@ -172,6 +180,7 @@ public class PresRlmiInfo implements Parcelable {
* Sets the presence subscription terminated reason.
* @hide
*/
+ @UnsupportedAppUsage
public void setSubscriptionTerminatedReason(String subscriptionTerminatedReason) {
this.mSubscriptionTerminatedReason = subscriptionTerminatedReason;
}
@@ -180,6 +189,7 @@ public class PresRlmiInfo implements Parcelable {
* Constructor for the PresTupleInfo class.
* @hide
*/
+ @UnsupportedAppUsage
public PresRlmiInfo(){};
/** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
index f7b7264fe528..83ba722fe2f1 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,6 +46,7 @@ public class PresServiceInfo implements Parcelable {
* Gets the media type.
* @hide
*/
+ @UnsupportedAppUsage
public int getMediaType() {
return mMediaCap;
}
@@ -61,6 +63,7 @@ public class PresServiceInfo implements Parcelable {
* Gets the service ID.
* @hide
*/
+ @UnsupportedAppUsage
public String getServiceId() {
return mServiceID;
}
@@ -76,6 +79,7 @@ public class PresServiceInfo implements Parcelable {
* Gets the service description.
* @hide
*/
+ @UnsupportedAppUsage
public String getServiceDesc() {
return mServiceDesc;
}
@@ -92,6 +96,7 @@ public class PresServiceInfo implements Parcelable {
* Gets the service version.
* @hide
*/
+ @UnsupportedAppUsage
public String getServiceVer() {
return mServiceVer;
}
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 456b443a392a..5e4259297d74 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@ public class PresSipResponse implements Parcelable {
* Gets the Presence command ID.
* @hide
*/
+ @UnsupportedAppUsage
public PresCmdId getCmdId() {
return mCmdId;
}
@@ -40,6 +42,7 @@ public class PresSipResponse implements Parcelable {
* Sets the Presence command ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setCmdId(PresCmdId cmdId) {
this.mCmdId = cmdId;
}
@@ -48,6 +51,7 @@ public class PresSipResponse implements Parcelable {
* Gets the request ID.
* @hide
*/
+ @UnsupportedAppUsage
public int getRequestId() {
return mRequestId;
}
@@ -56,6 +60,7 @@ public class PresSipResponse implements Parcelable {
* Sets the request ID.
* @hide
*/
+ @UnsupportedAppUsage
public void setRequestId(int requestId) {
this.mRequestId = requestId;
}
@@ -64,6 +69,7 @@ public class PresSipResponse implements Parcelable {
* Gets the SIP response code.
* @hide
*/
+ @UnsupportedAppUsage
public int getSipResponseCode() {
return mSipResponseCode;
}
@@ -72,6 +78,7 @@ public class PresSipResponse implements Parcelable {
* Sets the SIP response code.
* @hide
*/
+ @UnsupportedAppUsage
public void setSipResponseCode(int sipResponseCode) {
this.mSipResponseCode = sipResponseCode;
}
@@ -82,6 +89,7 @@ public class PresSipResponse implements Parcelable {
* code.
* @hide
*/
+ @UnsupportedAppUsage
public String getReasonPhrase() {
return mReasonPhrase;
}
@@ -90,6 +98,7 @@ public class PresSipResponse implements Parcelable {
* Sets the SIP response code reason phrase.
* @hide
*/
+ @UnsupportedAppUsage
public void setReasonPhrase(String reasonPhrase) {
this.mReasonPhrase = reasonPhrase;
}
@@ -98,6 +107,7 @@ public class PresSipResponse implements Parcelable {
* Gets the SIP retryAfter sec value.
* @hide
*/
+ @UnsupportedAppUsage
public int getRetryAfter() {
return mRetryAfter;
}
@@ -106,6 +116,7 @@ public class PresSipResponse implements Parcelable {
* Sets the SIP retryAfter sec value
* @hide
*/
+ @UnsupportedAppUsage
public void setRetryAfter(int retryAfter) {
this.mRetryAfter = retryAfter;
}
@@ -114,6 +125,7 @@ public class PresSipResponse implements Parcelable {
* Constructor for the PresSipResponse class.
* @hide
*/
+ @UnsupportedAppUsage
public PresSipResponse(){};
/** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java b/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
index 872bc23ee9ab..bee928c3280a 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -77,6 +78,7 @@ public class PresSubscriptionState implements Parcelable {
* Constructor for the PresSubscriptionState class.
* @hide
*/
+ @UnsupportedAppUsage
public PresSubscriptionState() { };
/**
@@ -92,6 +94,7 @@ public class PresSubscriptionState implements Parcelable {
* Sets the Presence subscription state.
* @hide
*/
+ @UnsupportedAppUsage
public void setPresSubscriptionState(int nPresSubscriptionState) {
this.mPresSubscriptionState = nPresSubscriptionState;
}
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index e1867c55836b..7a47786b5af6 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -16,6 +16,7 @@
package com.android.ims.internal.uce.presence;
+import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,6 +40,7 @@ public class PresTupleInfo implements Parcelable {
* Sets the feature tag.
* @hide
*/
+ @UnsupportedAppUsage
public void setFeatureTag(String featureTag) {
this.mFeatureTag = featureTag;
}
@@ -54,6 +56,7 @@ public class PresTupleInfo implements Parcelable {
* Sets the contact URI.
* @hide
*/
+ @UnsupportedAppUsage
public void setContactUri(String contactUri) {
this.mContactUri = contactUri;
}
@@ -70,6 +73,7 @@ public class PresTupleInfo implements Parcelable {
* Sets the timestamp.
* @hide
*/
+ @UnsupportedAppUsage
public void setTimestamp(String timestamp) {
this.mTimestamp = timestamp;
}
@@ -78,6 +82,7 @@ public class PresTupleInfo implements Parcelable {
* Constructor for the PresTupleInfo class.
* @hide
*/
+ @UnsupportedAppUsage
public PresTupleInfo(){};
/** @hide */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d850fbc471ab..f9c394000c83 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -390,7 +390,7 @@ interface ITelephony {
/**
* Returns the neighboring cell information of the device.
*/
- List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
+ List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg, int targetSdk);
int getCallState();
diff --git a/tests/FlickerTests/Android.mk b/tests/FlickerTests/Android.mk
new file mode 100644
index 000000000000..3c70f8bc2d72
--- /dev/null
+++ b/tests/FlickerTests/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := FlickerTests
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ flickertestapplib \
+ flickerlib \
+ truth-prebuilt \
+ app-helpers-core
+
+include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
new file mode 100644
index 000000000000..ba6394008642
--- /dev/null
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.wm.flicker">
+
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest> \ No newline at end of file
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
new file mode 100644
index 000000000000..b31235be8373
--- /dev/null
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager Flicker Tests">
+ <option name="test-tag" value="FlickerTests" />
+ <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on" />
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="FlickerTests.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.wm.flicker"/>
+ <option name="shell-timeout" value="6600s" />
+ <option name="test-timeout" value="6000s" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/flicker" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/README.md b/tests/FlickerTests/README.md
new file mode 100644
index 000000000000..a7c9e20e0a07
--- /dev/null
+++ b/tests/FlickerTests/README.md
@@ -0,0 +1,146 @@
+# Flicker Test Library
+
+## Motivation
+Detect *flicker* &mdash; any discontinuous, or unpredictable behavior seen during UI transitions that is not due to performance. This is often the result of a logic error in the code and difficult to identify because the issue is transient and at times difficult to reproduce. This library helps create integration tests between `SurfaceFlinger`, `WindowManager` and `SystemUI` to identify flicker.
+
+## Adding a Test
+The library builds and runs UI transitions, captures Winscope traces and exposes common assertions that can be tested against each trace.
+
+### Building Transitions
+Start by defining common or error prone transitions using `TransitionRunner`.
+```java
+// Example: Build a transition that cold launches an app from launcher
+TransitionRunner transition = TransitionRunner.newBuilder()
+ // Specify a tag to identify the transition (optional)
+ .withTag("OpenAppCold_" + testApp.getLauncherName())
+
+ // Specify preconditions to setup the device
+ // Wake up device and go to home screen
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+
+ // Setup transition under test
+ // Press the home button and close the app to test a cold start
+ .runBefore(device::pressHome)
+ .runBefore(testApp::exit)
+
+ // Run the transition under test
+ // Open the app and wait for UI to be idle
+ // This is the part of the transition that will be tested.
+ .run(testApp::open)
+ .run(device::waitForIdle)
+
+ // Perform any tear downs
+ // Close the app
+ .runAfterAll(testApp::exit)
+
+ // Number of times to repeat the transition to catch any flaky issues
+ .repeat(5);
+```
+
+
+Run the transition to get a list of `TransitionResult` for each time the transition is repeated.
+```java
+ List<TransitionResult> results = transition.run();
+```
+`TransitionResult` contains paths to test artifacts such as Winscope traces and screen recordings.
+
+
+### Checking Assertions
+Each `TransitionResult` can be tested using an extension of the Google Truth library, `LayersTraceSubject` and `WmTraceSubject`. They try to balance test principles set out by Google Truth (not supporting nested assertions, keeping assertions simple) with providing support for common assertion use cases.
+
+Each trace can be represented as a ordered collection of trace entries, with an associated timestamp. Each trace entry has common assertion checks. The trace subjects expose methods to filter the range of entries and test for changing assertions.
+
+```java
+ TransitionResult result = results.get(0);
+ Rect displayBounds = getDisplayBounds();
+
+ // check all trace entries
+ assertThat(result).coversRegion(displayBounds).forAllEntries();
+
+ // check a range of entries
+ assertThat(result).coversRegion(displayBounds).forRange(startTime, endTime);
+
+ // check first entry
+ assertThat(result).coversRegion(displayBounds).inTheBeginning();
+
+ // check last entry
+ assertThat(result).coversRegion(displayBounds).atTheEnd();
+
+ // check a change in assertions, e.g. wallpaper window is visible,
+ // then wallpaper window becomes and stays invisible
+ assertThat(result)
+ .showsBelowAppWindow("wallpaper")
+ .then()
+ .hidesBelowAppWindow("wallpaper")
+ .forAllEntries();
+```
+
+All assertions return `Result` which contains a `success` flag, `assertionName` string identifier, and `reason` string to provide actionable details to the user. The `reason` string is build along the way with all the details as to why the assertions failed and any hints which might help the user determine the root cause. Failed assertion message will also contain a path to the trace that was tested. Example of a failed test:
+
+```
+ java.lang.AssertionError: Not true that <com.android.server.wm.flicker.LayersTrace@65da4cc>
+ Layers Trace can be found in: /layers_trace_emptyregion.pb
+ Timestamp: 2308008331271
+ Assertion: coversRegion
+ Reason: Region to test: Rect(0, 0 - 1440, 2880)
+ first empty point: 0, 99
+ visible regions:
+ StatusBar#0Rect(0, 0 - 1440, 98)
+ NavigationBar#0Rect(0, 2712 - 1440, 2880)
+ ScreenDecorOverlay#0Rect(0, 0 - 1440, 91)
+ ...
+ at com.google.common.truth.FailureStrategy.fail(FailureStrategy.java:24)
+ ...
+```
+
+---
+
+## Running Tests
+
+The tests can be run as any other Android JUnit tests. `platform_testing/tests/flicker` uses the library to test common UI transitions. Run `atest FlickerTest` to execute these tests.
+
+---
+
+## Other Topics
+### Monitors
+Monitors capture test artifacts for each transition run. They are started before each iteration of the test transition (after the `runBefore` calls) and stopped after the transition is completed. Each iteration will produce a new test artifact. The following monitors are available:
+
+#### LayersTraceMonitor
+Captures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor.
+#### WindowManagerTraceMonitor
+Captures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor.
+#### WindowAnimationFrameStatsMonitor
+Captures WindowAnimationFrameStats for the transition. This monitor is started by default and is used to eliminate *janky* runs. If an iteration has skipped frames, as determined by WindowAnimationFrameStats, the results for the iteration is skipped. If the list of results is empty after all iterations are completed, then the test should fail. Build a transition with `includeJankyRuns()` to disable this monitor.
+#### ScreenRecorder
+Captures screen to a video file. This monitor is disabled by default. Build a transition with `recordEachRun()` to capture each transition or build with `recordAllRuns()` to capture every transition including setup and teardown.
+
+---
+
+### Extending Assertions
+
+To add a new assertion, add a function to one of the trace entry classes, `LayersTrace.Entry` or `WindowManagerTrace.Entry`.
+
+```java
+ // Example adds an assertion to the check if layer is hidden by parent.
+ Result isHiddenByParent(String layerName) {
+ // Result should contain a details if assertion fails for any reason
+ // such as if layer is not found or layer is not hidden by parent
+ // or layer has no parent.
+ // ...
+ }
+```
+Then add a function to the trace subject `LayersTraceSubject` or `WmTraceSubject` which will add the assertion for testing. When the assertion is evaluated, the trace will first be filtered then the assertion will be applied to the remaining entries.
+
+```java
+ public LayersTraceSubject isHiddenByParent(String layerName) {
+ mChecker.add(entry -> entry.isHiddenByParent(layerName),
+ "isHiddenByParent(" + layerName + ")");
+ return this;
+ }
+```
+
+To use the new assertion:
+```java
+ // Check if "Chrome" layer is hidden by parent in the first trace entry.
+ assertThat(result).isHiddenByParent("Chrome").inTheBeginning();
+``` \ No newline at end of file
diff --git a/tests/FlickerTests/TEST_MAPPING b/tests/FlickerTests/TEST_MAPPING
new file mode 100644
index 000000000000..55a61471dfb8
--- /dev/null
+++ b/tests/FlickerTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "FlickerTests"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/Android.mk b/tests/FlickerTests/lib/Android.mk
new file mode 100644
index 000000000000..6a8dfe8b5d0a
--- /dev/null
+++ b/tests/FlickerTests/lib/Android.mk
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := flickerlib
+LOCAL_MODULE_TAGS := tests optional
+# sign this with platform cert, so this test is allowed to call private platform apis
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ub-janktesthelper \
+ cts-amwm-util \
+ platformprotosnano \
+ layersprotosnano \
+ truth-prebuilt \
+ sysui-helper \
+ launcher-helper-lib \
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flickerautomationhelperlib
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := src/com/android/server/wm/flicker/AutomationUtils.java \
+ src/com/android/server/wm/flicker/WindowUtils.java
+LOCAL_STATIC_JAVA_LIBRARIES := sysui-helper \
+ launcher-helper-lib \
+ compatibility-device-util
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
new file mode 100644
index 000000000000..84f9f871324c
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+
+/**
+ * Collection of functional interfaces and classes representing assertions and their associated
+ * results. Assertions are functions that are applied over a single trace entry and returns a
+ * result which includes a detailed reason if the assertion fails.
+ */
+class Assertions {
+ /**
+ * Checks assertion on a single trace entry.
+ *
+ * @param <T> trace entry type to perform the assertion on.
+ */
+ @FunctionalInterface
+ interface TraceAssertion<T> extends Function<T, Result> {
+ /**
+ * Returns an assertion that represents the logical negation of this assertion.
+ *
+ * @return a assertion that represents the logical negation of this assertion
+ */
+ default TraceAssertion<T> negate() {
+ return (T t) -> apply(t).negate();
+ }
+ }
+
+ /**
+ * Checks assertion on a single layers trace entry.
+ */
+ @FunctionalInterface
+ interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {
+
+ }
+
+ /**
+ * Utility class to store assertions with an identifier to help generate more useful debug
+ * data when dealing with multiple assertions.
+ */
+ static class NamedAssertion<T> {
+ final TraceAssertion<T> assertion;
+ final String name;
+
+ NamedAssertion(TraceAssertion<T> assertion, String name) {
+ this.assertion = assertion;
+ this.name = name;
+ }
+ }
+
+ /**
+ * Contains the result of an assertion including the reason for failed assertions.
+ */
+ static class Result {
+ static final String NEGATION_PREFIX = "!";
+ final boolean success;
+ final long timestamp;
+ final String assertionName;
+ final String reason;
+
+ Result(boolean success, long timestamp, String assertionName, String reason) {
+ this.success = success;
+ this.timestamp = timestamp;
+ this.assertionName = assertionName;
+ this.reason = reason;
+ }
+
+ Result(boolean success, String reason) {
+ this.success = success;
+ this.reason = reason;
+ this.assertionName = "";
+ this.timestamp = 0;
+ }
+
+ /**
+ * Returns the negated {@code Result} and adds a negation prefix to the assertion name.
+ */
+ Result negate() {
+ String negatedAssertionName;
+ if (this.assertionName.startsWith(NEGATION_PREFIX)) {
+ negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
+ } else {
+ negatedAssertionName = NEGATION_PREFIX + this.assertionName;
+ }
+ return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
+ }
+
+ boolean passed() {
+ return this.success;
+ }
+
+ boolean failed() {
+ return !this.success;
+ }
+
+ @Override
+ public String toString() {
+ return "Timestamp: " + prettyTimestamp(timestamp)
+ + "\nAssertion: " + assertionName
+ + "\nReason: " + reason;
+ }
+
+ private String prettyTimestamp(long timestamp_ns) {
+ StringBuilder prettyTimestamp = new StringBuilder();
+ TimeUnit[] timeUnits = {TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS, TimeUnit
+ .MILLISECONDS};
+ String[] unitSuffixes = {"h", "m", "s", "ms"};
+
+ for (int i = 0; i < timeUnits.length; i++) {
+ long convertedTime = timeUnits[i].convert(timestamp_ns, TimeUnit.NANOSECONDS);
+ timestamp_ns -= TimeUnit.NANOSECONDS.convert(convertedTime, timeUnits[i]);
+ prettyTimestamp.append(convertedTime).append(unitSuffixes[i]);
+ }
+
+ return prettyTimestamp.toString();
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
new file mode 100644
index 000000000000..3c65d3c341b3
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import com.android.server.wm.flicker.Assertions.NamedAssertion;
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.Assertions.TraceAssertion;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Captures some of the common logic in {@link LayersTraceSubject} and {@link WmTraceSubject}
+ * used to filter trace entries and combine multiple assertions.
+ *
+ * @param <T> trace entry type
+ */
+public class AssertionsChecker<T extends ITraceEntry> {
+ private boolean mFilterEntriesByRange = false;
+ private long mFilterStartTime = 0;
+ private long mFilterEndTime = 0;
+ private AssertionOption mOption = AssertionOption.NONE;
+ private List<NamedAssertion<T>> mAssertions = new LinkedList<>();
+
+ void add(Assertions.TraceAssertion<T> assertion, String name) {
+ mAssertions.add(new NamedAssertion<>(assertion, name));
+ }
+
+ void filterByRange(long startTime, long endTime) {
+ mFilterEntriesByRange = true;
+ mFilterStartTime = startTime;
+ mFilterEndTime = endTime;
+ }
+
+ private void setOption(AssertionOption option) {
+ if (mOption != AssertionOption.NONE && option != mOption) {
+ throw new IllegalArgumentException("Cannot use " + mOption + " option with "
+ + option + " option.");
+ }
+ mOption = option;
+ }
+
+ public void checkFirstEntry() {
+ setOption(AssertionOption.CHECK_FIRST_ENTRY);
+ }
+
+ public void checkLastEntry() {
+ setOption(AssertionOption.CHECK_LAST_ENTRY);
+ }
+
+ public void checkChangingAssertions() {
+ setOption(AssertionOption.CHECK_CHANGING_ASSERTIONS);
+ }
+
+
+ /**
+ * Filters trace entries then runs assertions returning a list of failures.
+ *
+ * @param entries list of entries to perform assertions on
+ * @return list of failed assertion results
+ */
+ List<Result> test(List<T> entries) {
+ List<T> filteredEntries;
+ List<Result> failures;
+
+ if (mFilterEntriesByRange) {
+ filteredEntries = entries.stream()
+ .filter(e -> ((e.getTimestamp() >= mFilterStartTime)
+ && (e.getTimestamp() <= mFilterEndTime)))
+ .collect(Collectors.toList());
+ } else {
+ filteredEntries = entries;
+ }
+
+ switch (mOption) {
+ case CHECK_CHANGING_ASSERTIONS:
+ return assertChanges(filteredEntries);
+ case CHECK_FIRST_ENTRY:
+ return assertEntry(filteredEntries.get(0));
+ case CHECK_LAST_ENTRY:
+ return assertEntry(filteredEntries.get(filteredEntries.size() - 1));
+ }
+ return assertAll(filteredEntries);
+ }
+
+ /**
+ * Steps through each trace entry checking if provided assertions are true in the order they
+ * are added. Each assertion must be true for at least a single trace entry.
+ *
+ * This can be used to check for asserting a change in property over a trace. Such as visibility
+ * for a window changes from true to false or top-most window changes from A to Bb and back to A
+ * again.
+ */
+ private List<Result> assertChanges(List<T> entries) {
+ List<Result> failures = new ArrayList<>();
+ int entryIndex = 0;
+ int assertionIndex = 0;
+ int lastPassedAssertionIndex = -1;
+
+ if (mAssertions.size() == 0) {
+ return failures;
+ }
+
+ while (assertionIndex < mAssertions.size() && entryIndex < entries.size()) {
+ TraceAssertion<T> currentAssertion = mAssertions.get(assertionIndex).assertion;
+ Result result = currentAssertion.apply(entries.get(entryIndex));
+ if (result.passed()) {
+ lastPassedAssertionIndex = assertionIndex;
+ entryIndex++;
+ continue;
+ }
+
+ if (lastPassedAssertionIndex != assertionIndex) {
+ failures.add(result);
+ break;
+ }
+ assertionIndex++;
+
+ if (assertionIndex == mAssertions.size()) {
+ failures.add(result);
+ break;
+ }
+ }
+
+ if (failures.isEmpty()) {
+ if (assertionIndex != mAssertions.size() - 1) {
+ String reason = "\nAssertion " + mAssertions.get(assertionIndex).name
+ + " never became false";
+ reason += "\nPassed assertions: " + mAssertions.stream().limit(assertionIndex)
+ .map(assertion -> assertion.name).collect(Collectors.joining(","));
+ reason += "\nUntested assertions: " + mAssertions.stream().skip(assertionIndex + 1)
+ .map(assertion -> assertion.name).collect(Collectors.joining(","));
+
+ Result result = new Result(false /* success */, 0 /* timestamp */,
+ "assertChanges", "Not all assertions passed." + reason);
+ failures.add(result);
+ }
+ }
+ return failures;
+ }
+
+ private List<Result> assertEntry(T entry) {
+ List<Result> failures = new ArrayList<>();
+ for (NamedAssertion<T> assertion : mAssertions) {
+ Result result = assertion.assertion.apply(entry);
+ if (result.failed()) {
+ failures.add(result);
+ }
+ }
+ return failures;
+ }
+
+ private List<Result> assertAll(List<T> entries) {
+ return mAssertions.stream().flatMap(
+ assertion -> entries.stream()
+ .map(assertion.assertion)
+ .filter(Result::failed))
+ .collect(Collectors.toList());
+ }
+
+ private enum AssertionOption {
+ NONE,
+ CHECK_CHANGING_ASSERTIONS,
+ CHECK_FIRST_ENTRY,
+ CHECK_LAST_ENTRY,
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
new file mode 100644
index 000000000000..6bac67549ddc
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static android.os.SystemClock.sleep;
+import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
+import static android.view.Surface.ROTATION_0;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.launcherhelper.LauncherStrategyFactory;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+import android.util.Rational;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Collection of UI Automation helper functions.
+ */
+public class AutomationUtils {
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+ private static final long FIND_TIMEOUT = 10000;
+ private static final long LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2L;
+ private static final String TAG = "FLICKER";
+
+ public static void wakeUpAndGoToHomeScreen() {
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry
+ .getInstrumentation());
+ try {
+ device.wakeUp();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ device.pressHome();
+ }
+
+ /**
+ * Sets {@link android.app.UiAutomation#waitForIdle(long, long)} global timeout to 0 causing
+ * the {@link android.app.UiAutomation#waitForIdle(long, long)} function to timeout instantly.
+ * This removes some delays when using the UIAutomator library required to create fast UI
+ * transitions.
+ */
+ static void setFastWait() {
+ Configurator.getInstance().setWaitForIdleTimeout(0);
+ }
+
+ /**
+ * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
+ */
+ static void setDefaultWait() {
+ Configurator.getInstance().setWaitForIdleTimeout(10000);
+ }
+
+ public static boolean isQuickstepEnabled(UiDevice device) {
+ return device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null;
+ }
+
+ public static void openQuickstep(UiDevice device) {
+ if (isQuickstepEnabled(device)) {
+ int height = device.getDisplayHeight();
+ UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
+
+ Rect navBarVisibleBounds;
+
+ // TODO(vishnun) investigate why this object cannot be found.
+ if (navBar != null) {
+ navBarVisibleBounds = navBar.getVisibleBounds();
+ } else {
+ Log.e(TAG, "Could not find nav bar, infer location");
+ navBarVisibleBounds = WindowUtils.getNavigationBarPosition(ROTATION_0);
+ }
+
+ // Swipe from nav bar to 2/3rd down the screen.
+ device.swipe(
+ navBarVisibleBounds.centerX(), navBarVisibleBounds.centerY(),
+ navBarVisibleBounds.centerX(), height * 2 / 3,
+ (navBarVisibleBounds.centerY() - height * 2 / 3) / 100); // 100 px/step
+ } else {
+ try {
+ device.pressRecentApps();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
+
+ // use a long timeout to wait until recents populated
+ if (device.wait(
+ Until.findObject(isRecentsInLauncher()
+ ? getLauncherOverviewSelector(device) : RECENTS),
+ 10000) == null) {
+ fail("Recents didn't appear");
+ }
+ device.waitForIdle();
+ }
+
+ static void clearRecents(UiDevice device) {
+ if (isQuickstepEnabled(device)) {
+ openQuickstep(device);
+
+ for (int i = 0; i < 5; i++) {
+ device.swipe(device.getDisplayWidth() / 2,
+ device.getDisplayHeight() / 2, device.getDisplayWidth(),
+ device.getDisplayHeight() / 2,
+ 5);
+
+ BySelector clearAllSelector = By.res("com.google.android.apps.nexuslauncher",
+ "clear_all_button");
+ UiObject2 clearAllButton = device.wait(Until.findObject(clearAllSelector), 100);
+ if (clearAllButton != null) {
+ clearAllButton.click();
+ return;
+ }
+ }
+ }
+ }
+
+ private static BySelector getLauncherOverviewSelector(UiDevice device) {
+ return By.res(device.getLauncherPackageName(), "overview_panel");
+ }
+
+ private static void longPressRecents(UiDevice device) {
+ BySelector recentsSelector = By.res(SYSTEMUI_PACKAGE, "recent_apps");
+ UiObject2 recentsButton = device.wait(Until.findObject(recentsSelector), FIND_TIMEOUT);
+ assertNotNull("Unable to find recents button", recentsButton);
+ recentsButton.click(LONG_PRESS_TIMEOUT);
+ }
+
+ public static void launchSplitScreen(UiDevice device) {
+ String mLauncherPackage = LauncherStrategyFactory.getInstance(device)
+ .getLauncherStrategy().getSupportedLauncherPackage();
+
+ if (isQuickstepEnabled(device)) {
+ // Quickstep enabled
+ openQuickstep(device);
+
+ BySelector overviewIconSelector = By.res(mLauncherPackage, "icon")
+ .clazz(View.class);
+ UiObject2 overviewIcon = device.wait(Until.findObject(overviewIconSelector),
+ FIND_TIMEOUT);
+ assertNotNull("Unable to find app icon in Overview", overviewIcon);
+ overviewIcon.click();
+
+ BySelector splitscreenButtonSelector = By.text("Split screen");
+ UiObject2 splitscreenButton = device.wait(Until.findObject(splitscreenButtonSelector),
+ FIND_TIMEOUT);
+ assertNotNull("Unable to find Split screen button in Overview", overviewIcon);
+ splitscreenButton.click();
+ } else {
+ // Classic long press recents
+ longPressRecents(device);
+ }
+ // Wait for animation to complete.
+ sleep(2000);
+ }
+
+ public static void exitSplitScreen(UiDevice device) {
+ if (isQuickstepEnabled(device)) {
+ // Quickstep enabled
+ BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+ UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+ assertNotNull("Unable to find Split screen divider", divider);
+
+ // Drag the split screen divider to the top of the screen
+ divider.drag(new Point(device.getDisplayWidth() / 2, 0), 400);
+ } else {
+ // Classic long press recents
+ longPressRecents(device);
+ }
+ // Wait for animation to complete.
+ sleep(2000);
+ }
+
+ static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
+ BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+ UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+ assertNotNull("Unable to find Split screen divider", divider);
+ int destHeight =
+ (int) (WindowUtils.getDisplayBounds().height() * windowHeightRatio.floatValue());
+ // Drag the split screen divider to so that the ratio of top window height and bottom
+ // window height is windowHeightRatio
+ device.drag(divider.getVisibleBounds().centerX(), divider.getVisibleBounds().centerY(),
+ device.getDisplayWidth() / 2, destHeight, 10);
+ //divider.drag(new Point(device.getDisplayWidth() / 2, destHeight), 400)
+ divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+
+ // Wait for animation to complete.
+ sleep(2000);
+ }
+
+ static void closePipWindow(UiDevice device) {
+ UiObject2 pipWindow = device.findObject(
+ By.res(SYSTEMUI_PACKAGE, "background"));
+ pipWindow.click();
+ UiObject2 exitPipObject = device.findObject(
+ By.res(SYSTEMUI_PACKAGE, "dismiss"));
+ exitPipObject.click();
+ // Wait for animation to complete.
+ sleep(2000);
+ }
+
+ static void expandPipWindow(UiDevice device) {
+ UiObject2 pipWindow = device.findObject(
+ By.res(SYSTEMUI_PACKAGE, "background"));
+ pipWindow.click();
+ pipWindow.click();
+ }
+
+ public static void stopPackage(Context context, String packageName) {
+ runShellCommand("am force-stop " + packageName);
+ int packageUid;
+ try {
+ packageUid = context.getPackageManager().getPackageUid(packageName, /* flags= */0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ while (targetPackageIsRunning(packageUid)) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ }
+ }
+
+ private static boolean targetPackageIsRunning(int uid) {
+ final String result = runShellCommand(
+ String.format("cmd activity get-uid-state %d", uid));
+ return !result.contains("(NONEXISTENT)");
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
new file mode 100644
index 000000000000..9525f41b46b2
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+/**
+ * Common interface for Layer and WindowManager trace entries.
+ */
+interface ITraceEntry {
+ /**
+ * @return timestamp of current entry
+ */
+ long getTimestamp();
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
new file mode 100644
index 000000000000..660ec0fe4833
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.surfaceflinger.nano.Layers.LayerProto;
+import android.surfaceflinger.nano.Layers.RectProto;
+import android.surfaceflinger.nano.Layers.RegionProto;
+import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
+import android.surfaceflinger.nano.Layerstrace.LayersTraceProto;
+import android.util.SparseArray;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Contains a collection of parsed Layers trace entries and assertions to apply over
+ * a single entry.
+ *
+ * Each entry is parsed into a list of {@link LayersTrace.Entry} objects.
+ */
+public class LayersTrace {
+ final private List<Entry> mEntries;
+ @Nullable
+ final private Path mSource;
+
+ private LayersTrace(List<Entry> entries, Path source) {
+ this.mEntries = entries;
+ this.mSource = source;
+ }
+
+ /**
+ * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
+ * of trace entries, storing the flattened layers into its hierarchical structure.
+ *
+ * @param data binary proto data
+ * @param source Path to source of data for additional debug information
+ */
+ static LayersTrace parseFrom(byte[] data, Path source) {
+ List<Entry> entries = new ArrayList<>();
+ LayersTraceFileProto fileProto;
+ try {
+ fileProto = LayersTraceFileProto.parseFrom(data);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ for (LayersTraceProto traceProto : fileProto.entry) {
+ Entry entry = Entry.fromFlattenedLayers(traceProto.elapsedRealtimeNanos,
+ traceProto.layers.layers);
+ entries.add(entry);
+ }
+ return new LayersTrace(entries, source);
+ }
+
+ /**
+ * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
+ * of trace entries, storing the flattened layers into its hierarchical structure.
+ *
+ * @param data binary proto data
+ */
+ static LayersTrace parseFrom(byte[] data) {
+ return parseFrom(data, null);
+ }
+
+ List<Entry> getEntries() {
+ return mEntries;
+ }
+
+ Entry getEntry(long timestamp) {
+ Optional<Entry> entry = mEntries.stream()
+ .filter(e -> e.getTimestamp() == timestamp)
+ .findFirst();
+ if (!entry.isPresent()) {
+ throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
+ }
+ return entry.get();
+ }
+
+ Optional<Path> getSource() {
+ return Optional.ofNullable(mSource);
+ }
+
+ /**
+ * Represents a single Layer trace entry.
+ */
+ static class Entry implements ITraceEntry {
+ private long mTimestamp;
+ private List<Layer> mRootLayers; // hierarchical representation of layers
+ private List<Layer> mFlattenedLayers = null;
+
+ private Entry(long timestamp, List<Layer> rootLayers) {
+ this.mTimestamp = timestamp;
+ this.mRootLayers = rootLayers;
+ }
+
+ /**
+ * Constructs the layer hierarchy from a flattened list of layers.
+ */
+ static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) {
+ SparseArray<Layer> layerMap = new SparseArray<>();
+ ArrayList<Layer> orphans = new ArrayList<>();
+ for (LayerProto proto : protos) {
+ int id = proto.id;
+ int parentId = proto.parent;
+
+ Layer newLayer = layerMap.get(id);
+ if (newLayer == null) {
+ newLayer = new Layer(proto);
+ layerMap.append(id, newLayer);
+ } else if (newLayer.mProto != null) {
+ throw new RuntimeException("Duplicate layer id found:" + id);
+ } else {
+ newLayer.mProto = proto;
+ orphans.remove(newLayer);
+ }
+
+ // add parent placeholder
+ if (layerMap.get(parentId) == null) {
+ Layer orphanLayer = new Layer(null);
+ layerMap.append(parentId, orphanLayer);
+ orphans.add(orphanLayer);
+ }
+ layerMap.get(parentId).addChild(newLayer);
+ newLayer.addParent(layerMap.get(parentId));
+ }
+
+ // Fail if we find orphan layers.
+ orphans.remove(layerMap.get(-1));
+ orphans.forEach(orphan -> {
+ String childNodes = orphan.mChildren.stream().map(node ->
+ Integer.toString(node.getId())).collect(Collectors.joining(", "));
+ int orphanId = orphan.mChildren.get(0).mProto.parent;
+ throw new RuntimeException(
+ "Failed to parse layers trace. Found orphan layers with parent "
+ + "layer id:" + orphanId + " : " + childNodes);
+ });
+
+ return new Entry(timestamp, layerMap.get(-1).mChildren);
+ }
+
+ /**
+ * Extracts {@link Rect} from {@link RectProto}.
+ */
+ private static Rect extract(RectProto proto) {
+ return new Rect(proto.left, proto.top, proto.right, proto.bottom);
+ }
+
+ /**
+ * Extracts {@link Rect} from {@link RegionProto} by returning a rect that encompasses all
+ * the rects making up the region.
+ */
+ private static Rect extract(RegionProto regionProto) {
+ Rect region = new Rect();
+ for (RectProto proto : regionProto.rect) {
+ region.union(proto.left, proto.top, proto.right, proto.bottom);
+ }
+ return region;
+ }
+
+ /**
+ * Checks if a region specified by {@code testRect} is covered by all visible layers.
+ */
+ Result coversRegion(Rect testRect) {
+ String assertionName = "coversRegion";
+ Collection<Layer> layers = asFlattenedLayers();
+
+ for (int x = testRect.left; x < testRect.right; x++) {
+ for (int y = testRect.top; y < testRect.bottom; y++) {
+ boolean emptyRegionFound = true;
+ for (Layer layer : layers) {
+ if (layer.isInvisible() || layer.isHiddenByParent()) {
+ continue;
+ }
+ for (RectProto rectProto : layer.mProto.visibleRegion.rect) {
+ Rect r = extract(rectProto);
+ if (r.contains(x, y)) {
+ y = r.bottom;
+ emptyRegionFound = false;
+ }
+ }
+ }
+ if (emptyRegionFound) {
+ String reason = "Region to test: " + testRect
+ + "\nfirst empty point: " + x + ", " + y;
+ reason += "\nvisible regions:";
+ for (Layer layer : layers) {
+ if (layer.isInvisible() || layer.isHiddenByParent()) {
+ continue;
+ }
+ Rect r = extract(layer.mProto.visibleRegion);
+ reason += "\n" + layer.mProto.name + r.toString();
+ }
+ return new Result(false /* success */, this.mTimestamp, assertionName,
+ reason);
+ }
+ }
+ }
+ String info = "Region covered: " + testRect;
+ return new Result(true /* success */, this.mTimestamp, assertionName, info);
+ }
+
+ /**
+ * Checks if a layer with name {@code layerName} has a visible region
+ * {@code expectedVisibleRegion}.
+ */
+ Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
+ String assertionName = "hasVisibleRegion";
+ String reason = "Could not find " + layerName;
+ for (Layer layer : asFlattenedLayers()) {
+ if (layer.mProto.name.contains(layerName)) {
+ if (layer.isHiddenByParent()) {
+ reason = layer.getHiddenByParentReason();
+ continue;
+ }
+ if (layer.isInvisible()) {
+ reason = layer.getVisibilityReason();
+ continue;
+ }
+ Rect visibleRegion = extract(layer.mProto.visibleRegion);
+ if (visibleRegion.equals(expectedVisibleRegion)) {
+ return new Result(true /* success */, this.mTimestamp, assertionName,
+ layer.mProto.name + "has visible region " + expectedVisibleRegion);
+ }
+ reason = layer.mProto.name + " has visible region:" + visibleRegion + " "
+ + "expected:" + expectedVisibleRegion;
+ }
+ }
+ return new Result(false /* success */, this.mTimestamp, assertionName, reason);
+ }
+
+ /**
+ * Checks if a layer with name {@code layerName} is visible.
+ */
+ Result isVisible(String layerName) {
+ String assertionName = "isVisible";
+ String reason = "Could not find " + layerName;
+ for (Layer layer : asFlattenedLayers()) {
+ if (layer.mProto.name.contains(layerName)) {
+ if (layer.isHiddenByParent()) {
+ reason = layer.getHiddenByParentReason();
+ continue;
+ }
+ if (layer.isInvisible()) {
+ reason = layer.getVisibilityReason();
+ continue;
+ }
+ return new Result(true /* success */, this.mTimestamp, assertionName,
+ layer.mProto.name + " is visible");
+ }
+ }
+ return new Result(false /* success */, this.mTimestamp, assertionName, reason);
+ }
+
+ @Override
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ List<Layer> getRootLayers() {
+ return mRootLayers;
+ }
+
+ List<Layer> asFlattenedLayers() {
+ if (mFlattenedLayers == null) {
+ mFlattenedLayers = new ArrayList<>();
+ ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers);
+ while (!pendingLayers.isEmpty()) {
+ Layer layer = pendingLayers.remove(0);
+ mFlattenedLayers.add(layer);
+ pendingLayers.addAll(layer.mChildren);
+ }
+ }
+ return mFlattenedLayers;
+ }
+
+ Rect getVisibleBounds(String layerName) {
+ List<Layer> layers = asFlattenedLayers();
+ for (Layer layer : layers) {
+ if (layer.mProto.name.contains(layerName) && layer.isVisible()) {
+ return extract(layer.mProto.visibleRegion);
+ }
+ }
+ return new Rect(0, 0, 0, 0);
+ }
+ }
+
+ /**
+ * Represents a single layer with links to its parent and child layers.
+ */
+ static class Layer {
+ @Nullable
+ LayerProto mProto;
+ List<Layer> mChildren;
+ @Nullable
+ Layer mParent = null;
+
+ private Layer(LayerProto proto) {
+ this.mProto = proto;
+ this.mChildren = new ArrayList<>();
+ }
+
+ private void addChild(Layer childLayer) {
+ this.mChildren.add(childLayer);
+ }
+
+ private void addParent(Layer parentLayer) {
+ this.mParent = parentLayer;
+ }
+
+ int getId() {
+ return mProto.id;
+ }
+
+ boolean isActiveBufferEmpty() {
+ return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0
+ || this.mProto.activeBuffer.width == 0;
+ }
+
+ boolean isVisibleRegionEmpty() {
+ if (this.mProto.visibleRegion == null) {
+ return true;
+ }
+ Rect visibleRect = Entry.extract(this.mProto.visibleRegion);
+ return visibleRect.height() == 0 || visibleRect.width() == 0;
+ }
+
+ boolean isHidden() {
+ return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0;
+ }
+
+ boolean isVisible() {
+ return (!isActiveBufferEmpty() || isColorLayer()) &&
+ !isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty();
+ }
+
+ boolean isColorLayer() {
+ return this.mProto.type.equals("ColorLayer");
+ }
+
+ boolean isRootLayer() {
+ return mParent == null || mParent.mProto == null;
+ }
+
+ boolean isInvisible() {
+ return !isVisible();
+ }
+
+ boolean isHiddenByParent() {
+ return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent());
+ }
+
+ String getHiddenByParentReason() {
+ String reason = "Layer " + mProto.name;
+ if (isHiddenByParent()) {
+ reason += " is hidden by parent: " + mParent.mProto.name;
+ } else {
+ reason += " is not hidden by parent: " + mParent.mProto.name;
+ }
+ return reason;
+ }
+
+ String getVisibilityReason() {
+ String reason = "Layer " + mProto.name;
+ if (isVisible()) {
+ reason += " is visible:";
+ } else {
+ reason += " is invisible:";
+ if (this.mProto.activeBuffer == null) {
+ reason += " activeBuffer=null";
+ } else if (this.mProto.activeBuffer.height == 0) {
+ reason += " activeBuffer.height=0";
+ } else if (this.mProto.activeBuffer.width == 0) {
+ reason += " activeBuffer.width=0";
+ }
+ if (!isColorLayer()) {
+ reason += " type != ColorLayer";
+ }
+ if (isHidden()) {
+ reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)";
+ }
+ if (this.mProto.color.a == 0) {
+ reason += " color.a=0";
+ }
+ if (isVisibleRegionEmpty()) {
+ reason += " visible region is empty";
+ }
+ }
+ return reason;
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
new file mode 100644
index 000000000000..b4c97e4fc4f2
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.LayersTrace.Entry;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Truth subject for {@link LayersTrace} objects.
+ */
+public class LayersTraceSubject extends Subject<LayersTraceSubject, LayersTrace> {
+ // Boiler-plate Subject.Factory for LayersTraceSubject
+ private static final SubjectFactory<LayersTraceSubject, LayersTrace> FACTORY =
+ new SubjectFactory<LayersTraceSubject, LayersTrace>() {
+ @Override
+ public LayersTraceSubject getSubject(
+ FailureStrategy fs, @Nullable LayersTrace target) {
+ return new LayersTraceSubject(fs, target);
+ }
+ };
+
+ private AssertionsChecker<Entry> mChecker = new AssertionsChecker<>();
+
+ private LayersTraceSubject(FailureStrategy fs, @Nullable LayersTrace subject) {
+ super(fs, subject);
+ }
+
+ // User-defined entry point
+ public static LayersTraceSubject assertThat(@Nullable LayersTrace entry) {
+ return assertAbout(FACTORY).that(entry);
+ }
+
+ // User-defined entry point
+ public static LayersTraceSubject assertThat(@Nullable TransitionResult result) {
+ LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+ result.getLayersTracePath());
+ return assertWithMessage(result.toString()).about(FACTORY).that(entries);
+ }
+
+ // Static method for getting the subject factory (for use with assertAbout())
+ public static SubjectFactory<LayersTraceSubject, LayersTrace> entries() {
+ return FACTORY;
+ }
+
+ public void forAllEntries() {
+ test();
+ }
+
+ public void forRange(long startTime, long endTime) {
+ mChecker.filterByRange(startTime, endTime);
+ test();
+ }
+
+ public LayersTraceSubject then() {
+ mChecker.checkChangingAssertions();
+ return this;
+ }
+
+ public void inTheBeginning() {
+ if (getSubject().getEntries().isEmpty()) {
+ fail("No entries found.");
+ }
+ mChecker.checkFirstEntry();
+ test();
+ }
+
+ public void atTheEnd() {
+ if (getSubject().getEntries().isEmpty()) {
+ fail("No entries found.");
+ }
+ mChecker.checkLastEntry();
+ test();
+ }
+
+ private void test() {
+ List<Result> failures = mChecker.test(getSubject().getEntries());
+ if (!failures.isEmpty()) {
+ String failureLogs = failures.stream().map(Result::toString)
+ .collect(Collectors.joining("\n"));
+ String tracePath = "";
+ if (getSubject().getSource().isPresent()) {
+ tracePath = "\nLayers Trace can be found in: "
+ + getSubject().getSource().get().toAbsolutePath() + "\n";
+ }
+ fail(tracePath + failureLogs);
+ }
+ }
+
+ public LayersTraceSubject coversRegion(Rect rect) {
+ mChecker.add(entry -> entry.coversRegion(rect),
+ "coversRegion(" + rect + ")");
+ return this;
+ }
+
+ public LayersTraceSubject hasVisibleRegion(String layerName, Rect size) {
+ mChecker.add(entry -> entry.hasVisibleRegion(layerName, size),
+ "hasVisibleRegion(" + layerName + size + ")");
+ return this;
+ }
+
+ public LayersTraceSubject showsLayer(String layerName) {
+ mChecker.add(entry -> entry.isVisible(layerName),
+ "showsLayer(" + layerName + ")");
+ return this;
+ }
+
+ public LayersTraceSubject hidesLayer(String layerName) {
+ mChecker.add(entry -> entry.isVisible(layerName).negate(),
+ "hidesLayer(" + layerName + ")");
+ return this;
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
new file mode 100644
index 000000000000..f6e8192ee4c0
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import com.android.server.wm.flicker.monitor.ITransitionMonitor;
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
+import com.android.server.wm.flicker.monitor.ScreenRecorder;
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
+
+import com.google.common.io.Files;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Builds and runs UI transitions capturing test artifacts.
+ *
+ * User can compose a transition from simpler steps, specifying setup and teardown steps. During
+ * a transition, Layers trace, WindowManager trace, screen recordings and window animation frame
+ * stats can be captured.
+ *
+ * <pre>
+ * Transition builder options:
+ * {@link TransitionBuilder#run(Runnable)} run transition under test. Monitors will be started
+ * before the transition and stopped after the transition is completed.
+ * {@link TransitionBuilder#repeat(int)} repeat transitions under test multiple times recording
+ * result for each run.
+ * {@link TransitionBuilder#withTag(String)} specify a string identifier used to prefix logs and
+ * artifacts generated.
+ * {@link TransitionBuilder#runBeforeAll(Runnable)} run setup transitions once before all other
+ * transition are run to set up an initial state on device.
+ * {@link TransitionBuilder#runBefore(Runnable)} run setup transitions before each test transition
+ * run.
+ * {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions after each test
+ * transition.
+ * {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions once after all
+ * other transition are run.
+ * {@link TransitionBuilder#includeJankyRuns()} disables {@link WindowAnimationFrameStatsMonitor}
+ * to monitor janky frames. If janky frames are detected, then the test run is skipped. This
+ * monitor is enabled by default.
+ * {@link TransitionBuilder#skipLayersTrace()} disables {@link LayersTraceMonitor} used to
+ * capture Layers trace during a transition. This monitor is enabled by default.
+ * {@link TransitionBuilder#skipWindowManagerTrace()} disables {@link WindowManagerTraceMonitor}
+ * used to capture WindowManager trace during a transition. This monitor is enabled by
+ * default.
+ * {@link TransitionBuilder#recordAllRuns()} records the screen contents and saves it to a file.
+ * All the runs including setup and teardown transitions are included in the recording. This
+ * monitor is used for debugging purposes.
+ * {@link TransitionBuilder#recordEachRun()} records the screen contents during test transitions
+ * and saves it to a file for each run. This monitor is used for debugging purposes.
+ *
+ * Example transition to capture WindowManager and Layers trace when opening a test app:
+ * {@code
+ * TransitionRunner.newBuilder()
+ * .withTag("OpenTestAppFast")
+ * .runBeforeAll(UiAutomationLib::wakeUp)
+ * .runBeforeAll(UiAutomationLib::UnlockDevice)
+ * .runBeforeAll(UiAutomationLib::openTestApp)
+ * .runBefore(UiAutomationLib::closeTestApp)
+ * .run(UiAutomationLib::openTestApp)
+ * .runAfterAll(UiAutomationLib::closeTestApp)
+ * .repeat(5)
+ * .build()
+ * .run();
+ * }
+ * </pre>
+ */
+class TransitionRunner {
+ private static final String TAG = "FLICKER";
+ private final ScreenRecorder mScreenRecorder;
+ private final WindowManagerTraceMonitor mWmTraceMonitor;
+ private final LayersTraceMonitor mLayersTraceMonitor;
+ private final WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
+
+ private final List<ITransitionMonitor> mAllRunsMonitors;
+ private final List<ITransitionMonitor> mPerRunMonitors;
+ private final List<Runnable> mBeforeAlls;
+ private final List<Runnable> mBefores;
+ private final List<Runnable> mTransitions;
+ private final List<Runnable> mAfters;
+ private final List<Runnable> mAfterAlls;
+
+ private final int mIterations;
+ private final String mTestTag;
+
+ @Nullable
+ private List<TransitionResult> mResults = null;
+
+ private TransitionRunner(TransitionBuilder builder) {
+ mScreenRecorder = builder.mScreenRecorder;
+ mWmTraceMonitor = builder.mWmTraceMonitor;
+ mLayersTraceMonitor = builder.mLayersTraceMonitor;
+ mFrameStatsMonitor = builder.mFrameStatsMonitor;
+
+ mAllRunsMonitors = builder.mAllRunsMonitors;
+ mPerRunMonitors = builder.mPerRunMonitors;
+ mBeforeAlls = builder.mBeforeAlls;
+ mBefores = builder.mBefores;
+ mTransitions = builder.mTransitions;
+ mAfters = builder.mAfters;
+ mAfterAlls = builder.mAfterAlls;
+
+ mIterations = builder.mIterations;
+ mTestTag = builder.mTestTag;
+ }
+
+ static TransitionBuilder newBuilder() {
+ return new TransitionBuilder();
+ }
+
+ /**
+ * Runs the composed transition and calls monitors at the appropriate stages. If jank monitor
+ * is enabled, transitions with jank are skipped.
+ *
+ * @return itself
+ */
+ TransitionRunner run() {
+ mResults = new ArrayList<>();
+ mAllRunsMonitors.forEach(ITransitionMonitor::start);
+ mBeforeAlls.forEach(Runnable::run);
+ for (int iteration = 0; iteration < mIterations; iteration++) {
+ mBefores.forEach(Runnable::run);
+ mPerRunMonitors.forEach(ITransitionMonitor::start);
+ mTransitions.forEach(Runnable::run);
+ mPerRunMonitors.forEach(ITransitionMonitor::stop);
+ mAfters.forEach(Runnable::run);
+ if (runJankFree() && mFrameStatsMonitor.jankyFramesDetected()) {
+ String msg = String.format("Skipping iteration %d/%d for test %s due to jank. %s",
+ iteration, mIterations - 1, mTestTag, mFrameStatsMonitor.toString());
+ Log.e(TAG, msg);
+ continue;
+ }
+ mResults.add(saveResult(iteration));
+ }
+ mAfterAlls.forEach(Runnable::run);
+ mAllRunsMonitors.forEach(monitor -> {
+ monitor.stop();
+ Path path = monitor.save(mTestTag);
+ Log.e(TAG, "Video saved to " + path.toString());
+ });
+ return this;
+ }
+
+ /**
+ * Returns a list of transition results.
+ *
+ * @return list of transition results.
+ */
+ List<TransitionResult> getResults() {
+ if (mResults == null) {
+ throw new IllegalStateException("Results do not exist!");
+ }
+ return mResults;
+ }
+
+ /**
+ * Deletes all transition results that are not marked for saving.
+ *
+ * @return list of transition results.
+ */
+ void deleteResults() {
+ if (mResults == null) {
+ return;
+ }
+ mResults.stream()
+ .filter(TransitionResult::canDelete)
+ .forEach(TransitionResult::delete);
+ mResults = null;
+ }
+
+ /**
+ * Saves monitor results to file.
+ *
+ * @return object containing paths to test artifacts
+ */
+ private TransitionResult saveResult(int iteration) {
+ Path windowTrace = null;
+ Path layerTrace = null;
+ Path screenCaptureVideo = null;
+
+ if (mPerRunMonitors.contains(mWmTraceMonitor)) {
+ windowTrace = mWmTraceMonitor.save(mTestTag, iteration);
+ }
+ if (mPerRunMonitors.contains(mLayersTraceMonitor)) {
+ layerTrace = mLayersTraceMonitor.save(mTestTag, iteration);
+ }
+ if (mPerRunMonitors.contains(mScreenRecorder)) {
+ screenCaptureVideo = mScreenRecorder.save(mTestTag, iteration);
+ }
+ return new TransitionResult(layerTrace, windowTrace, screenCaptureVideo);
+ }
+
+ private boolean runJankFree() {
+ return mPerRunMonitors.contains(mFrameStatsMonitor);
+ }
+
+ public String getTestTag() {
+ return mTestTag;
+ }
+
+ /**
+ * Stores paths to all test artifacts.
+ */
+ @VisibleForTesting
+ public static class TransitionResult {
+ @Nullable
+ final Path layersTrace;
+ @Nullable
+ final Path windowManagerTrace;
+ @Nullable
+ final Path screenCaptureVideo;
+ private boolean flaggedForSaving;
+
+ TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
+ @Nullable Path screenCaptureVideo) {
+ this.layersTrace = layersTrace;
+ this.windowManagerTrace = windowManagerTrace;
+ this.screenCaptureVideo = screenCaptureVideo;
+ }
+
+ void flagForSaving() {
+ flaggedForSaving = true;
+ }
+
+ boolean canDelete() {
+ return !flaggedForSaving;
+ }
+
+ boolean layersTraceExists() {
+ return layersTrace != null && layersTrace.toFile().exists();
+ }
+
+ byte[] getLayersTrace() {
+ try {
+ return Files.toByteArray(this.layersTrace.toFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Path getLayersTracePath() {
+ return layersTrace;
+ }
+
+ boolean windowManagerTraceExists() {
+ return windowManagerTrace != null && windowManagerTrace.toFile().exists();
+ }
+
+ public byte[] getWindowManagerTrace() {
+ try {
+ return Files.toByteArray(this.windowManagerTrace.toFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Path getWindowManagerTracePath() {
+ return windowManagerTrace;
+ }
+
+ boolean screenCaptureVideoExists() {
+ return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
+ }
+
+ Path screenCaptureVideoPath() {
+ return screenCaptureVideo;
+ }
+
+ void delete() {
+ if (layersTraceExists()) layersTrace.toFile().delete();
+ if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
+ if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
+ }
+ }
+
+ /**
+ * Builds a {@link TransitionRunner} instance.
+ */
+ static class TransitionBuilder {
+ private ScreenRecorder mScreenRecorder;
+ private WindowManagerTraceMonitor mWmTraceMonitor;
+ private LayersTraceMonitor mLayersTraceMonitor;
+ private WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
+
+ private List<ITransitionMonitor> mAllRunsMonitors = new LinkedList<>();
+ private List<ITransitionMonitor> mPerRunMonitors = new LinkedList<>();
+ private List<Runnable> mBeforeAlls = new LinkedList<>();
+ private List<Runnable> mBefores = new LinkedList<>();
+ private List<Runnable> mTransitions = new LinkedList<>();
+ private List<Runnable> mAfters = new LinkedList<>();
+ private List<Runnable> mAfterAlls = new LinkedList<>();
+
+ private boolean mRunJankFree = true;
+ private boolean mCaptureWindowManagerTrace = true;
+ private boolean mCaptureLayersTrace = true;
+ private boolean mRecordEachRun = false;
+ private int mIterations = 1;
+ private String mTestTag = "";
+
+ private boolean mRecordAllRuns = false;
+
+ TransitionBuilder() {
+ mScreenRecorder = new ScreenRecorder();
+ mWmTraceMonitor = new WindowManagerTraceMonitor();
+ mLayersTraceMonitor = new LayersTraceMonitor();
+ mFrameStatsMonitor = new
+ WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation());
+ }
+
+ TransitionRunner build() {
+ if (mCaptureWindowManagerTrace) {
+ mPerRunMonitors.add(mWmTraceMonitor);
+ }
+
+ if (mCaptureLayersTrace) {
+ mPerRunMonitors.add(mLayersTraceMonitor);
+ }
+
+ if (mRunJankFree) {
+ mPerRunMonitors.add(mFrameStatsMonitor);
+ }
+
+ if (mRecordAllRuns) {
+ mAllRunsMonitors.add(mScreenRecorder);
+ }
+
+ if (mRecordEachRun) {
+ mPerRunMonitors.add(mScreenRecorder);
+ }
+
+ return new TransitionRunner(this);
+ }
+
+ TransitionBuilder runBeforeAll(Runnable runnable) {
+ mBeforeAlls.add(runnable);
+ return this;
+ }
+
+ TransitionBuilder runBefore(Runnable runnable) {
+ mBefores.add(runnable);
+ return this;
+ }
+
+ TransitionBuilder run(Runnable runnable) {
+ mTransitions.add(runnable);
+ return this;
+ }
+
+ TransitionBuilder runAfter(Runnable runnable) {
+ mAfters.add(runnable);
+ return this;
+ }
+
+ TransitionBuilder runAfterAll(Runnable runnable) {
+ mAfterAlls.add(runnable);
+ return this;
+ }
+
+ TransitionBuilder repeat(int iterations) {
+ mIterations = iterations;
+ return this;
+ }
+
+ TransitionBuilder skipWindowManagerTrace() {
+ mCaptureWindowManagerTrace = false;
+ return this;
+ }
+
+ TransitionBuilder skipLayersTrace() {
+ mCaptureLayersTrace = false;
+ return this;
+ }
+
+ TransitionBuilder includeJankyRuns() {
+ mRunJankFree = false;
+ return this;
+ }
+
+ TransitionBuilder recordEachRun() {
+ if (mRecordAllRuns) {
+ throw new IllegalArgumentException("Invalid option with recordAllRuns");
+ }
+ mRecordEachRun = true;
+ return this;
+ }
+
+ TransitionBuilder recordAllRuns() {
+ if (mRecordEachRun) {
+ throw new IllegalArgumentException("Invalid option with recordEachRun");
+ }
+ mRecordAllRuns = true;
+ return this;
+ }
+
+ TransitionBuilder withTag(String testTag) {
+ mTestTag = testTag;
+ return this;
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
new file mode 100644
index 000000000000..e3592eb8cd01
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.annotation.Nullable;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.nano.AppWindowTokenProto;
+import com.android.server.wm.nano.StackProto;
+import com.android.server.wm.nano.TaskProto;
+import com.android.server.wm.nano.WindowManagerTraceFileProto;
+import com.android.server.wm.nano.WindowManagerTraceProto;
+import com.android.server.wm.nano.WindowStateProto;
+import com.android.server.wm.nano.WindowTokenProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Contains a collection of parsed WindowManager trace entries and assertions to apply over
+ * a single entry.
+ *
+ * Each entry is parsed into a list of {@link WindowManagerTrace.Entry} objects.
+ */
+public class WindowManagerTrace {
+ private static final int DEFAULT_DISPLAY = 0;
+ private final List<Entry> mEntries;
+ @Nullable
+ final private Path mSource;
+
+ private WindowManagerTrace(List<Entry> entries, Path source) {
+ this.mEntries = entries;
+ this.mSource = source;
+ }
+
+ /**
+ * Parses {@code WindowManagerTraceFileProto} from {@code data} and uses the proto to
+ * generates a list of trace entries.
+ *
+ * @param data binary proto data
+ * @param source Path to source of data for additional debug information
+ */
+ static WindowManagerTrace parseFrom(byte[] data, Path source) {
+ List<Entry> entries = new ArrayList<>();
+
+ WindowManagerTraceFileProto fileProto;
+ try {
+ fileProto = WindowManagerTraceFileProto.parseFrom(data);
+ } catch (InvalidProtocolBufferNanoException e) {
+ throw new RuntimeException(e);
+ }
+ for (WindowManagerTraceProto entryProto : fileProto.entry) {
+ entries.add(new Entry(entryProto));
+ }
+ return new WindowManagerTrace(entries, source);
+ }
+
+ static WindowManagerTrace parseFrom(byte[] data) {
+ return parseFrom(data, null);
+ }
+
+ public List<Entry> getEntries() {
+ return mEntries;
+ }
+
+ Entry getEntry(long timestamp) {
+ Optional<Entry> entry = mEntries.stream()
+ .filter(e -> e.getTimestamp() == timestamp)
+ .findFirst();
+ if (!entry.isPresent()) {
+ throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
+ }
+ return entry.get();
+ }
+
+ Optional<Path> getSource() {
+ return Optional.ofNullable(mSource);
+ }
+
+ /**
+ * Represents a single WindowManager trace entry.
+ */
+ static class Entry implements ITraceEntry {
+ private final WindowManagerTraceProto mProto;
+
+ Entry(WindowManagerTraceProto proto) {
+ mProto = proto;
+ }
+
+ private static Result isWindowVisible(String windowTitle,
+ WindowTokenProto[] windowTokenProtos) {
+ boolean titleFound = false;
+ for (WindowTokenProto windowToken : windowTokenProtos) {
+ for (WindowStateProto windowState : windowToken.windows) {
+ if (windowState.identifier.title.contains(windowTitle)) {
+ titleFound = true;
+ if (isVisible(windowState)) {
+ return new Result(true /* success */,
+ windowState.identifier.title + " is visible");
+ }
+ }
+ }
+ }
+
+ String reason;
+ if (!titleFound) {
+ reason = windowTitle + " cannot be found";
+ } else {
+ reason = windowTitle + " is invisible";
+ }
+ return new Result(false /* success */, reason);
+ }
+
+ private static boolean isVisible(WindowStateProto windowState) {
+ return windowState.windowContainer.visible;
+ }
+
+ @Override
+ public long getTimestamp() {
+ return mProto.elapsedRealtimeNanos;
+ }
+
+ /**
+ * Returns window title of the top most visible app window.
+ */
+ private String getTopVisibleAppWindow() {
+ StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
+ .displays[DEFAULT_DISPLAY].stacks;
+ for (StackProto stack : stacks) {
+ for (TaskProto task : stack.tasks) {
+ for (AppWindowTokenProto token : task.appWindowTokens) {
+ for (WindowStateProto windowState : token.windowToken.windows) {
+ if (windowState.windowContainer.visible) {
+ return task.appWindowTokens[0].name;
+ }
+ }
+ }
+ }
+ }
+
+ return "";
+ }
+
+ /**
+ * Checks if aboveAppWindow with {@code windowTitle} is visible.
+ */
+ Result isAboveAppWindowVisible(String windowTitle) {
+ WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+ .rootWindowContainer
+ .displays[DEFAULT_DISPLAY].aboveAppWindows;
+ Result result = isWindowVisible(windowTitle, windowTokenProtos);
+ return new Result(result.success, getTimestamp(), "showsAboveAppWindow", result.reason);
+ }
+
+ /**
+ * Checks if belowAppWindow with {@code windowTitle} is visible.
+ */
+ Result isBelowAppWindowVisible(String windowTitle) {
+ WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+ .rootWindowContainer
+ .displays[DEFAULT_DISPLAY].belowAppWindows;
+ Result result = isWindowVisible(windowTitle, windowTokenProtos);
+ return new Result(result.success, getTimestamp(), "isBelowAppWindowVisible",
+ result.reason);
+ }
+
+ /**
+ * Checks if imeWindow with {@code windowTitle} is visible.
+ */
+ Result isImeWindowVisible(String windowTitle) {
+ WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+ .rootWindowContainer
+ .displays[DEFAULT_DISPLAY].imeWindows;
+ Result result = isWindowVisible(windowTitle, windowTokenProtos);
+ return new Result(result.success, getTimestamp(), "isImeWindowVisible",
+ result.reason);
+ }
+
+ /**
+ * Checks if app window with {@code windowTitle} is on top.
+ */
+ Result isVisibleAppWindowOnTop(String windowTitle) {
+ String topAppWindow = getTopVisibleAppWindow();
+ boolean success = topAppWindow.contains(windowTitle);
+ String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
+ return new Result(success, getTimestamp(), "isAppWindowOnTop", reason);
+ }
+
+ /**
+ * Checks if app window with {@code windowTitle} is visible.
+ */
+ Result isAppWindowVisible(String windowTitle) {
+ final String assertionName = "isAppWindowVisible";
+ boolean titleFound = false;
+ StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
+ .displays[DEFAULT_DISPLAY].stacks;
+ for (StackProto stack : stacks) {
+ for (TaskProto task : stack.tasks) {
+ for (AppWindowTokenProto token : task.appWindowTokens) {
+ if (token.name.contains(windowTitle)) {
+ titleFound = true;
+ for (WindowStateProto windowState : token.windowToken.windows) {
+ if (windowState.windowContainer.visible) {
+ return new Result(true /* success */, getTimestamp(),
+ assertionName, "Window " + token.name +
+ "is visible");
+ }
+ }
+ }
+ }
+ }
+ }
+ String reason;
+ if (!titleFound) {
+ reason = "Window " + windowTitle + " cannot be found";
+ } else {
+ reason = "Window " + windowTitle + " is invisible";
+ }
+ return new Result(false /* success */, getTimestamp(), assertionName, reason);
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
new file mode 100644
index 000000000000..0da876173995
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.view.Surface;
+import android.view.WindowManager;
+
+/**
+ * Helper functions to retrieve system window sizes and positions.
+ */
+class WindowUtils {
+
+ static Rect getDisplayBounds() {
+ Point display = new Point();
+ WindowManager wm =
+ (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+ Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getRealSize(display);
+ return new Rect(0, 0, display.x, display.y);
+ }
+
+ private static int getCurrentRotation() {
+ WindowManager wm =
+ (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+ Context.WINDOW_SERVICE);
+ return wm.getDefaultDisplay().getRotation();
+ }
+
+ static Rect getDisplayBounds(int requestedRotation) {
+ Rect displayBounds = getDisplayBounds();
+ int currentDisplayRotation = getCurrentRotation();
+
+ boolean displayIsRotated = (currentDisplayRotation == Surface.ROTATION_90 ||
+ currentDisplayRotation == Surface.ROTATION_270);
+
+ boolean requestedDisplayIsRotated = requestedRotation == Surface.ROTATION_90 ||
+ requestedRotation == Surface.ROTATION_270;
+
+ // if the current orientation changes with the requested rotation,
+ // flip height and width of display bounds.
+ if (displayIsRotated != requestedDisplayIsRotated) {
+ return new Rect(0, 0, displayBounds.height(), displayBounds.width());
+ }
+
+ return new Rect(0, 0, displayBounds.width(), displayBounds.height());
+ }
+
+
+ static Rect getAppPosition(int requestedRotation) {
+ Rect displayBounds = getDisplayBounds();
+ int currentDisplayRotation = getCurrentRotation();
+
+ boolean displayIsRotated = currentDisplayRotation == Surface.ROTATION_90 ||
+ currentDisplayRotation == Surface.ROTATION_270;
+
+ boolean requestedAppIsRotated = requestedRotation == Surface.ROTATION_90 ||
+ requestedRotation == Surface.ROTATION_270;
+
+ // display size will change if the display is reflected. Flip height and width of app if the
+ // requested rotation is different from the current rotation.
+ if (displayIsRotated != requestedAppIsRotated) {
+ return new Rect(0, 0, displayBounds.height(), displayBounds.width());
+ }
+
+ return new Rect(0, 0, displayBounds.width(), displayBounds.height());
+ }
+
+ static Rect getStatusBarPosition(int requestedRotation) {
+ Resources resources = InstrumentationRegistry.getContext().getResources();
+ String resourceName;
+ Rect displayBounds = getDisplayBounds();
+ int width;
+ if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
+ resourceName = "status_bar_height_portrait";
+ width = Math.min(displayBounds.width(), displayBounds.height());
+ } else {
+ resourceName = "status_bar_height_landscape";
+ width = Math.max(displayBounds.width(), displayBounds.height());
+ }
+
+ int resourceId = resources.getIdentifier(resourceName, "dimen", "android");
+ int height = resources.getDimensionPixelSize(resourceId);
+
+ return new Rect(0, 0, width, height);
+ }
+
+ static Rect getNavigationBarPosition(int requestedRotation) {
+ Resources resources = InstrumentationRegistry.getContext().getResources();
+ Rect displayBounds = getDisplayBounds();
+ int displayWidth = Math.min(displayBounds.width(), displayBounds.height());
+ int displayHeight = Math.max(displayBounds.width(), displayBounds.height());
+ int resourceId;
+ if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
+ resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+ int height = resources.getDimensionPixelSize(resourceId);
+ return new Rect(0, displayHeight - height, displayWidth, displayHeight);
+ } else {
+ resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
+ int width = resources.getDimensionPixelSize(resourceId);
+ // swap display dimensions in landscape or seascape mode
+ int temp = displayHeight;
+ displayHeight = displayWidth;
+ displayWidth = temp;
+ if (requestedRotation == Surface.ROTATION_90) {
+ return new Rect(0, 0, width, displayHeight);
+ } else {
+ return new Rect(displayWidth - width, 0, displayWidth, displayHeight);
+ }
+ }
+ }
+
+ static int getNavigationBarHeight() {
+ Resources resources = InstrumentationRegistry.getContext().getResources();
+ int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+ return resources.getDimensionPixelSize(resourceId);
+ }
+
+ static int getDockedStackDividerInset() {
+ Resources resources = InstrumentationRegistry.getContext().getResources();
+ int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen",
+ "android");
+ return resources.getDimensionPixelSize(resourceId);
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
new file mode 100644
index 000000000000..1fc7d591d2bb
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.Nullable;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Truth subject for {@link WindowManagerTrace} objects.
+ */
+public class WmTraceSubject extends Subject<WmTraceSubject, WindowManagerTrace> {
+ // Boiler-plate Subject.Factory for WmTraceSubject
+ private static final SubjectFactory<WmTraceSubject, WindowManagerTrace> FACTORY =
+ new SubjectFactory<WmTraceSubject, WindowManagerTrace>() {
+ @Override
+ public WmTraceSubject getSubject(
+ FailureStrategy fs, @Nullable WindowManagerTrace target) {
+ return new WmTraceSubject(fs, target);
+ }
+ };
+
+ private AssertionsChecker<WindowManagerTrace.Entry> mChecker = new AssertionsChecker<>();
+
+ private WmTraceSubject(FailureStrategy fs, @Nullable WindowManagerTrace subject) {
+ super(fs, subject);
+ }
+
+ // User-defined entry point
+ public static WmTraceSubject assertThat(@Nullable WindowManagerTrace entry) {
+ return assertAbout(FACTORY).that(entry);
+ }
+
+ // User-defined entry point
+ public static WmTraceSubject assertThat(@Nullable TransitionResult result) {
+ WindowManagerTrace entries = WindowManagerTrace.parseFrom(result.getWindowManagerTrace(),
+ result.getWindowManagerTracePath());
+ return assertWithMessage(result.toString()).about(FACTORY).that(entries);
+ }
+
+ // Static method for getting the subject factory (for use with assertAbout())
+ public static SubjectFactory<WmTraceSubject, WindowManagerTrace> entries() {
+ return FACTORY;
+ }
+
+ public void forAllEntries() {
+ test();
+ }
+
+ public void forRange(long startTime, long endTime) {
+ mChecker.filterByRange(startTime, endTime);
+ test();
+ }
+
+ public WmTraceSubject then() {
+ mChecker.checkChangingAssertions();
+ return this;
+ }
+
+ public void inTheBeginning() {
+ if (getSubject().getEntries().isEmpty()) {
+ fail("No entries found.");
+ }
+ mChecker.checkFirstEntry();
+ test();
+ }
+
+ public void atTheEnd() {
+ if (getSubject().getEntries().isEmpty()) {
+ fail("No entries found.");
+ }
+ mChecker.checkLastEntry();
+ test();
+ }
+
+ private void test() {
+ List<Result> failures = mChecker.test(getSubject().getEntries());
+ if (!failures.isEmpty()) {
+ Optional<Path> failureTracePath = getSubject().getSource();
+ String failureLogs = failures.stream().map(Result::toString)
+ .collect(Collectors.joining("\n"));
+ String tracePath = "";
+ if (failureTracePath.isPresent()) {
+ tracePath = "\nWindowManager Trace can be found in: "
+ + failureTracePath.get().toAbsolutePath() + "\n";
+ }
+ fail(tracePath + failureLogs);
+ }
+ }
+
+ public WmTraceSubject showsAboveAppWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle),
+ "showsAboveAppWindow(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject hidesAboveAppWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle).negate(),
+ "hidesAboveAppWindow" + "(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject showsBelowAppWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle),
+ "showsBelowAppWindow(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject hidesBelowAppWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle).negate(),
+ "hidesBelowAppWindow" + "(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject showsImeWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle),
+ "showsBelowAppWindow(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject hidesImeWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle).negate(),
+ "hidesImeWindow" + "(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject showsAppWindowOnTop(String partialWindowTitle) {
+ mChecker.add(
+ entry -> {
+ Result result = entry.isAppWindowVisible(partialWindowTitle);
+ if (result.passed()) {
+ result = entry.isVisibleAppWindowOnTop(partialWindowTitle);
+ }
+ return result;
+ },
+ "showsAppWindowOnTop(" + partialWindowTitle + ")"
+ );
+ return this;
+ }
+
+ public WmTraceSubject hidesAppWindowOnTop(String partialWindowTitle) {
+ mChecker.add(
+ entry -> {
+ Result result = entry.isAppWindowVisible(partialWindowTitle).negate();
+ if (result.failed()) {
+ result = entry.isVisibleAppWindowOnTop(partialWindowTitle).negate();
+ }
+ return result;
+ },
+ "hidesAppWindowOnTop(" + partialWindowTitle + ")"
+ );
+ return this;
+ }
+
+ public WmTraceSubject showsAppWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle),
+ "showsAppWindow(" + partialWindowTitle + ")");
+ return this;
+ }
+
+ public WmTraceSubject hidesAppWindow(String partialWindowTitle) {
+ mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle).negate(),
+ "hidesAppWindow(" + partialWindowTitle + ")");
+ return this;
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
new file mode 100644
index 000000000000..67e0ecc1cde7
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import android.os.Environment;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Collects test artifacts during a UI transition.
+ */
+public interface ITransitionMonitor {
+ Path OUTPUT_DIR = Paths.get(Environment.getExternalStorageDirectory().toString(), "flicker");
+
+ /**
+ * Starts monitor.
+ */
+ void start();
+
+ /**
+ * Stops monitor.
+ */
+ void stop();
+
+ /**
+ * Saves any monitor artifacts to file adding {@code testTag} and {@code iteration}
+ * to the file name.
+ *
+ * @param testTag suffix added to artifact name
+ * @param iteration suffix added to artifact name
+ *
+ * @return Path to saved artifact
+ */
+ default Path save(String testTag, int iteration) {
+ return save(testTag + "_" + iteration);
+ }
+
+ /**
+ * Saves any monitor artifacts to file adding {@code testTag} to the file name.
+ *
+ * @param testTag suffix added to artifact name
+ *
+ * @return Path to saved artifact
+ */
+ default Path save(String testTag) {
+ throw new UnsupportedOperationException("Save not implemented for this monitor");
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
new file mode 100644
index 000000000000..c55d068b41b8
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Captures Layers trace from SurfaceFlinger.
+ */
+public class LayersTraceMonitor extends TraceMonitor {
+ private static final String TAG = "LayersTraceMonitor";
+ private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+
+ public LayersTraceMonitor() {
+ traceFileName = "layers_trace.pb";
+ }
+
+ @Override
+ public void start() {
+ setEnabled(true);
+ }
+
+ @Override
+ public void stop() {
+ setEnabled(false);
+ }
+
+ @Override
+ public boolean isEnabled() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026,
+ data, reply, 0 /* flags */);
+ return reply.readBoolean();
+ }
+
+ private void setEnabled(boolean isEnabled) {
+ Parcel data = null;
+ try {
+ if (mSurfaceFlinger != null) {
+ data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(isEnabled ? 1 : 0);
+ mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025,
+ data, null, 0 /* flags */);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not set layer tracing." + e.toString());
+ } finally {
+ if (data != null) {
+ data.recycle();
+ }
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
new file mode 100644
index 000000000000..4787586777ae
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Captures screen contents and saves it as a mp4 video file.
+ */
+public class ScreenRecorder implements ITransitionMonitor {
+ @VisibleForTesting
+ static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
+ private static final String TAG = "FLICKER";
+ private Thread recorderThread;
+
+ @VisibleForTesting
+ static Path getPath(String testTag) {
+ return OUTPUT_DIR.resolve(testTag + ".mp4");
+ }
+
+ @Override
+ public void start() {
+ OUTPUT_DIR.toFile().mkdirs();
+ String command = "screenrecord " + DEFAULT_OUTPUT_PATH;
+ recorderThread = new Thread(() -> {
+ try {
+ Runtime.getRuntime().exec(command);
+ } catch (IOException e) {
+ Log.e(TAG, "Error executing " + command, e);
+ }
+ });
+ recorderThread.start();
+ }
+
+ @Override
+ public void stop() {
+ runShellCommand("killall -s 2 screenrecord");
+ try {
+ recorderThread.join();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ @Override
+ public Path save(String testTag) {
+ try {
+ return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
+ REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
new file mode 100644
index 000000000000..0e154ecd5d4d
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+/**
+ * Base class for monitors containing common logic to read the trace
+ * as a byte array and save the trace to another location.
+ */
+public abstract class TraceMonitor implements ITransitionMonitor {
+ public static final String TAG = "FLICKER";
+ private static final String TRACE_DIR = "/data/misc/wmtrace/";
+
+ String traceFileName;
+
+ abstract boolean isEnabled() throws RemoteException;
+
+ /**
+ * Saves trace file to the external storage directory suffixing the name with the testtag
+ * and iteration.
+ *
+ * Moves the trace file from the default location via a shell command since the test app
+ * does not have security privileges to access /data/misc/wmtrace.
+ *
+ * @param testTag suffix added to trace name used to identify trace
+ *
+ * @return Path to saved trace file
+ */
+ @Override
+ public Path save(String testTag) {
+ OUTPUT_DIR.toFile().mkdirs();
+ Path traceFileCopy = getOutputTraceFilePath(testTag);
+ String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
+ traceFileName, traceFileCopy.toString());
+ runShellCommand(copyCommand);
+ return traceFileCopy;
+ }
+
+ @VisibleForTesting
+ Path getOutputTraceFilePath(String testTag) {
+ return OUTPUT_DIR.resolve(traceFileName + "_" + testTag);
+ }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
new file mode 100644
index 000000000000..717d187e1d4a
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static android.view.FrameStats.UNDEFINED_TIME_NANO;
+
+import android.app.Instrumentation;
+import android.util.Log;
+import android.view.FrameStats;
+
+/**
+ * Monitors {@link android.view.WindowAnimationFrameStats} to detect janky frames.
+ *
+ * Adapted from {@link android.support.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
+ * using the same threshold to determine jank.
+ */
+public class WindowAnimationFrameStatsMonitor implements ITransitionMonitor {
+
+ private static final String TAG = "FLICKER";
+ // Maximum normalized error in frame duration before the frame is considered janky
+ private static final double MAX_ERROR = 0.5f;
+ // Maximum normalized frame duration before the frame is considered a pause
+ private static final double PAUSE_THRESHOLD = 15.0f;
+ private Instrumentation mInstrumentation;
+ private FrameStats stats;
+ private int numJankyFrames;
+ private long mLongestFrameNano = 0L;
+
+
+ /**
+ * Constructs a WindowAnimationFrameStatsMonitor instance.
+ */
+ public WindowAnimationFrameStatsMonitor(Instrumentation instrumentation) {
+ mInstrumentation = instrumentation;
+ }
+
+ private void analyze() {
+ int frameCount = stats.getFrameCount();
+ long refreshPeriodNano = stats.getRefreshPeriodNano();
+
+ // Skip first frame
+ for (int i = 2; i < frameCount; i++) {
+ // Handle frames that have not been presented.
+ if (stats.getFramePresentedTimeNano(i) == UNDEFINED_TIME_NANO) {
+ // The animation must not have completed. Warn and break out of the loop.
+ Log.w(TAG, "Skipping fenced frame.");
+ break;
+ }
+ long frameDurationNano = stats.getFramePresentedTimeNano(i) -
+ stats.getFramePresentedTimeNano(i - 1);
+ double normalized = (double) frameDurationNano / refreshPeriodNano;
+ if (normalized < PAUSE_THRESHOLD) {
+ if (normalized > 1.0f + MAX_ERROR) {
+ numJankyFrames++;
+ }
+ mLongestFrameNano = Math.max(mLongestFrameNano, frameDurationNano);
+ }
+ }
+ }
+
+ @Override
+ public void start() {
+ // Clear out any previous data
+ numJankyFrames = 0;
+ mLongestFrameNano = 0;
+ mInstrumentation.getUiAutomation().clearWindowAnimationFrameStats();
+ }
+
+ @Override
+ public void stop() {
+ stats = mInstrumentation.getUiAutomation().getWindowAnimationFrameStats();
+ analyze();
+ }
+
+ public boolean jankyFramesDetected() {
+ return stats.getFrameCount() > 0 && numJankyFrames > 0;
+ }
+
+ @Override
+ public String toString() {
+ return stats.toString() +
+ " RefreshPeriodNano:" + stats.getRefreshPeriodNano() +
+ " NumJankyFrames:" + numJankyFrames +
+ " LongestFrameNano:" + mLongestFrameNano;
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
new file mode 100644
index 000000000000..ae160b68c976
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import android.os.RemoteException;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Captures WindowManager trace from WindowManager.
+ */
+public class WindowManagerTraceMonitor extends TraceMonitor {
+ private IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+ public WindowManagerTraceMonitor() {
+ traceFileName = "wm_trace.pb";
+ }
+
+ @Override
+ public void start() {
+ try {
+ wm.startWindowTrace();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not start trace", e);
+ }
+ }
+
+ @Override
+ public void stop() {
+ try {
+ wm.stopWindowTrace();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not stop trace", e);
+ }
+ }
+
+ @Override
+ public boolean isEnabled() throws RemoteException{
+ return wm.isWindowTraceEnabled();
+ }
+}
diff --git a/tests/FlickerTests/lib/test/Android.mk b/tests/FlickerTests/lib/test/Android.mk
new file mode 100644
index 000000000000..0e3f58d8a8c9
--- /dev/null
+++ b/tests/FlickerTests/lib/test/Android.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FlickerLibTest
+LOCAL_MODULE_TAGS := tests optional
+# sign this with platform cert, so this test is allowed to call private platform apis
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ platform-test-annotations \
+ truth-prebuilt \
+ platformprotosnano \
+ layersprotosnano \
+ flickerlib
+
+include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidManifest.xml b/tests/FlickerTests/lib/test/AndroidManifest.xml
new file mode 100644
index 000000000000..d30172d56c2c
--- /dev/null
+++ b/tests/FlickerTests/lib/test/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.wm.flicker">
+
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <application android:label="FlickerLibTest">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker"
+ android:label="WindowManager Flicker Lib Test">
+ </instrumentation>
+
+</manifest> \ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidTest.xml b/tests/FlickerTests/lib/test/AndroidTest.xml
new file mode 100644
index 000000000000..e4cc298a2aa8
--- /dev/null
+++ b/tests/FlickerTests/lib/test/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Config for WindowManager Flicker Tests">
+ <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on" />
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="FlickerLibTest.apk"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.wm.flicker"/>
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
new file mode 100644
index 000000000000..98ee6f3ed269
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
new file mode 100644
index 000000000000..20572d79d826
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
new file mode 100644
index 000000000000..af4079707c69
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
new file mode 100644
index 000000000000..b3f31706f55c
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
new file mode 100644
index 000000000000..b3b73ce0518a
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
new file mode 100644
index 000000000000..8e7fe1b4f942
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains {@link AssertionsChecker} tests.
+ * To run this test: {@code atest FlickerLibTest:AssertionsCheckerTest}
+ */
+public class AssertionsCheckerTest {
+
+ /**
+ * Returns a list of SimpleEntry objects with {@code data} and incremental timestamps starting
+ * at 0.
+ */
+ private static List<SimpleEntry> getTestEntries(int... data) {
+ List<SimpleEntry> entries = new ArrayList<>();
+ for (int i = 0; i < data.length; i++) {
+ entries.add(new SimpleEntry(i, data[i]));
+ }
+ return entries;
+ }
+
+ @Test
+ public void canCheckAllEntries() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.add(SimpleEntry::isData42, "isData42");
+
+ List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+ assertThat(failures).hasSize(5);
+ }
+
+ @Test
+ public void canCheckFirstEntry() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.checkFirstEntry();
+ checker.add(SimpleEntry::isData42, "isData42");
+
+ List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+ assertThat(failures).hasSize(1);
+ assertThat(failures.get(0).timestamp).isEqualTo(0);
+ }
+
+ @Test
+ public void canCheckLastEntry() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.checkLastEntry();
+ checker.add(SimpleEntry::isData42, "isData42");
+
+ List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+ assertThat(failures).hasSize(1);
+ assertThat(failures.get(0).timestamp).isEqualTo(4);
+ }
+
+ @Test
+ public void canCheckRangeOfEntries() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.filterByRange(1, 2);
+ checker.add(SimpleEntry::isData42, "isData42");
+
+ List<Result> failures = checker.test(getTestEntries(1, 42, 42, 1, 1));
+
+ assertThat(failures).hasSize(0);
+ }
+
+ @Test
+ public void emptyRangePasses() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.filterByRange(9, 10);
+ checker.add(SimpleEntry::isData42, "isData42");
+
+ List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+ assertThat(failures).isEmpty();
+ }
+
+ @Test
+ public void canCheckChangingAssertions() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.add(SimpleEntry::isData42, "isData42");
+ checker.add(SimpleEntry::isData0, "isData0");
+ checker.checkChangingAssertions();
+
+ List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
+
+ assertThat(failures).isEmpty();
+ }
+
+ @Test
+ public void canCheckChangingAssertions_withNoAssertions() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.checkChangingAssertions();
+
+ List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
+
+ assertThat(failures).isEmpty();
+ }
+
+ @Test
+ public void canCheckChangingAssertions_withSingleAssertion() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.add(SimpleEntry::isData42, "isData42");
+ checker.checkChangingAssertions();
+
+ List<Result> failures = checker.test(getTestEntries(42, 42, 42, 42, 42));
+
+ assertThat(failures).isEmpty();
+ }
+
+ @Test
+ public void canFailCheckChangingAssertions_ifStartingAssertionFails() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.add(SimpleEntry::isData42, "isData42");
+ checker.add(SimpleEntry::isData0, "isData0");
+ checker.checkChangingAssertions();
+
+ List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
+
+ assertThat(failures).hasSize(1);
+ }
+
+ @Test
+ public void canFailCheckChangingAssertions_ifStartingAssertionAlwaysPasses() {
+ AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+ checker.add(SimpleEntry::isData42, "isData42");
+ checker.add(SimpleEntry::isData0, "isData0");
+ checker.checkChangingAssertions();
+
+ List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
+
+ assertThat(failures).hasSize(1);
+ }
+
+ static class SimpleEntry implements ITraceEntry {
+ long timestamp;
+ int data;
+
+ SimpleEntry(long timestamp, int data) {
+ this.timestamp = timestamp;
+ this.data = data;
+ }
+
+ @Override
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ Result isData42() {
+ return new Result(this.data == 42, this.timestamp, "is42", "");
+ }
+
+ Result isData0() {
+ return new Result(this.data == 0, this.timestamp, "is42", "");
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
new file mode 100644
index 000000000000..7fd178ca6e51
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Test;
+
+/**
+ * Contains {@link Assertions} tests.
+ * To run this test: {@code atest FlickerLibTest:AssertionsTest}
+ */
+public class AssertionsTest {
+ @Test
+ public void traceEntryAssertionCanNegateResult() {
+ Assertions.TraceAssertion<Integer> assertNumEquals42 =
+ getIntegerTraceEntryAssertion();
+
+ assertThat(assertNumEquals42.apply(1).success).isFalse();
+ assertThat(assertNumEquals42.negate().apply(1).success).isTrue();
+
+ assertThat(assertNumEquals42.apply(42).success).isTrue();
+ assertThat(assertNumEquals42.negate().apply(42).success).isFalse();
+ }
+
+ @Test
+ public void resultCanBeNegated() {
+ String reason = "Everything is fine!";
+ Result result = new Result(true, 0, "TestAssert", reason);
+ Result negatedResult = result.negate();
+ assertThat(negatedResult.success).isFalse();
+ assertThat(negatedResult.reason).isEqualTo(reason);
+ assertThat(negatedResult.assertionName).isEqualTo("!TestAssert");
+ }
+
+ private Assertions.TraceAssertion<Integer> getIntegerTraceEntryAssertion() {
+ return (num) -> {
+ if (num == 42) {
+ return new Result(true, "Num equals 42");
+ }
+ return new Result(false, "Num doesn't equal 42, actual:" + num);
+ };
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
new file mode 100644
index 000000000000..d06c5d76552b
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.LayersTraceSubject.assertThat;
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.Rect;
+
+import org.junit.Test;
+
+import java.nio.file.Paths;
+
+/**
+ * Contains {@link LayersTraceSubject} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceSubjectTest}
+ */
+public class LayersTraceSubjectTest {
+ private static final Rect displayRect = new Rect(0, 0, 1440, 2880);
+
+ private static LayersTrace readLayerTraceFromFile(String relativePath) {
+ try {
+ return LayersTrace.parseFrom(readTestFile(relativePath), Paths.get(relativePath));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testCanDetectEmptyRegionFromLayerTrace() {
+ LayersTrace layersTraceEntries = readLayerTraceFromFile("layers_trace_emptyregion.pb");
+ try {
+ assertThat(layersTraceEntries).coversRegion(displayRect).forAllEntries();
+ fail("Assertion passed");
+ } catch (AssertionError e) {
+ assertWithMessage("Contains path to trace")
+ .that(e.getMessage()).contains("layers_trace_emptyregion.pb");
+ assertWithMessage("Contains timestamp")
+ .that(e.getMessage()).contains("0h38m28s8ms");
+ assertWithMessage("Contains assertion function")
+ .that(e.getMessage()).contains("coversRegion");
+ assertWithMessage("Contains debug info")
+ .that(e.getMessage()).contains("Region to test: " + displayRect);
+ assertWithMessage("Contains debug info")
+ .that(e.getMessage()).contains("first empty point: 0, 99");
+ }
+ }
+
+ @Test
+ public void testCanDetectIncorrectVisibilityFromLayerTrace() {
+ LayersTrace layersTraceEntries = readLayerTraceFromFile(
+ "layers_trace_invalid_layer_visibility.pb");
+ try {
+ assertThat(layersTraceEntries).showsLayer("com.android.server.wm.flicker.testapp")
+ .then().hidesLayer("com.android.server.wm.flicker.testapp").forAllEntries();
+ fail("Assertion passed");
+ } catch (AssertionError e) {
+ assertWithMessage("Contains path to trace")
+ .that(e.getMessage()).contains("layers_trace_invalid_layer_visibility.pb");
+ assertWithMessage("Contains timestamp")
+ .that(e.getMessage()).contains("70h13m14s303ms");
+ assertWithMessage("Contains assertion function")
+ .that(e.getMessage()).contains("!isVisible");
+ assertWithMessage("Contains debug info")
+ .that(e.getMessage()).contains(
+ "com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp"
+ + ".SimpleActivity#0 is visible");
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
new file mode 100644
index 000000000000..42b2acace8c9
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.view.WindowManager;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Contains {@link LayersTrace} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceTest}
+ */
+public class LayersTraceTest {
+ private static LayersTrace readLayerTraceFromFile(String relativePath) {
+ try {
+ return LayersTrace.parseFrom(readTestFile(relativePath));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Rect getDisplayBounds() {
+ Point display = new Point();
+ WindowManager wm =
+ (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+ Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getRealSize(display);
+ return new Rect(0, 0, display.x, display.y);
+ }
+
+ @Test
+ public void canParseAllLayers() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ assertThat(trace.getEntries()).isNotEmpty();
+ assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+ assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+ .isEqualTo(2308521813510L);
+ List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
+ String msg = "Layers:\n" + flattenedLayers.stream().map(layer -> layer.mProto.name)
+ .collect(Collectors.joining("\n\t"));
+ assertWithMessage(msg).that(flattenedLayers).hasSize(47);
+ }
+
+ @Test
+ public void canParseVisibleLayers() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ assertThat(trace.getEntries()).isNotEmpty();
+ assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+ assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+ .isEqualTo(2308521813510L);
+ List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
+ List<LayersTrace.Layer> visibleLayers = flattenedLayers.stream()
+ .filter(layer -> layer.isVisible() && !layer.isHiddenByParent())
+ .collect(Collectors.toList());
+
+ String msg = "Visible Layers:\n" + visibleLayers.stream()
+ .map(layer -> layer.mProto.name)
+ .collect(Collectors.joining("\n\t"));
+
+ assertWithMessage(msg).that(visibleLayers).hasSize(9);
+ }
+
+ @Test
+ public void canParseLayerHierarchy() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ assertThat(trace.getEntries()).isNotEmpty();
+ assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+ assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+ .isEqualTo(2308521813510L);
+ List<LayersTrace.Layer> layers = trace.getEntries().get(0).getRootLayers();
+ assertThat(layers).hasSize(2);
+ assertThat(layers.get(0).mChildren).hasSize(layers.get(0).mProto.children.length);
+ assertThat(layers.get(1).mChildren).hasSize(layers.get(1).mProto.children.length);
+ }
+
+ // b/76099859
+ @Test
+ public void canDetectOrphanLayers() {
+ try {
+ readLayerTraceFromFile(
+ "layers_trace_orphanlayers.pb");
+ fail("Failed to detect orphaned layers.");
+ } catch (RuntimeException exception) {
+ assertThat(exception.getMessage()).contains(
+ "Failed to parse layers trace. Found orphan layers "
+ + "with parent layer id:1006 : 49");
+ }
+ }
+
+ // b/75276931
+ @Test
+ public void canDetectUncoveredRegion() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+ Assertions.Result result = entry.coversRegion(getDisplayBounds());
+
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains("Region to test: Rect(0, 0 - 1440, 2880)");
+ assertThat(result.reason).contains("first empty point: 0, 99");
+ assertThat(result.reason).contains("visible regions:");
+ assertWithMessage("Reason contains list of visible regions")
+ .that(result.reason).contains("StatusBar#0Rect(0, 0 - 1440, 98");
+ }
+
+ // Visible region tests
+ @Test
+ public void canTestLayerVisibleRegion_layerDoesNotExist() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+ final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+ Assertions.Result result = entry.hasVisibleRegion("ImaginaryLayer",
+ expectedVisibleRegion);
+
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains("Could not find ImaginaryLayer");
+ }
+
+ @Test
+ public void canTestLayerVisibleRegion_layerDoesNotHaveExpectedVisibleRegion() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ LayersTrace.Entry entry = trace.getEntry(2307993020072L);
+
+ final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+ Assertions.Result result = entry.hasVisibleRegion("NexusLauncherActivity#2",
+ expectedVisibleRegion);
+
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains(
+ "Layer com.google.android.apps.nexuslauncher/com.google.android.apps"
+ + ".nexuslauncher.NexusLauncherActivity#2 is invisible: activeBuffer=null"
+ + " type != ColorLayer flags=1 (FLAG_HIDDEN set) visible region is empty");
+ }
+
+ @Test
+ public void canTestLayerVisibleRegion_layerIsHiddenByParent() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ LayersTrace.Entry entry = trace.getEntry(2308455948035L);
+
+ final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+ Assertions.Result result = entry.hasVisibleRegion(
+ "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main",
+ expectedVisibleRegion);
+
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains(
+ "Layer SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0 is "
+ + "hidden by parent: com.android.chrome/com.google.android.apps.chrome"
+ + ".Main#0");
+ }
+
+ @Test
+ public void canTestLayerVisibleRegion_incorrectRegionSize() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+ final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 99);
+ Assertions.Result result = entry.hasVisibleRegion(
+ "StatusBar",
+ expectedVisibleRegion);
+
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains("StatusBar#0 has visible "
+ + "region:Rect(0, 0 - 1440, 98) expected:Rect(0, 0 - 1440, 99)");
+ }
+
+ @Test
+ public void canTestLayerVisibleRegion() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_emptyregion.pb");
+ LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+ final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 98);
+ Assertions.Result result = entry.hasVisibleRegion("StatusBar", expectedVisibleRegion);
+
+ assertThat(result.passed()).isTrue();
+ }
+
+ @Test
+ public void canTestLayerVisibleRegion_layerIsNotVisible() {
+ LayersTrace trace = readLayerTraceFromFile(
+ "layers_trace_invalid_layer_visibility.pb");
+ LayersTrace.Entry entry = trace.getEntry(252794268378458L);
+
+ Assertions.Result result = entry.isVisible("com.android.server.wm.flicker.testapp");
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains(
+ "Layer com.android.server.wm.flicker.testapp/com.android.server.wm.flicker"
+ + ".testapp.SimpleActivity#0 is invisible: type != ColorLayer visible "
+ + "region is empty");
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
new file mode 100644
index 000000000000..5a24e6d91595
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+
+import com.google.common.io.ByteStreams;
+
+import java.io.InputStream;
+
+/**
+ * Helper functions for test file resources.
+ */
+class TestFileUtils {
+ static byte[] readTestFile(String relativePath) throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ InputStream in = context.getResources().getAssets().open("testdata/" + relativePath);
+ return ByteStreams.toByteArray(in);
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
new file mode 100644
index 000000000000..9c5e2059a0e6
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.Environment;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
+import com.android.server.wm.flicker.monitor.ScreenRecorder;
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+
+/**
+ * Contains {@link TransitionRunner} tests.
+ * {@code atest FlickerLibTest:TransitionRunnerTest}
+ */
+public class TransitionRunnerTest {
+ @Mock
+ private SimpleUiTransitions mTransitionsMock;
+ @Mock
+ private ScreenRecorder mScreenRecorderMock;
+ @Mock
+ private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
+ @Mock
+ private LayersTraceMonitor mLayersTraceMonitorMock;
+ @Mock
+ private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
+ @InjectMocks
+ private TransitionBuilder mTransitionBuilder;
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void transitionsRunInOrder() {
+ TransitionRunner.newBuilder()
+ .runBeforeAll(mTransitionsMock::turnOnDevice)
+ .runBefore(mTransitionsMock::openApp)
+ .run(mTransitionsMock::performMagic)
+ .runAfter(mTransitionsMock::closeApp)
+ .runAfterAll(mTransitionsMock::cleanUpTracks)
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .build()
+ .run();
+
+ InOrder orderVerifier = inOrder(mTransitionsMock);
+ orderVerifier.verify(mTransitionsMock).turnOnDevice();
+ orderVerifier.verify(mTransitionsMock).openApp();
+ orderVerifier.verify(mTransitionsMock).performMagic();
+ orderVerifier.verify(mTransitionsMock).closeApp();
+ orderVerifier.verify(mTransitionsMock).cleanUpTracks();
+ }
+
+ @Test
+ public void canCombineTransitions() {
+ TransitionRunner.newBuilder()
+ .runBeforeAll(mTransitionsMock::turnOnDevice)
+ .runBeforeAll(mTransitionsMock::turnOnDevice)
+ .runBefore(mTransitionsMock::openApp)
+ .runBefore(mTransitionsMock::openApp)
+ .run(mTransitionsMock::performMagic)
+ .run(mTransitionsMock::performMagic)
+ .runAfter(mTransitionsMock::closeApp)
+ .runAfter(mTransitionsMock::closeApp)
+ .runAfterAll(mTransitionsMock::cleanUpTracks)
+ .runAfterAll(mTransitionsMock::cleanUpTracks)
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .build()
+ .run();
+
+ final int wantedNumberOfInvocations = 2;
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).turnOnDevice();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).cleanUpTracks();
+ }
+
+ @Test
+ public void emptyTransitionPasses() {
+ List<TransitionResult> results = TransitionRunner.newBuilder()
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .build()
+ .run()
+ .getResults();
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).layersTraceExists()).isFalse();
+ assertThat(results.get(0).windowManagerTraceExists()).isFalse();
+ assertThat(results.get(0).screenCaptureVideoExists()).isFalse();
+ }
+
+ @Test
+ public void canRepeatTransitions() {
+ final int wantedNumberOfInvocations = 10;
+ TransitionRunner.newBuilder()
+ .runBeforeAll(mTransitionsMock::turnOnDevice)
+ .runBefore(mTransitionsMock::openApp)
+ .run(mTransitionsMock::performMagic)
+ .runAfter(mTransitionsMock::closeApp)
+ .runAfterAll(mTransitionsMock::cleanUpTracks)
+ .repeat(wantedNumberOfInvocations)
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .build()
+ .run();
+ verify(mTransitionsMock).turnOnDevice();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
+ verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
+ verify(mTransitionsMock).cleanUpTracks();
+ }
+
+ private void emptyTask() {
+
+ }
+
+ @Test
+ public void canCaptureWindowManagerTrace() {
+ mTransitionBuilder
+ .run(this::emptyTask)
+ .includeJankyRuns()
+ .skipLayersTrace()
+ .withTag("mCaptureWmTraceTransitionRunner")
+ .build().run();
+ InOrder orderVerifier = inOrder(mWindowManagerTraceMonitorMock);
+ orderVerifier.verify(mWindowManagerTraceMonitorMock).start();
+ orderVerifier.verify(mWindowManagerTraceMonitorMock).stop();
+ orderVerifier.verify(mWindowManagerTraceMonitorMock)
+ .save("mCaptureWmTraceTransitionRunner", 0);
+ verifyNoMoreInteractions(mWindowManagerTraceMonitorMock);
+ }
+
+ @Test
+ public void canCaptureLayersTrace() {
+ mTransitionBuilder
+ .run(this::emptyTask)
+ .includeJankyRuns()
+ .skipWindowManagerTrace()
+ .withTag("mCaptureLayersTraceTransitionRunner")
+ .build().run();
+ InOrder orderVerifier = inOrder(mLayersTraceMonitorMock);
+ orderVerifier.verify(mLayersTraceMonitorMock).start();
+ orderVerifier.verify(mLayersTraceMonitorMock).stop();
+ orderVerifier.verify(mLayersTraceMonitorMock)
+ .save("mCaptureLayersTraceTransitionRunner", 0);
+ verifyNoMoreInteractions(mLayersTraceMonitorMock);
+ }
+
+ @Test
+ public void canRecordEachRun() throws IOException {
+ mTransitionBuilder
+ .run(this::emptyTask)
+ .withTag("mRecordEachRun")
+ .recordEachRun()
+ .includeJankyRuns()
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .repeat(2)
+ .build().run();
+ InOrder orderVerifier = inOrder(mScreenRecorderMock);
+ orderVerifier.verify(mScreenRecorderMock).start();
+ orderVerifier.verify(mScreenRecorderMock).stop();
+ orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 0);
+ orderVerifier.verify(mScreenRecorderMock).start();
+ orderVerifier.verify(mScreenRecorderMock).stop();
+ orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 1);
+ verifyNoMoreInteractions(mScreenRecorderMock);
+ }
+
+ @Test
+ public void canRecordAllRuns() throws IOException {
+ doReturn(Paths.get(Environment.getExternalStorageDirectory().getAbsolutePath(),
+ "mRecordAllRuns.mp4")).when(mScreenRecorderMock).save("mRecordAllRuns");
+ mTransitionBuilder
+ .run(this::emptyTask)
+ .recordAllRuns()
+ .includeJankyRuns()
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .withTag("mRecordAllRuns")
+ .repeat(2)
+ .build().run();
+ InOrder orderVerifier = inOrder(mScreenRecorderMock);
+ orderVerifier.verify(mScreenRecorderMock).start();
+ orderVerifier.verify(mScreenRecorderMock).stop();
+ orderVerifier.verify(mScreenRecorderMock).save("mRecordAllRuns");
+ verifyNoMoreInteractions(mScreenRecorderMock);
+ }
+
+ @Test
+ public void canSkipJankyRuns() {
+ doReturn(false).doReturn(true).doReturn(false)
+ .when(mWindowAnimationFrameStatsMonitor).jankyFramesDetected();
+ List<TransitionResult> results = mTransitionBuilder
+ .run(this::emptyTask)
+ .skipLayersTrace()
+ .skipWindowManagerTrace()
+ .repeat(3)
+ .build().run().getResults();
+ assertThat(results).hasSize(2);
+ }
+
+ public static class SimpleUiTransitions {
+ public void turnOnDevice() {
+ }
+
+ public void openApp() {
+ }
+
+ public void performMagic() {
+ }
+
+ public void closeApp() {
+ }
+
+ public void cleanUpTracks() {
+ }
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
new file mode 100644
index 000000000000..49278718932c
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Contains {@link WindowManagerTrace} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowManagerTraceTest}
+ */
+public class WindowManagerTraceTest {
+ private WindowManagerTrace mTrace;
+
+ private static WindowManagerTrace readWindowManagerTraceFromFile(String relativePath) {
+ try {
+ return WindowManagerTrace.parseFrom(readTestFile(relativePath));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Before
+ public void setup() {
+ mTrace = readWindowManagerTraceFromFile("wm_trace_openchrome.pb");
+ }
+
+ @Test
+ public void canParseAllEntries() {
+ assertThat(mTrace.getEntries().get(0).getTimestamp()).isEqualTo(241777211939236L);
+ assertThat(mTrace.getEntries().get(mTrace.getEntries().size() - 1).getTimestamp()).isEqualTo
+ (241779809471942L);
+ }
+
+ @Test
+ public void canDetectAboveAppWindowVisibility() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+ Result result = entry.isAboveAppWindowVisible("NavigationBar");
+ assertThat(result.passed()).isTrue();
+ }
+
+ @Test
+ public void canDetectBelowAppWindowVisibility() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+ Result result = entry.isBelowAppWindowVisible("wallpaper");
+ assertThat(result.passed()).isTrue();
+ }
+
+ @Test
+ public void canDetectAppWindowVisibility() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+ Result result = entry.isAppWindowVisible("com.google.android.apps.nexuslauncher");
+ assertThat(result.passed()).isTrue();
+ }
+
+ @Test
+ public void canFailWithReasonForVisibilityChecks_windowNotFound() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+ Result result = entry.isAboveAppWindowVisible("ImaginaryWindow");
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains("ImaginaryWindow cannot be found");
+ }
+
+ @Test
+ public void canFailWithReasonForVisibilityChecks_windowNotVisible() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+ Result result = entry.isAboveAppWindowVisible("AssistPreviewPanel");
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains("AssistPreviewPanel is invisible");
+ }
+
+ @Test
+ public void canDetectAppZOrder() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
+ Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.chrome");
+ assertThat(result.passed()).isTrue();
+ }
+
+ @Test
+ public void canFailWithReasonForZOrderChecks_windowNotOnTop() {
+ WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
+ Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.nexuslauncher");
+ assertThat(result.failed()).isTrue();
+ assertThat(result.reason).contains("wanted=com.google.android.apps.nexuslauncher");
+ assertThat(result.reason).contains("found=com.android.chrome/"
+ + "com.google.android.apps.chrome.Main");
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
new file mode 100644
index 000000000000..d547a188a663
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import org.junit.Test;
+
+/**
+ * Contains {@link WmTraceSubject} tests.
+ * To run this test: {@code atest FlickerLibTest:WmTraceSubjectTest}
+ */
+public class WmTraceSubjectTest {
+ private static WindowManagerTrace readWmTraceFromFile(String relativePath) {
+ try {
+ return WindowManagerTrace.parseFrom(readTestFile(relativePath));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void testCanTransitionInAppWindow() {
+ WindowManagerTrace trace = readWmTraceFromFile("wm_trace_openchrome2.pb");
+
+ assertThat(trace).showsAppWindowOnTop("com.google.android.apps.nexuslauncher/"
+ + ".NexusLauncherActivity").forRange(174684850717208L, 174685957511016L);
+ assertThat(trace).showsAppWindowOnTop(
+ "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+ .then()
+ .showsAppWindowOnTop("com.android.chrome")
+ .forAllEntries();
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
new file mode 100644
index 000000000000..dbd6761a05b0
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_H;
+import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_L;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Contains {@link LayersTraceMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceMonitorTest}
+ */
+public class LayersTraceMonitorTest {
+ private LayersTraceMonitor mLayersTraceMonitor;
+
+ @Before
+ public void setup() {
+ mLayersTraceMonitor = new LayersTraceMonitor();
+ }
+
+ @After
+ public void teardown() {
+ mLayersTraceMonitor.stop();
+ mLayersTraceMonitor.getOutputTraceFilePath("captureLayersTrace").toFile().delete();
+ }
+
+ @Test
+ public void canStartLayersTrace() throws Exception {
+ mLayersTraceMonitor.start();
+ assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void canStopLayersTrace() throws Exception {
+ mLayersTraceMonitor.start();
+ assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
+ mLayersTraceMonitor.stop();
+ assertThat(mLayersTraceMonitor.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void captureLayersTrace() throws Exception {
+ mLayersTraceMonitor.start();
+ mLayersTraceMonitor.stop();
+ File testFile = mLayersTraceMonitor.save("captureLayersTrace").toFile();
+ assertThat(testFile.exists()).isTrue();
+ byte[] trace = Files.toByteArray(testFile);
+ assertThat(trace.length).isGreaterThan(0);
+ LayersTraceFileProto mLayerTraceFileProto = LayersTraceFileProto.parseFrom(trace);
+ assertThat(mLayerTraceFileProto.magicNumber).isEqualTo(
+ (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
new file mode 100644
index 000000000000..e73eecc348f0
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static android.os.SystemClock.sleep;
+
+import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
+import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Contains {@link ScreenRecorder} tests.
+ * To run this test: {@code atest FlickerLibTest:ScreenRecorderTest}
+ */
+public class ScreenRecorderTest {
+ private static final String TEST_VIDEO_FILENAME = "test.mp4";
+ private ScreenRecorder mScreenRecorder;
+
+ @Before
+ public void setup() {
+ mScreenRecorder = new ScreenRecorder();
+ }
+
+ @After
+ public void teardown() {
+ DEFAULT_OUTPUT_PATH.toFile().delete();
+ getPath(TEST_VIDEO_FILENAME).toFile().delete();
+ }
+
+ @Test
+ public void videoIsRecorded() {
+ mScreenRecorder.start();
+ sleep(100);
+ mScreenRecorder.stop();
+ File file = DEFAULT_OUTPUT_PATH.toFile();
+ assertThat(file.exists()).isTrue();
+ }
+
+ @Test
+ public void videoCanBeSaved() {
+ mScreenRecorder.start();
+ sleep(100);
+ mScreenRecorder.stop();
+ mScreenRecorder.save(TEST_VIDEO_FILENAME);
+ File file = getPath(TEST_VIDEO_FILENAME).toFile();
+ assertThat(file.exists()).isTrue();
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
new file mode 100644
index 000000000000..f7fa0d572de6
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Contains {@link WindowAnimationFrameStatsMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowAnimationFrameStatsMonitorTest}
+ */
+public class WindowAnimationFrameStatsMonitorTest {
+ private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
+
+ @Before
+ public void setup() {
+ mWindowAnimationFrameStatsMonitor = new WindowAnimationFrameStatsMonitor(
+ InstrumentationRegistry.getInstrumentation());
+ wakeUpAndGoToHomeScreen();
+ }
+
+ // TODO(vishnun)
+ @Ignore("Disabled until app-helper libraries are available.")
+ @Test
+ public void captureWindowAnimationFrameStats() throws Exception {
+ mWindowAnimationFrameStatsMonitor.start();
+ //AppHelperWrapper.getInstance().getHelper(CHROME).open();
+ //AppHelperWrapper.getInstance().getHelper(CHROME).exit();
+ mWindowAnimationFrameStatsMonitor.stop();
+ }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
new file mode 100644
index 000000000000..56284d7d516a
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.monitor;
+
+import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.nano.WindowManagerTraceFileProto;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Contains {@link WindowManagerTraceMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowManagerTraceMonitorTest}
+ */
+public class WindowManagerTraceMonitorTest {
+ private WindowManagerTraceMonitor mWindowManagerTraceMonitor;
+
+ @Before
+ public void setup() {
+ mWindowManagerTraceMonitor = new WindowManagerTraceMonitor();
+ }
+
+ @After
+ public void teardown() {
+ mWindowManagerTraceMonitor.stop();
+ mWindowManagerTraceMonitor.getOutputTraceFilePath("captureWindowTrace").toFile().delete();
+ }
+
+ @Test
+ public void canStartWindowTrace() throws Exception {
+ mWindowManagerTraceMonitor.start();
+ assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void canStopWindowTrace() throws Exception {
+ mWindowManagerTraceMonitor.start();
+ assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
+ mWindowManagerTraceMonitor.stop();
+ assertThat(mWindowManagerTraceMonitor.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void captureWindowTrace() throws Exception {
+ mWindowManagerTraceMonitor.start();
+ mWindowManagerTraceMonitor.stop();
+ File testFile = mWindowManagerTraceMonitor.save("captureWindowTrace").toFile();
+ assertThat(testFile.exists()).isTrue();
+ byte[] trace = Files.toByteArray(testFile);
+ assertThat(trace.length).isGreaterThan(0);
+ WindowManagerTraceFileProto mWindowTraceFileProto = WindowManagerTraceFileProto.parseFrom(
+ trace);
+ assertThat(mWindowTraceFileProto.magicNumber).isEqualTo(
+ (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
new file mode 100644
index 000000000000..34f4ebb89543
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
+import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
+import static com.android.server.wm.flicker.WindowUtils.getStatusBarPosition;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.util.Log;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Cycle through supported app rotations.
+ * To run this test: {@code atest FlickerTest:ChangeAppRotationTest}
+ */
+@RunWith(Parameterized.class)
+@LargeTest
+public class ChangeAppRotationTest extends FlickerTestBase {
+ private int beginRotation;
+ private int endRotation;
+
+ public ChangeAppRotationTest(String beginRotationName, String endRotationName,
+ int beginRotation, int endRotation) {
+ this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ this.beginRotation = beginRotation;
+ this.endRotation = endRotation;
+ }
+
+ @Parameters(name = "{0}-{1}")
+ public static Collection<Object[]> getParams() {
+ int[] supportedRotations =
+ {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+ Collection<Object[]> params = new ArrayList<>();
+ for (int begin : supportedRotations) {
+ for (int end : supportedRotations) {
+ if (begin != end) {
+ params.add(new Object[]{rotationToString(begin), rotationToString(end), begin,
+ end});
+ }
+ }
+ }
+ return params;
+ }
+
+ @Before
+ public void runTransition() {
+ super.runTransition(
+ changeAppRotation(testApp, uiDevice, beginRotation, endRotation).build());
+ }
+
+ @Test
+ public void checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults(result -> assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults(result -> assertThat(result)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkPosition_navBarLayerRotatesAndScales() {
+ Rect startingPos = getNavigationBarPosition(beginRotation);
+ Rect endingPos = getNavigationBarPosition(endRotation);
+ checkResults(result -> {
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ .inTheBeginning();
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos).atTheEnd();
+ }
+ );
+ }
+
+ @Test
+ public void checkPosition_appLayerRotates() {
+ Rect startingPos = getAppPosition(beginRotation);
+ Rect endingPos = getAppPosition(endRotation);
+ Log.e(TAG, "startingPos=" + startingPos + " endingPos=" + endingPos);
+ checkResults(result -> {
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning();
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd();
+ }
+ );
+ }
+
+ @Test
+ public void checkPosition_statusBarLayerScales() {
+ Rect startingPos = getStatusBarPosition(beginRotation);
+ Rect endingPos = getStatusBarPosition(endRotation);
+ checkResults(result -> {
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
+ .inTheBeginning();
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd();
+ }
+ );
+ }
+
+ @Test
+ public void checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
new file mode 100644
index 000000000000..2b62fcf196fb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CloseImeWindowToAppTest extends FlickerTestBase {
+
+ private static final String IME_WINDOW_TITLE = "InputMethod";
+ private IAppHelper mImeTestApp = new StandardAppHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+
+ @Before
+ public void runTransition() {
+ super.runTransition(editTextLoseFocusToApp(uiDevice)
+ .includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkVisibility_imeLayerBecomesInvisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(IME_WINDOW_TITLE)
+ .then()
+ .hidesLayer(IME_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_imeAppLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(mImeTestApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_imeAppWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAppWindowOnTop(mImeTestApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+ getDisplayBounds()).forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
new file mode 100644
index 000000000000..42b161f8a604
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window closing to home transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToHomeTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CloseImeWindowToHomeTest extends FlickerTestBase {
+
+ private static final String IME_WINDOW_TITLE = "InputMethod";
+ private IAppHelper mImeTestApp = new StandardAppHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+
+ @Before
+ public void runTransition() {
+ super.runTransition(editTextLoseFocusToHome(uiDevice)
+ .includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkVisibility_imeWindowBecomesInvisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsImeWindow(IME_WINDOW_TITLE)
+ .then()
+ .hidesImeWindow(IME_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_imeLayerBecomesInvisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(IME_WINDOW_TITLE)
+ .then()
+ .hidesLayer(IME_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_imeAppLayerBecomesInvisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(mImeTestApp.getPackage())
+ .then()
+ .hidesLayer(mImeTestApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_imeAppWindowBecomesInvisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAppWindowOnTop(mImeTestApp.getPackage())
+ .then()
+ .hidesAppWindowOnTop(mImeTestApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+ getDisplayBounds()).forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
new file mode 100644
index 000000000000..92bb1ea0b58f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static android.os.SystemClock.sleep;
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.AutomationUtils.clearRecents;
+import static com.android.server.wm.flicker.AutomationUtils.closePipWindow;
+import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen;
+import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow;
+import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen;
+import static com.android.server.wm.flicker.AutomationUtils.stopPackage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Rational;
+import android.view.Surface;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+
+/**
+ * Collection of common transitions which can be used to test different apps or scenarios.
+ */
+class CommonTransitions {
+
+ public static final int ITERATIONS = 1;
+ private static final String TAG = "FLICKER";
+ private static final long APP_LAUNCH_TIMEOUT = 10000;
+
+ private static void setRotation(UiDevice device, int rotation) {
+ try {
+ switch (rotation) {
+ case Surface.ROTATION_270:
+ device.setOrientationLeft();
+ break;
+
+ case Surface.ROTATION_90:
+ device.setOrientationRight();
+ break;
+
+ case Surface.ROTATION_0:
+ default:
+ device.setOrientationNatural();
+ }
+ // Wait for animation to complete
+ sleep(3000);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void clickEditTextWidget(UiDevice device, IAppHelper testApp) {
+ UiObject2 editText = device.findObject(By.res(testApp.getPackage(), "plain_text_input"));
+ editText.click();
+ sleep(500);
+ }
+
+ private static void clickEnterPipButton(UiDevice device, IAppHelper testApp) {
+ UiObject2 enterPipButton = device.findObject(By.res(testApp.getPackage(), "enter_pip"));
+ enterPipButton.click();
+ sleep(500);
+ }
+
+ static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice
+ device) {
+ return TransitionRunner.newBuilder()
+ .withTag("OpenAppWarm_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBeforeAll(testApp::open)
+ .runBefore(device::pressHome)
+ .runBefore(device::waitForIdle)
+ .run(testApp::open)
+ .runAfterAll(testApp::exit)
+ .runAfterAll(AutomationUtils::setDefaultWait)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder closeAppWithBackKey(IAppHelper testApp, UiDevice
+ device) {
+ return TransitionRunner.newBuilder()
+ .withTag("closeAppWithBackKey_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(testApp::open)
+ .runBefore(device::waitForIdle)
+ .run(device::pressBack)
+ .run(device::waitForIdle)
+ .runAfterAll(testApp::exit)
+ .runAfterAll(AutomationUtils::setDefaultWait)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder closeAppWithHomeKey(IAppHelper testApp, UiDevice
+ device) {
+ return TransitionRunner.newBuilder()
+ .withTag("closeAppWithHomeKey_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(testApp::open)
+ .runBefore(device::waitForIdle)
+ .run(device::pressHome)
+ .run(device::waitForIdle)
+ .runAfterAll(testApp::exit)
+ .runAfterAll(AutomationUtils::setDefaultWait)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder getOpenAppCold(IAppHelper testApp,
+ UiDevice device) {
+ return TransitionRunner.newBuilder()
+ .withTag("OpenAppCold_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::exit)
+ .runBefore(device::waitForIdle)
+ .run(testApp::open)
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder changeAppRotation(IAppHelper testApp, UiDevice
+ device, int beginRotation, int endRotation) {
+ return TransitionRunner.newBuilder()
+ .withTag("changeAppRotation_" + testApp.getLauncherName()
+ + rotationToString(beginRotation) + "_" +
+ rotationToString(endRotation))
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBeforeAll(testApp::open)
+ .runBefore(() -> setRotation(device, beginRotation))
+ .run(() -> setRotation(device, endRotation))
+ .runAfterAll(testApp::exit)
+ .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder changeAppRotation(Intent intent, String intentId, Context context,
+ UiDevice
+ device, int beginRotation, int endRotation) {
+ final String testTag = "changeAppRotation_" + intentId + "_" +
+ rotationToString(beginRotation) + "_" + rotationToString(endRotation);
+ return TransitionRunner.newBuilder()
+ .withTag(testTag)
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBeforeAll(() -> {
+ context.startActivity(intent);
+ device.wait(Until.hasObject(By.pkg(intent.getComponent()
+ .getPackageName()).depth(0)), APP_LAUNCH_TIMEOUT);
+ }
+ )
+ .runBefore(() -> setRotation(device, beginRotation))
+ .run(() -> setRotation(device, endRotation))
+ .runAfterAll(() -> stopPackage(context, intent.getComponent().getPackageName()))
+ .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device) {
+ return TransitionRunner.newBuilder()
+ .withTag("appToSplitScreen_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(testApp::open)
+ .runBefore(device::waitForIdle)
+ .runBefore(() -> sleep(500))
+ .run(() -> launchSplitScreen(device))
+ .runAfter(() -> exitSplitScreen(device))
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder splitScreenToLauncher(IAppHelper testApp, UiDevice device) {
+ return TransitionRunner.newBuilder()
+ .withTag("splitScreenToLauncher_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(testApp::open)
+ .runBefore(device::waitForIdle)
+ .runBefore(() -> launchSplitScreen(device))
+ .run(() -> exitSplitScreen(device))
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder editTextSetFocus(UiDevice device) {
+ IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+ return TransitionRunner.newBuilder()
+ .withTag("editTextSetFocus_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::open)
+ .run(() -> clickEditTextWidget(device, testApp))
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, IAppHelper testAppBottom,
+ UiDevice device, Rational startRatio, Rational stopRatio) {
+ String testTag = "resizeSplitScreen_" + testAppTop.getLauncherName() + "_" +
+ testAppBottom.getLauncherName() + "_" +
+ startRatio.toString().replace("/", ":") + "_to_" +
+ stopRatio.toString().replace("/", ":");
+ return TransitionRunner.newBuilder()
+ .withTag(testTag)
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBeforeAll(() -> clearRecents(device))
+ .runBefore(testAppBottom::open)
+ .runBefore(device::pressHome)
+ .runBefore(testAppTop::open)
+ .runBefore(device::waitForIdle)
+ .runBefore(() -> launchSplitScreen(device))
+ .runBefore(() -> {
+ UiObject2 snapshot = device.findObject(
+ By.res("com.google.android.apps.nexuslauncher", "snapshot"));
+ snapshot.click();
+ })
+ .runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio))
+ .run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio))
+ .runAfter(() -> exitSplitScreen(device))
+ .runAfter(device::pressHome)
+ .runAfterAll(testAppTop::exit)
+ .runAfterAll(testAppBottom::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder editTextLoseFocusToHome(UiDevice device) {
+ IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+ return TransitionRunner.newBuilder()
+ .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::open)
+ .runBefore(() -> clickEditTextWidget(device, testApp))
+ .run(device::pressHome)
+ .run(device::waitForIdle)
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder editTextLoseFocusToApp(UiDevice device) {
+ IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+ return TransitionRunner.newBuilder()
+ .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::open)
+ .runBefore(() -> clickEditTextWidget(device, testApp))
+ .run(device::pressBack)
+ .run(device::waitForIdle)
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder enterPipMode(UiDevice device) {
+ IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "PipApp");
+ return TransitionRunner.newBuilder()
+ .withTag("enterPipMode_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::open)
+ .run(() -> clickEnterPipButton(device, testApp))
+ .runAfter(() -> closePipWindow(device))
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder exitPipModeToHome(UiDevice device) {
+ IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "PipApp");
+ return TransitionRunner.newBuilder()
+ .withTag("exitPipModeToHome_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::open)
+ .runBefore(() -> clickEnterPipButton(device, testApp))
+ .run(() -> closePipWindow(device))
+ .run(device::waitForIdle)
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+
+ static TransitionBuilder exitPipModeToApp(UiDevice device) {
+ IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "PipApp");
+ return TransitionRunner.newBuilder()
+ .withTag("exitPipModeToApp_" + testApp.getLauncherName())
+ .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBefore(device::pressHome)
+ .runBefore(testApp::open)
+ .runBefore(() -> clickEnterPipButton(device, testApp))
+ .run(() -> expandPipWindow(device))
+ .run(device::waitForIdle)
+ .runAfterAll(testApp::exit)
+ .repeat(ITERATIONS);
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
new file mode 100644
index 000000000000..fec248c13818
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Rational;
+import android.view.Surface;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to help debug individual transitions, capture video recordings and create test cases.
+ */
+@Ignore("Used for debugging transitions used in FlickerTests.")
+@RunWith(AndroidJUnit4.class)
+public class DebugTest {
+ private IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ private UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ /**
+ * atest FlickerTest:DebugTests#openAppCold
+ */
+ @Test
+ public void openAppCold() {
+ CommonTransitions.getOpenAppCold(testApp, uiDevice).recordAllRuns().build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#openAppWarm
+ */
+ @Test
+ public void openAppWarm() {
+ CommonTransitions.openAppWarm(testApp, uiDevice).recordAllRuns().build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#changeOrientationFromNaturalToLeft
+ */
+ @Test
+ public void changeOrientationFromNaturalToLeft() {
+ CommonTransitions.changeAppRotation(testApp, uiDevice, Surface.ROTATION_0,
+ Surface.ROTATION_270).recordAllRuns().build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#closeAppWithBackKey
+ */
+ @Test
+ public void closeAppWithBackKey() {
+ CommonTransitions.closeAppWithBackKey(testApp, uiDevice).recordAllRuns().build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#closeAppWithHomeKey
+ */
+ @Test
+ public void closeAppWithHomeKey() {
+ CommonTransitions.closeAppWithHomeKey(testApp, uiDevice).recordAllRuns().build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#openAppToSplitScreen
+ */
+ @Test
+ public void openAppToSplitScreen() {
+ CommonTransitions.appToSplitScreen(testApp, uiDevice).includeJankyRuns().recordAllRuns()
+ .build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#splitScreenToLauncher
+ */
+ @Test
+ public void splitScreenToLauncher() {
+ CommonTransitions.splitScreenToLauncher(testApp,
+ uiDevice).includeJankyRuns().recordAllRuns()
+ .build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#resizeSplitScreen
+ */
+ @Test
+ public void resizeSplitScreen() {
+ IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+ CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
+ new Rational(2, 3)).includeJankyRuns().recordEachRun().build().run();
+ }
+
+ // IME tests
+
+ /**
+ * atest FlickerTest:DebugTests#editTextSetFocus
+ */
+ @Test
+ public void editTextSetFocus() {
+ CommonTransitions.editTextSetFocus(uiDevice).includeJankyRuns().recordEachRun()
+ .build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#editTextLoseFocusToHome
+ */
+ @Test
+ public void editTextLoseFocusToHome() {
+ CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+ .build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#editTextLoseFocusToApp
+ */
+ @Test
+ public void editTextLoseFocusToApp() {
+ CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+ .build().run();
+ }
+
+ // PIP tests
+
+ /**
+ * atest FlickerTest:DebugTests#enterPipMode
+ */
+ @Test
+ public void enterPipMode() {
+ CommonTransitions.enterPipMode(uiDevice).includeJankyRuns().recordEachRun().build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#exitPipModeToHome
+ */
+ @Test
+ public void exitPipModeToHome() {
+ CommonTransitions.exitPipModeToHome(uiDevice).includeJankyRuns().recordEachRun()
+ .build().run();
+ }
+
+ /**
+ * atest FlickerTest:DebugTests#exitPipModeToApp
+ */
+ @Test
+ public void exitPipModeToApp() {
+ CommonTransitions.exitPipModeToApp(uiDevice).includeJankyRuns().recordEachRun()
+ .build().run();
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
new file mode 100644
index 000000000000..7061b23c069d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.AutomationUtils.setDefaultWait;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import org.junit.After;
+import org.junit.AfterClass;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Base class of all Flicker test that performs common functions for all flicker tests:
+ * <p>
+ * - Caches transitions so that a transition is run once and the transition results are used by
+ * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
+ * multiple times.
+ * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
+ * - Fails tests if results are not available for any test due to jank.
+ */
+public class FlickerTestBase {
+ public static final String TAG = "FLICKER";
+ static final String NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar";
+ static final String STATUS_BAR_WINDOW_TITLE = "StatusBar";
+ static final String DOCKED_STACK_DIVIDER = "DockedStackDivider";
+ private static HashMap<String, List<TransitionResult>> transitionResults =
+ new HashMap<>();
+ IAppHelper testApp;
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ private List<TransitionResult> results;
+ private TransitionResult lastResult = null;
+
+ /**
+ * Teardown any system settings and clean up test artifacts from the file system.
+ *
+ * Note: test artifacts for failed tests will remain on the device.
+ */
+ @AfterClass
+ public static void teardown() {
+ setDefaultWait();
+ transitionResults.values().stream()
+ .flatMap(List::stream)
+ .forEach(result -> {
+ if (result.canDelete()) {
+ result.delete();
+ } else {
+ if (result.layersTraceExists()) {
+ Log.e(TAG, "Layers trace saved to " + result.getLayersTracePath());
+ }
+ if (result.windowManagerTraceExists()) {
+ Log.e(TAG, "WindowManager trace saved to " + result
+ .getWindowManagerTracePath
+ ());
+ }
+ if (result.screenCaptureVideoExists()) {
+ Log.e(TAG, "Screen capture video saved to " + result
+ .screenCaptureVideo.toString());
+ }
+ }
+ });
+ }
+
+ /**
+ * Runs a transition, returns a cached result if the transition has run before.
+ */
+ void runTransition(TransitionRunner transition) {
+ if (transitionResults.containsKey(transition.getTestTag())) {
+ results = transitionResults.get(transition.getTestTag());
+ return;
+ }
+ results = transition.run().getResults();
+ /* Fail if we don't have any results due to jank */
+ assertWithMessage("No results to test because all transition runs were invalid because "
+ + "of Jank").that(results).isNotEmpty();
+ transitionResults.put(transition.getTestTag(), results);
+ }
+
+ /**
+ * Goes through a list of transition results and checks assertions on each result.
+ */
+ void checkResults(Consumer<TransitionResult> assertion) {
+
+ for (TransitionResult result : results) {
+ lastResult = result;
+ assertion.accept(result);
+ }
+ lastResult = null;
+ }
+
+ /**
+ * Kludge to mark a file for saving. If {@code checkResults} fails, the last result is not
+ * cleared. This indicates the assertion failed for the result, so mark it for saving.
+ */
+ @After
+ public void markArtifactsForSaving() {
+ if (lastResult != null) {
+ lastResult.flagForSaving();
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
new file mode 100644
index 000000000000..7e713699c72e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.getOpenAppCold;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cold launch app from launcher.
+ * To run this test: {@code atest FlickerTests:OpenAppColdTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppColdTest extends FlickerTestBase {
+
+ public OpenAppColdTest() {
+ this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ }
+
+ @Before
+ public void runTransition() {
+ super.runTransition(getOpenAppCold(testApp, uiDevice).build());
+ }
+
+ @Test
+ public void checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults(result -> assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults(result -> assertThat(result)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_wallpaperWindowBecomesInvisible() {
+ checkResults(result -> assertThat(result)
+ .showsBelowAppWindow("wallpaper")
+ .then()
+ .hidesBelowAppWindow("wallpaper")
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+ checkResults(result -> assertThat(result)
+ .showsAppWindowOnTop(
+ "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+ .then()
+ .showsAppWindowOnTop(testApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+ getDisplayBounds()).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_wallpaperLayerBecomesInvisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("wallpaper")
+ .then()
+ .hidesLayer("wallpaper")
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
new file mode 100644
index 000000000000..745569aac7ab
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test open app to split screen.
+ * To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppToSplitScreenTest extends FlickerTestBase {
+
+ public OpenAppToSplitScreenTest() {
+ this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ }
+
+ @Before
+ public void runTransition() {
+ super.runTransition(appToSplitScreen(testApp, uiDevice).includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_dividerWindowBecomesVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result ->
+ LayersTraceSubject.assertThat(result)
+ .coversRegion(getDisplayBounds()).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_dividerLayerBecomesVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
new file mode 100644
index 000000000000..de7639d43d3c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test warm launch app.
+ * To run this test: {@code atest FlickerTests:OpenAppWarmTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppWarmTest extends FlickerTestBase {
+
+ public OpenAppWarmTest() {
+ this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ }
+
+ @Before
+ public void runTransition() {
+ super.runTransition(openAppWarm(testApp, uiDevice).build());
+ }
+
+ @Test
+ public void checkVisibility_navBarIsAlwaysVisible() {
+ checkResults(result -> assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarIsAlwaysVisible() {
+ checkResults(result -> assertThat(result)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_wallpaperBecomesInvisible() {
+ checkResults(result -> assertThat(result)
+ .showsBelowAppWindow("wallpaper")
+ .then()
+ .hidesBelowAppWindow("wallpaper")
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+ checkResults(result -> assertThat(result)
+ .showsAppWindowOnTop(
+ "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+ .then()
+ .showsAppWindowOnTop(testApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+ getDisplayBounds()).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_wallpaperLayerBecomesInvisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("wallpaper")
+ .then()
+ .hidesLayer("wallpaper")
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
new file mode 100644
index 000000000000..1bd519c8334e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window opening transitions.
+ * To run this test: {@code atest FlickerTests:OpenImeWindowTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenImeWindowTest extends FlickerTestBase {
+
+ private static final String IME_WINDOW_TITLE = "InputMethod";
+
+ @Before
+ public void runTransition() {
+ super.runTransition(editTextSetFocus(uiDevice)
+ .includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkVisibility_imeWindowBecomesVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .hidesImeWindow(IME_WINDOW_TITLE)
+ .then()
+ .showsImeWindow(IME_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_imeLayerBecomesVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hidesLayer(IME_WINDOW_TITLE)
+ .then()
+ .showsLayer(IME_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+ getDisplayBounds()).forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
new file mode 100644
index 000000000000..8a15cbdb7709
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.resizeSplitScreen;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WindowUtils.getDockedStackDividerInset;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarHeight;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Rect;
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Rational;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test split screen resizing window transitions.
+ * To run this test: {@code atest FlickerTests:ResizeSplitScreenTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ResizeSplitScreenTest extends FlickerTestBase {
+
+ public ResizeSplitScreenTest() {
+ this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ }
+
+ @Before
+ public void runTransition() {
+ IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry
+ .getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "ImeApp");
+ super.runTransition(resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
+ new Rational(2, 3)).includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkVisibility_navBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(STATUS_BAR_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_topAppLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("SimpleActivity")
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_bottomAppLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("ImeActivity")
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_dividerLayerIsAlwaysVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkPosition_appsStartingBounds() {
+ Rect displayBounds = getDisplayBounds();
+ checkResults(result -> {
+ LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+ result.getLayersTracePath());
+
+ assertThat(entries.getEntries()).isNotEmpty();
+ Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds
+ (DOCKED_STACK_DIVIDER);
+
+ Rect startingTopAppBounds = new Rect(0, 0, startingDividerBounds.right,
+ startingDividerBounds.top + getDockedStackDividerInset());
+
+ Rect startingBottomAppBounds = new Rect(0,
+ startingDividerBounds.bottom - getDockedStackDividerInset(),
+ displayBounds.right,
+ displayBounds.bottom - getNavigationBarHeight());
+
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+ .inTheBeginning();
+
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+ .inTheBeginning();
+ });
+ }
+
+ @Test
+ public void checkPosition_appsEndingBounds() {
+ Rect displayBounds = getDisplayBounds();
+ checkResults(result -> {
+ LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+ result.getLayersTracePath());
+
+ assertThat(entries.getEntries()).isNotEmpty();
+ Rect endingDividerBounds = entries.getEntries().get(
+ entries.getEntries().size() - 1).getVisibleBounds(
+ DOCKED_STACK_DIVIDER);
+
+ Rect startingTopAppBounds = new Rect(0, 0, endingDividerBounds.right,
+ endingDividerBounds.top + getDockedStackDividerInset());
+
+ Rect startingBottomAppBounds = new Rect(0,
+ endingDividerBounds.bottom - getDockedStackDividerInset(),
+ displayBounds.right,
+ displayBounds.bottom - getNavigationBarHeight());
+
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+ .atTheEnd();
+
+ LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+ .atTheEnd();
+ });
+ }
+
+ @Test
+ public void checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_topAppWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAppWindow("SimpleActivity")
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_bottomAppWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAppWindow("ImeActivity")
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_dividerWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
new file mode 100644
index 000000000000..3eab68d1272f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
+import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME;
+
+import android.content.Intent;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Cycle through supported app rotations using seamless rotations.
+ * To run this test: {@code atest FlickerTests:SeamlessAppRotationTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class SeamlessAppRotationTest extends FlickerTestBase {
+ private int mBeginRotation;
+ private int mEndRotation;
+ private Intent mIntent;
+
+ public SeamlessAppRotationTest(String testId, Intent intent, int beginRotation,
+ int endRotation) {
+ this.mIntent = intent;
+ this.mBeginRotation = beginRotation;
+ this.mEndRotation = endRotation;
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection<Object[]> getParams() {
+ int[] supportedRotations =
+ {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+ Collection<Object[]> params = new ArrayList<>();
+
+ ArrayList<Intent> testIntents = new ArrayList<>();
+
+ // launch test activity that supports seamless rotation
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(SEAMLESS_ACTIVITY_COMPONENT_NAME);
+ testIntents.add(intent);
+
+ // launch test activity that supports seamless rotation with a busy UI thread to miss frames
+ // when the app is asked to redraw
+ intent = new Intent(intent);
+ intent.putExtra(EXTRA_STARVE_UI_THREAD, true);
+ testIntents.add(intent);
+
+ for (Intent testIntent : testIntents) {
+ for (int begin : supportedRotations) {
+ for (int end : supportedRotations) {
+ if (begin != end) {
+ String testId = rotationToString(begin) + "_" + rotationToString(end);
+ if (testIntent.getExtras() != null &&
+ testIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
+ testId += "_" + "BUSY_UI_THREAD";
+ }
+ params.add(new Object[]{testId, testIntent, begin, end});
+ }
+ }
+ }
+ }
+ return params;
+ }
+
+ @Before
+ public void runTransition() {
+ String intentId = "";
+ if (mIntent.getExtras() != null &&
+ mIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
+ intentId = "BUSY_UI_THREAD";
+ }
+
+ super.runTransition(
+ changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(),
+ uiDevice, mBeginRotation, mEndRotation).repeat(5).build());
+ }
+
+ @Test
+ public void checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkPosition_navBarLayerRotatesAndScales() {
+ Rect startingPos = getNavigationBarPosition(mBeginRotation);
+ Rect endingPos = getNavigationBarPosition(mEndRotation);
+ if (startingPos.equals(endingPos)) {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ .forAllEntries());
+ } else {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ .inTheBeginning());
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+ .atTheEnd());
+ }
+ }
+
+ @Test
+ public void checkPosition_appLayerRotates() {
+ Rect startingPos = getAppPosition(mBeginRotation);
+ Rect endingPos = getAppPosition(mEndRotation);
+ if (startingPos.equals(endingPos)) {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
+ .forAllEntries());
+ } else {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
+ .then()
+ .hasVisibleRegion(mIntent.getComponent().getPackageName(), endingPos)
+ .forAllEntries());
+ }
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ Rect startingBounds = getDisplayBounds(mBeginRotation);
+ Rect endingBounds = getDisplayBounds(mEndRotation);
+ if (startingBounds.equals(endingBounds)) {
+ checkResults(result ->
+ LayersTraceSubject.assertThat(result)
+ .coversRegion(startingBounds)
+ .forAllEntries());
+ } else {
+ checkResults(result ->
+ LayersTraceSubject.assertThat(result)
+ .coversRegion(startingBounds)
+ .then()
+ .coversRegion(endingBounds)
+ .forAllEntries());
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
new file mode 100644
index 000000000000..40bd4e962153
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.splitScreenToLauncher;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test open app to split screen.
+ * To run this test: {@code atest FlickerTests:SplitScreenToLauncherTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class SplitScreenToLauncherTest extends FlickerTestBase {
+
+ public SplitScreenToLauncherTest() {
+ this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ "com.android.server.wm.flicker.testapp", "SimpleApp");
+ }
+
+ @Before
+ public void runTransition() {
+ super.runTransition(splitScreenToLauncher(testApp, uiDevice).includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkCoveredRegion_noUncoveredRegions() {
+ checkResults(result ->
+ LayersTraceSubject.assertThat(result)
+ .coversRegion(getDisplayBounds()).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_dividerLayerBecomesInVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ .forAllEntries());
+ }
+
+ @FlakyTest(bugId = 79686616)
+ @Test
+ public void checkVisibility_appLayerBecomesInVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(testApp.getPackage())
+ .then()
+ .hidesLayer(testApp.getPackage())
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_navBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_dividerWindowBecomesInVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
new file mode 100644
index 000000000000..79a0220e0e87
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import android.app.Instrumentation;
+import android.platform.helpers.AbstractStandardAppHelper;
+
+/**
+ * Class to take advantage of {@code IAppHelper} interface so the same test can be run against
+ * first party and third party apps.
+ */
+public class StandardAppHelper extends AbstractStandardAppHelper {
+ private final String mPackageName;
+ private final String mLauncherName;
+
+ public StandardAppHelper(Instrumentation instr, String packageName, String launcherName) {
+ super(instr);
+ mPackageName = packageName;
+ mLauncherName = launcherName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPackage() {
+ return mPackageName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getLauncherName() {
+ return mLauncherName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dismissInitialDialogs() {
+
+ }
+}
diff --git a/tests/FlickerTests/test-apps/Android.mk b/tests/FlickerTests/test-apps/Android.mk
new file mode 100644
index 000000000000..9af9f444ca59
--- /dev/null
+++ b/tests/FlickerTests/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include $(call all-subdir-makefiles)
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.mk b/tests/FlickerTests/test-apps/flickerapp/Android.mk
new file mode 100644
index 000000000000..b916900a7504
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FlickerTestApp
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SDK_VERSION := current
+LOCAL_COMPATIBILITY_SUITE := device-tests
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flickertestapplib
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH)) \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
new file mode 100644
index 000000000000..b694172d60ca
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.wm.flicker.testapp">
+
+ <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="27"/>
+ <application
+ android:allowBackup="false"
+ android:supportsRtl="true">
+ <activity android:name=".SimpleActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+ android:label="SimpleApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".ImeActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+ android:label="ImeApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".PipActivity"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:configChanges=
+ "screenSize|smallestScreenSize|screenLayout|orientation"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
+ android:label="PipApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".SeamlessRotationActivity"
+ android:taskAffinity=
+ "com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="SeamlessApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
new file mode 100644
index 000000000000..d5eb02330441
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_green_light">
+ <EditText android:id="@+id/plain_text_input"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:inputType="text"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
new file mode 100644
index 000000000000..2c58d91e34fe
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_blue_bright">
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/enter_pip"
+ android:text="Enter PIP"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
new file mode 100644
index 000000000000..5d94e5177dcc
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
new file mode 100644
index 000000000000..18994111324e
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.content.ComponentName;
+
+public class ActivityOptions {
+ public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
+ public static final ComponentName SEAMLESS_ACTIVITY_COMPONENT_NAME =
+ new ComponentName("com.android.server.wm.flicker.testapp",
+ "com.android.server.wm.flicker.testapp.SeamlessRotationActivity");
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
new file mode 100644
index 000000000000..df60460e7ae3
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ImeActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_ime);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
new file mode 100644
index 000000000000..9a8f39907877
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Rational;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class PipActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_pip);
+ Button enterPip = (Button) findViewById(R.id.enter_pip);
+
+ PictureInPictureParams params = new PictureInPictureParams.Builder()
+ .setAspectRatio(new Rational(1, 1))
+ .setSourceRectHint(new Rect(0, 0, 100, 100))
+ .build();
+
+ enterPip.setOnClickListener((v) -> enterPictureInPictureMode(params));
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
new file mode 100644
index 000000000000..3a0c1c9382fe
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import static android.os.SystemClock.sleep;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+
+import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class SeamlessRotationActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ enableSeamlessRotation();
+ setContentView(R.layout.activity_simple);
+ boolean starveUiThread = getIntent().getExtras() != null &&
+ getIntent().getExtras().getBoolean(EXTRA_STARVE_UI_THREAD);
+ if (starveUiThread) {
+ starveUiThread();
+ }
+ }
+
+ private void starveUiThread() {
+ Handler handler = new Handler(Looper.getMainLooper(), (Message unused) -> {
+ sleep(20);
+ return true;
+ });
+ new Timer().schedule(new TimerTask() {
+ @Override
+ public void run() {
+ handler.sendEmptyMessage(0);
+ }
+ }, 0, 21);
+ }
+
+ private void enableSeamlessRotation() {
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ getWindow().setAttributes(p);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
new file mode 100644
index 000000000000..699abf87d341
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class SimpleActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_simple);
+ }
+}
diff --git a/vr/java/com/google/vr/platform/DeviceInfo.java b/vr/java/com/google/vr/platform/DeviceInfo.java
index f6da66b1807b..6a4617d4624d 100644
--- a/vr/java/com/google/vr/platform/DeviceInfo.java
+++ b/vr/java/com/google/vr/platform/DeviceInfo.java
@@ -1,5 +1,6 @@
package com.google.vr.platform;
+import android.annotation.UnsupportedAppUsage;
import android.os.SystemProperties;
/**
@@ -13,6 +14,7 @@ public class DeviceInfo {
/**
* Returns true if this device boots directly in VR mode.
*/
+ @UnsupportedAppUsage
public static boolean getVrBoot() {
return SystemProperties.getBoolean(VR_MODE_BOOT, false);
}
diff --git a/vr/java/com/google/vr/platform/Dvr.java b/vr/java/com/google/vr/platform/Dvr.java
index b07d6347c9a5..41dcd8741ffb 100644
--- a/vr/java/com/google/vr/platform/Dvr.java
+++ b/vr/java/com/google/vr/platform/Dvr.java
@@ -1,5 +1,7 @@
package com.google.vr.platform;
+import android.annotation.UnsupportedAppUsage;
+
/**
* Class to load the dvr api.
* @hide
@@ -10,6 +12,7 @@ public class Dvr {
*
* @return A Long object describing the handle returned by dlopen.
*/
+ @UnsupportedAppUsage
public static Long loadLibrary() {
// Load a thin JNI library that runs dlopen on request.
System.loadLibrary("dvr_loader");
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 2ea6e797ec93..66297fc6ab29 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -16,11 +16,12 @@
package android.net.wifi.hotspot2;
+import android.net.wifi.WifiManager;
import android.os.Handler;
/**
* Base class for provisioning callbacks. Should be extended by applications and set when calling
- * {@link WifiManager#startSubscriptionProvisiong(OsuProvider, ProvisioningCallback, Handler)}.
+ * {@link WifiManager#startSubscriptionProvisioning(OsuProvider, ProvisioningCallback, Handler)}.
*
* @hide
*/
@@ -28,84 +29,101 @@ public abstract class ProvisioningCallback {
/**
* The reason code for Provisioning Failure due to connection failure to OSU AP.
- * @hide
*/
- public static final int OSU_FAILURE_AP_CONNECTION = 1;
+ public static final int OSU_FAILURE_AP_CONNECTION = 1;
/**
- * The reason code for Provisioning Failure due to connection failure to OSU AP.
- * @hide
+ * The reason code for invalid server URL address.
*/
public static final int OSU_FAILURE_SERVER_URL_INVALID = 2;
/**
- * The reason code for Provisioning Failure due to connection failure to OSU AP.
- * @hide
+ * The reason code for provisioning failure due to connection failure to the server.
*/
- public static final int OSU_FAILURE_SERVER_CONNECTION = 3;
+ public static final int OSU_FAILURE_SERVER_CONNECTION = 3;
/**
- * The reason code for Provisioning Failure due to connection failure to OSU AP.
- * @hide
+ * The reason code for provisioning failure due to invalid server certificate.
*/
- public static final int OSU_FAILURE_SERVER_VALIDATION = 4;
+ public static final int OSU_FAILURE_SERVER_VALIDATION = 4;
/**
- * The reason code for Provisioning Failure due to connection failure to OSU AP.
- * @hide
+ * The reason code for provisioning failure due to invalid service provider.
*/
- public static final int OSU_FAILURE_PROVIDER_VERIFICATION = 5;
+ public static final int OSU_FAILURE_SERVICE_PROVIDER_VERIFICATION = 5;
/**
- * The reason code for Provisioning Failure when a provisioning flow is aborted.
- * @hide
+ * The reason code for provisioning failure when a provisioning flow is aborted.
*/
public static final int OSU_FAILURE_PROVISIONING_ABORTED = 6;
/**
- * The reason code for Provisioning Failure when a provisioning flow is aborted.
- * @hide
+ * The reason code for provisioning failure when a provisioning flow is not possible.
*/
public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7;
/**
- * The status code for Provisioning flow to indicate connecting to OSU AP
- * @hide
+ * The reason code for provisioning failure due to invalid server url.
+ */
+ public static final int OSU_FAILURE_INVALID_SERVER_URL = 8;
+
+ /**
+ * The reason code for provisioning failure when a command received is not the expected command
+ * type.
+ */
+ public static final int OSU_FAILURE_UNEXPECTED_COMMAND_TYPE = 9;
+
+ /**
+ * The reason code for provisioning failure when a SOAP message is not the expected message
+ * type.
+ */
+ public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_TYPE = 10;
+
+ /**
+ * The reason code for provisioning failure when a SOAP message exchange fails.
+ */
+ public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11;
+
+ /**
+ * The status code for provisioning flow to indicate connecting to OSU AP
+ */
+ public static final int OSU_STATUS_AP_CONNECTING = 1;
+
+ /**
+ * The status code for provisioning flow to indicate the OSU AP is connected.
*/
- public static final int OSU_STATUS_AP_CONNECTING = 1;
+ public static final int OSU_STATUS_AP_CONNECTED = 2;
/**
- * The status code for Provisioning flow to indicate connected to OSU AP
- * @hide
+ * The status code for provisioning flow to indicate the server connection is completed.
*/
- public static final int OSU_STATUS_AP_CONNECTED = 2;
+ public static final int OSU_STATUS_SERVER_CONNECTED = 3;
/**
- * The status code for Provisioning flow to indicate connecting to OSU AP
- * @hide
+ * The status code for provisioning flow to indicate the server certificate is validated.
*/
- public static final int OSU_STATUS_SERVER_CONNECTED = 3;
+ public static final int OSU_STATUS_SERVER_VALIDATED = 4;
/**
- * The status code for Provisioning flow to indicate connecting to OSU AP
- * @hide
+ * The status code for provisioning flow to indicate the service provider is verified.
*/
- public static final int OSU_STATUS_SERVER_VALIDATED = 4;
+ public static final int OSU_STATUS_SERVICE_PROVIDER_VERIFIED = 5;
/**
- * The status code for Provisioning flow to indicate connecting to OSU AP
- * @hide
+ * The status code for provisioning flow to indicate starting the SOAP exchange.
*/
- public static final int OSU_STATUS_PROVIDER_VERIFIED = 5;
+ public static final int OSU_STATUS_INIT_SOAP_EXCHANGE = 6;
/**
* Provisioning status for OSU failure
+ *
* @param status indicates error condition
*/
public abstract void onProvisioningFailure(int status);
/**
* Provisioning status when OSU is in progress
+ *
* @param status indicates status of OSU flow
*/
public abstract void onProvisioningStatus(int status);