summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java16
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java2
-rw-r--r--apex/statsd/framework/Android.bp25
-rw-r--r--apex/statsd/framework/test/Android.bp36
-rw-r--r--apex/statsd/framework/test/AndroidTest.xml34
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java4
-rw-r--r--cmds/statsd/src/atoms.proto152
-rw-r--r--cmds/statsd/tests/FieldValue_test.cpp104
-rw-r--r--core/java/android/app/ActivityManager.java4
-rw-r--r--core/java/android/app/ActivityView.java15
-rw-r--r--core/java/android/app/AppOpsManager.java19
-rw-r--r--core/java/android/companion/BluetoothDeviceFilterUtils.java7
-rw-r--r--core/java/android/companion/BluetoothLeDeviceFilter.java14
-rw-r--r--core/java/android/content/pm/dex/ArtManagerInternal.java2
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java14
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java30
-rw-r--r--core/java/android/hardware/display/DisplayManager.java12
-rw-r--r--core/java/android/net/NetworkCapabilities.java27
-rw-r--r--core/java/android/net/RouteInfo.java43
-rw-r--r--core/java/android/os/storage/StorageManager.java32
-rwxr-xr-xcore/java/android/provider/Settings.java4
-rw-r--r--core/java/android/service/autofill/augmented/AugmentedAutofillService.java4
-rw-r--r--core/java/android/service/autofill/augmented/FillCallback.java21
-rw-r--r--core/java/android/service/autofill/augmented/IFillCallback.aidl4
-rw-r--r--core/java/android/telephony/PhoneStateListener.java14
-rw-r--r--core/java/android/view/Display.java24
-rw-r--r--core/java/android/view/DisplayInfo.java9
-rw-r--r--core/java/android/view/InsetsController.java18
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java2
-rw-r--r--core/java/android/view/PendingInsetsController.java14
-rw-r--r--core/java/android/view/SurfaceControl.java26
-rw-r--r--core/java/android/view/SurfaceView.java11
-rw-r--r--core/java/android/view/WindowInsetsController.java7
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java16
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java58
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java9
-rw-r--r--core/java/com/android/internal/app/ResolverViewPager.java6
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java28
-rw-r--r--core/java/com/android/internal/infra/ServiceConnector.java2
-rw-r--r--core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java2
-rw-r--r--core/java/com/android/internal/policy/BackdropFrameRenderer.java46
-rw-r--r--core/java/com/android/internal/policy/DecorView.java59
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java11
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java10
-rw-r--r--core/java/com/android/internal/widget/MessagingLinearLayout.java21
-rw-r--r--core/jni/android_media_AudioFormat.h15
-rw-r--r--core/proto/android/app/enums.proto1
-rw-r--r--core/proto/android/stats/launcher/launcher.proto10
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/layout/notification_template_messaging_group.xml1
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml6
-rw-r--r--core/res/res/values-be/strings.xml2
-rw-r--r--core/res/res/values-es/strings.xml14
-rw-r--r--core/res/res/values-et/strings.xml12
-rw-r--r--core/res/res/values-hi/strings.xml2
-rw-r--r--core/res/res/values-hy/strings.xml2
-rw-r--r--core/res/res/values-in/strings.xml2
-rw-r--r--core/res/res/values-it/strings.xml14
-rw-r--r--core/res/res/values-kk/strings.xml10
-rw-r--r--core/res/res/values-ko/strings.xml4
-rw-r--r--core/res/res/values-my/strings.xml2
-rw-r--r--core/res/res/values-ne/strings.xml70
-rw-r--r--core/res/res/values-sr/strings.xml6
-rw-r--r--core/res/res/values-tl/strings.xml16
-rw-r--r--core/res/res/values-tr/strings.xml2
-rw-r--r--data/etc/car/Android.bp7
-rw-r--r--data/etc/car/com.android.car.companiondevicesupport.xml24
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp2
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/TetherUtil.java33
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java7
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java4
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml2
-rw-r--r--packages/SystemUI/res/layout/bubbles_manage_button_education.xml5
-rw-r--r--packages/SystemUI/res/layout/controls_management_favorites.xml2
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml11
-rw-r--r--packages/SystemUI/res/layout/screen_record_dialog.xml209
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHost.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java227
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/Events.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java127
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java38
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java33
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java28
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java10
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java9
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java53
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java10
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java30
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java18
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java21
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java32
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java99
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java2
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java49
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java3
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java15
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java6
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java3
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java21
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java2
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java3
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java4
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java6
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java2
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java35
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java16
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java6
-rw-r--r--services/core/java/com/android/server/media/MediaKeyDispatcher.java9
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java664
-rw-r--r--services/core/java/com/android/server/notification/NotificationChannelLogger.java28
-rw-r--r--services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java9
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java1
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java467
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java73
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java3
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java31
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java9
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java14
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java46
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java386
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java18
-rw-r--r--tests/net/common/java/android/net/LinkPropertiesTest.java18
-rw-r--r--tests/net/common/java/android/net/RouteInfoTest.java42
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java27
197 files changed, 4219 insertions, 1647 deletions
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 850a1d25339c..381efc10b416 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -227,7 +227,8 @@ public class BlobStoreManagerService extends SystemService {
int n = 0;
long sessionId;
do {
- sessionId = Math.abs(mRandom.nextLong());
+ final long randomLong = mRandom.nextLong();
+ sessionId = (randomLong == Long.MIN_VALUE) ? INVALID_BLOB_ID : Math.abs(randomLong);
if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != INVALID_BLOB_ID) {
return sessionId;
}
@@ -647,6 +648,17 @@ public class BlobStoreManagerService extends SystemService {
session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
+ // If the commit fails and this blob data didn't exist before, delete it.
+ // But if it is a recommit, just leave it as is.
+ if (session.getSessionId() == blob.getBlobId()) {
+ deleteBlobLocked(blob);
+ userBlobs.remove(blob.getBlobHandle());
+ }
+ }
+ // Delete redundant data from recommits.
+ if (session.getSessionId() != blob.getBlobId()) {
+ session.getSessionFile().delete();
+ mActiveBlobIds.remove(session.getSessionId());
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
@@ -1543,7 +1555,7 @@ public class BlobStoreManagerService extends SystemService {
public int handleShellCommand(@NonNull ParcelFileDescriptor in,
@NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
@NonNull String[] args) {
- return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this,
+ return new BlobStoreManagerShellCommand(BlobStoreManagerService.this).exec(this,
in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 51cf805aa000..77ca4aa7a9e6 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -100,7 +100,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
private File mSessionFile;
@GuardedBy("mRevocableFds")
- private ArrayList<RevocableFileDescriptor> mRevocableFds = new ArrayList<>();
+ private final ArrayList<RevocableFileDescriptor> mRevocableFds = new ArrayList<>();
// This will be accessed from only one thread at any point of time, so no need to grab
// a lock for this.
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 15a2f22e0fea..8a0f66040711 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -88,27 +88,4 @@ java_sdk_library {
"com.android.os.statsd",
"test_com.android.os.statsd",
],
-}
-
-android_test {
- name: "FrameworkStatsdTest",
- platform_apis: true,
- srcs: [
- // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
- ":framework-statsd-sources",
- "test/**/*.java",
- ],
- manifest: "test/AndroidManifest.xml",
- static_libs: [
- "androidx.test.rules",
- "truth-prebuilt",
- ],
- libs: [
- "android.test.runner.stubs",
- "android.test.base.stubs",
- ],
- test_suites: [
- "device-tests",
- ],
-}
-
+} \ No newline at end of file
diff --git a/apex/statsd/framework/test/Android.bp b/apex/statsd/framework/test/Android.bp
new file mode 100644
index 000000000000..b113d595b57c
--- /dev/null
+++ b/apex/statsd/framework/test/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "FrameworkStatsdTest",
+ platform_apis: true,
+ srcs: [
+ // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
+ ":framework-statsd-sources",
+ "**/*.java",
+ ],
+ manifest: "AndroidManifest.xml",
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+} \ No newline at end of file
diff --git a/apex/statsd/framework/test/AndroidTest.xml b/apex/statsd/framework/test/AndroidTest.xml
new file mode 100644
index 000000000000..fb519150ecd5
--- /dev/null
+++ b/apex/statsd/framework/test/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Tests for Statsd.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworkStatsdTest.apk" />
+ <option name="install-arg" value="-g" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="mts" />
+ <option name="test-tag" value="FrameworkStatsdTest" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.os.statsd.framework.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+ </object>
+</configuration> \ No newline at end of file
diff --git a/api/test-current.txt b/api/test-current.txt
index 795d873ec69c..fc82bc788ac7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5103,6 +5103,7 @@ package android.view {
method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
method public int getType();
method public boolean hasAccess(int);
+ field public static final int FLAG_TRUSTED = 128; // 0x80
field public static final int TYPE_EXTERNAL = 2; // 0x2
field public static final int TYPE_INTERNAL = 1; // 0x1
field public static final int TYPE_OVERLAY = 4; // 0x4
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 2adbc1f6e1ae..7c30c8b1e1dd 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -17,8 +17,8 @@
package com.android.commands.am;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
-import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
@@ -512,7 +512,7 @@ public class Instrument {
flags |= INSTR_FLAG_DISABLE_TEST_API_CHECKS;
}
if (disableIsolatedStorage) {
- flags |= INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
+ flags |= INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
}
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 278278fc18c4..042983be9ab7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -481,6 +481,8 @@ message Atom {
BlobCommitted blob_committed = 298 [(module) = "framework"];
BlobLeased blob_leased = 299 [(module) = "framework"];
BlobOpened blob_opened = 300 [(module) = "framework"];
+ ContactsProviderStatusReported contacts_provider_status_reported = 301;
+ KeystoreKeyEventReported keystore_key_event_reported = 302;
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -9907,6 +9909,48 @@ message TvCasSessionOpenStatus {
}
/**
+ * Logs for ContactsProvider general usage.
+ * This is atom ID 301.
+ *
+ * Logged from:
+ * packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
+ */
+message ContactsProviderStatusReported {
+ enum ApiType {
+ UNKNOWN_API = 0;
+ QUERY = 1;
+ // INSERT includes insert and bulkInsert, and inserts triggered by applyBatch.
+ INSERT = 2;
+ // UPDATE and DELETE includes update/delete and the ones triggered by applyBatch.
+ UPDATE = 3;
+ DELETE = 4;
+ }
+
+ enum ResultType {
+ UNKNOWN_RESULT = 0;
+ SUCCESS = 1;
+ FAIL = 2;
+ ILLEGAL_ARGUMENT = 3;
+ UNSUPPORTED_OPERATION = 4;
+ }
+
+ enum CallerType {
+ UNSPECIFIED_CALLER_TYPE = 0;
+ CALLER_IS_SYNC_ADAPTER = 1;
+ CALLER_IS_NOT_SYNC_ADAPTER = 2;
+ }
+
+ optional ApiType api_type = 1;
+ // Defined in
+ // packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java
+ optional int32 uri_type = 2;
+ optional CallerType caller_type = 3;
+ optional ResultType result_type = 4;
+ optional int32 result_count = 5;
+ optional int64 latency_micros = 6;
+}
+
+/**
* Logs when an app is frozen or unfrozen.
*
* Logged from:
@@ -10883,6 +10927,114 @@ message MediametricsAudioDeviceConnectionReported {
optional int32 connection_count = 6;
}
+/**
+ * Logs: i) creation of different types of cryptographic keys in the keystore,
+ * ii) operations performed using the keys,
+ * iii) attestation of the keys
+ * Logged from: system/security/keystore/key_event_log_handler.cpp
+ */
+message KeystoreKeyEventReported {
+
+ enum Algorithm {
+ /** Asymmetric algorithms. */
+ RSA = 1;
+ // 2 removed, do not reuse.
+ EC = 3;
+ /** Block cipher algorithms */
+ AES = 32;
+ TRIPLE_DES = 33;
+ /** MAC algorithms */
+ HMAC = 128;
+ };
+ /** Algorithm associated with the key */
+ optional Algorithm algorithm = 1;
+
+ /** Size of the key */
+ optional int32 key_size = 2;
+
+ enum KeyOrigin {
+ /** Generated in keymaster. Should not exist outside the TEE. */
+ GENERATED = 0;
+ /** Derived inside keymaster. Likely exists off-device. */
+ DERIVED = 1;
+ /** Imported into keymaster. Existed as cleartext in Android. */
+ IMPORTED = 2;
+ /** Keymaster did not record origin. */
+ UNKNOWN = 3;
+ /** Securely imported into Keymaster. */
+ SECURELY_IMPORTED = 4;
+ };
+ /* Logs whether the key was generated, imported, securely imported, or derived.*/
+ optional KeyOrigin key_origin = 3;
+
+ enum HardwareAuthenticatorType {
+ NONE = 0;
+ PASSWORD = 1;
+ FINGERPRINT = 2;
+ // Additional entries must be powers of 2.
+ };
+ /**
+ * What auth types does this key require? If none,
+ * then no auth required.
+ */
+ optional HardwareAuthenticatorType user_auth_type = 4;
+
+ /**
+ * If user authentication is required, is the requirement time based? If it
+ * is not time based then this field will not be used and the key is per
+ * operation. Per operation keys must be user authenticated on each usage.
+ */
+ optional int32 user_auth_key_timeout_secs = 5;
+
+ /**
+ * padding mode, digest, block_mode and purpose should ideally be repeated
+ * fields. However, since statsd does not support repeated fields in
+ * pushed atoms, they are represented using bitmaps.
+ */
+
+ /** Track which padding mode is being used.*/
+ optional int32 padding_mode_bitmap = 6;
+
+ /** Track which digest is being used. */
+ optional int32 digest_bitmap = 7;
+
+ /** Track what block mode is being used (for encryption). */
+ optional int32 block_mode_bitmap = 8;
+
+ /** Track what purpose is this key serving. */
+ optional int32 purpose_bitmap = 9;
+
+ enum EcCurve {
+ P_224 = 0;
+ P_256 = 1;
+ P_384 = 2;
+ P_521 = 3;
+ };
+ /** Which ec curve was selected if elliptic curve cryptography is in use **/
+ optional EcCurve ec_curve = 10;
+
+ enum KeyBlobUsageRequirements {
+ STANDALONE = 0;
+ REQUIRES_FILE_SYSTEM = 1;
+ };
+ /** Standalone or is a file system required */
+ optional KeyBlobUsageRequirements key_blob_usage_reqs = 11;
+
+ enum Type {
+ key_operation = 0;
+ key_creation = 1;
+ key_attestation = 2;
+ }
+ /** Key creation event, operation event or attestation event? */
+ optional Type type = 12;
+
+ /** Was the key creation, operation, or attestation successful? */
+ optional bool was_successful = 13;
+
+ /** Response code or error code */
+ optional int32 error_code = 14;
+}
+
// Blob Committer stats
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 23f8ca4e74e6..a21eb9b9147f 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -33,6 +33,12 @@ namespace android {
namespace os {
namespace statsd {
+// These constants must be kept in sync with those in StatsDimensionsValue.java.
+const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
+const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
+const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
+const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
+
namespace {
void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
const vector<int>& attributionUids, const vector<string>& attributionTags,
@@ -291,34 +297,76 @@ TEST(AtomMatcherTest, TestWriteDimensionPath) {
}
}
-//TODO(b/149050405) Update this test for StatsDimensionValueParcel
-//TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
-// HashableDimensionKey dim;
-//
-// int pos1[] = {1, 1, 1};
-// int pos2[] = {1, 1, 2};
-// int pos3[] = {1, 1, 3};
-// int pos4[] = {2, 0, 0};
-//
-// Field field1(10, pos1, 2);
-// Field field2(10, pos2, 2);
-// Field field3(10, pos3, 2);
-// Field field4(10, pos4, 0);
-//
-// Value value1((int32_t)10025);
-// Value value2("tag");
-// Value value3((int32_t)987654);
-// Value value4((int32_t)99999);
-//
-// dim.addValue(FieldValue(field1, value1));
-// dim.addValue(FieldValue(field2, value2));
-// dim.addValue(FieldValue(field3, value3));
-// dim.addValue(FieldValue(field4, value4));
-//
-// SubscriberReporter::getStatsDimensionsValue(dim);
-// // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
-// // have any read api.
-//}
+void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel,
+ int32_t nodeDepthInAttributionChain,
+ int32_t uid, string tag) {
+ EXPECT_EQ(attributionNodeParcel.field, nodeDepthInAttributionChain /*position at depth 1*/);
+ ASSERT_EQ(attributionNodeParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
+ ASSERT_EQ(attributionNodeParcel.tupleValue.size(), 2);
+
+ StatsDimensionsValueParcel uidParcel = attributionNodeParcel.tupleValue[0];
+ EXPECT_EQ(uidParcel.field, 1 /*position at depth 2*/);
+ EXPECT_EQ(uidParcel.valueType, STATS_DIMENSIONS_VALUE_INT_TYPE);
+ EXPECT_EQ(uidParcel.intValue, uid);
+
+ StatsDimensionsValueParcel tagParcel = attributionNodeParcel.tupleValue[1];
+ EXPECT_EQ(tagParcel.field, 2 /*position at depth 2*/);
+ EXPECT_EQ(tagParcel.valueType, STATS_DIMENSIONS_VALUE_STRING_TYPE);
+ EXPECT_EQ(tagParcel.stringValue, tag);
+}
+
+// Test conversion of a HashableDimensionKey into a StatsDimensionValueParcel
+TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
+ int atomId = 10;
+ // First four fields form an attribution chain
+ int pos1[] = {1, 1, 1};
+ int pos2[] = {1, 1, 2};
+ int pos3[] = {1, 2, 1};
+ int pos4[] = {1, 2, 2};
+ int pos5[] = {2, 1, 1};
+
+ Field field1(atomId, pos1, /*depth=*/2);
+ Field field2(atomId, pos2, /*depth=*/2);
+ Field field3(atomId, pos3, /*depth=*/2);
+ Field field4(atomId, pos4, /*depth=*/2);
+ Field field5(atomId, pos5, /*depth=*/0);
+
+ Value value1((int32_t)1);
+ Value value2("string2");
+ Value value3((int32_t)3);
+ Value value4("string4");
+ Value value5((float)5.0);
+
+ HashableDimensionKey dimensionKey;
+ dimensionKey.addValue(FieldValue(field1, value1));
+ dimensionKey.addValue(FieldValue(field2, value2));
+ dimensionKey.addValue(FieldValue(field3, value3));
+ dimensionKey.addValue(FieldValue(field4, value4));
+ dimensionKey.addValue(FieldValue(field5, value5));
+
+ StatsDimensionsValueParcel rootParcel = dimensionKey.toStatsDimensionsValueParcel();
+ EXPECT_EQ(rootParcel.field, atomId);
+ ASSERT_EQ(rootParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
+ ASSERT_EQ(rootParcel.tupleValue.size(), 2);
+
+ // Check that attribution chain is populated correctly
+ StatsDimensionsValueParcel attributionChainParcel = rootParcel.tupleValue[0];
+ EXPECT_EQ(attributionChainParcel.field, 1 /*position at depth 0*/);
+ ASSERT_EQ(attributionChainParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
+ ASSERT_EQ(attributionChainParcel.tupleValue.size(), 2);
+ checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[0],
+ /*nodeDepthInAttributionChain=*/1,
+ value1.int_value, value2.str_value);
+ checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[1],
+ /*nodeDepthInAttributionChain=*/2,
+ value3.int_value, value4.str_value);
+
+ // Check that the float is populated correctly
+ StatsDimensionsValueParcel floatParcel = rootParcel.tupleValue[1];
+ EXPECT_EQ(floatParcel.field, 2 /*position at depth 0*/);
+ EXPECT_EQ(floatParcel.valueType, STATS_DIMENSIONS_VALUE_FLOAT_TYPE);
+ EXPECT_EQ(floatParcel.floatValue, value5.float_value);
+}
TEST(AtomMatcherTest, TestWriteDimensionToProto) {
HashableDimensionKey dim;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b608a343fc7d..acf6315ddc5d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -159,10 +159,10 @@ public class ActivityManager {
*/
public static final int INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
/**
- * Mount full external storage for the newly started instrumentation.
+ * Grant full access to the external storage for the newly started instrumentation.
* @hide
*/
- public static final int INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL = 1 << 1;
+ public static final int INSTR_FLAG_DISABLE_ISOLATED_STORAGE = 1 << 1;
/**
* Disable test API access for the newly started instrumentation.
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index d650bbcdfa33..b749c3504811 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -360,18 +360,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
}
/**
- * Release this container. Activity launching will no longer be permitted.
- * <p>Note: Calling this method is allowed after
- * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
- * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
- *
- * @see StateCallback
+ * Release this container if it is initialized. Activity launching will no longer be permitted.
*/
public void release() {
- if (!mTaskEmbedder.isInitialized()) {
- throw new IllegalStateException(
- "Trying to release container that is not initialized.");
- }
performRelease();
}
@@ -487,7 +478,9 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd
return;
}
mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
- mTaskEmbedder.release();
+ if (mTaskEmbedder.isInitialized()) {
+ mTaskEmbedder.release();
+ }
mTaskEmbedder.setListener(null);
mGuard.close();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index e620f1641acd..71b866b7b16a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1122,8 +1122,11 @@ public class AppOpsManager {
AppProtoEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER;
/** @hide */
+ public static final int OP_NO_ISOLATED_STORAGE = AppProtoEnums.APP_OP_NO_ISOLATED_STORAGE;
+
+ /** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 99;
+ public static final int _NUM_OP = 100;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1434,6 +1437,12 @@ public class AppOpsManager {
@SystemApi
public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
+ /**
+ * AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage}
+ *
+ * @hide
+ */
+ public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -1623,6 +1632,7 @@ public class AppOpsManager {
OP_DEPRECATED_1, // deprecated
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ OP_NO_ISOLATED_STORAGE, // NO_ISOLATED_STORAGE
};
/**
@@ -1728,6 +1738,7 @@ public class AppOpsManager {
"", // deprecated
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
+ OPSTR_NO_ISOLATED_STORAGE,
};
/**
@@ -1834,6 +1845,7 @@ public class AppOpsManager {
"deprecated",
"AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
"AUTO_REVOKE_MANAGED_BY_INSTALLER",
+ "NO_ISOLATED_STORAGE",
};
/**
@@ -1941,6 +1953,7 @@ public class AppOpsManager {
null, // deprecated operation
null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ null, // no permission for OP_NO_ISOLATED_STORAGE
};
/**
@@ -2048,6 +2061,7 @@ public class AppOpsManager {
null, // deprecated operation
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
+ null, // NO_ISOLATED_STORAGE
};
/**
@@ -2154,6 +2168,7 @@ public class AppOpsManager {
null, // deprecated operation
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
+ null, // NO_ISOLATED_STORAGE
};
/**
@@ -2259,6 +2274,7 @@ public class AppOpsManager {
AppOpsManager.MODE_IGNORED, // deprecated operation
AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
};
/**
@@ -2368,6 +2384,7 @@ public class AppOpsManager {
false, // deprecated operation
false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
+ true, // NO_ISOLATED_STORAGE
};
/**
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 24be45cb20fe..8e687413b7e1 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -51,13 +51,6 @@ public class BluetoothDeviceFilterUtils {
return s == null ? null : Pattern.compile(s);
}
- static boolean matches(ScanFilter filter, BluetoothDevice device) {
- boolean result = matchesAddress(filter.getDeviceAddress(), device)
- && matchesServiceUuid(filter.getServiceUuid(), filter.getServiceUuidMask(), device);
- if (DEBUG) debugLogMatchResult(result, device, filter);
- return result;
- }
-
static boolean matchesAddress(String deviceAddress, BluetoothDevice device) {
final boolean result = deviceAddress == null
|| (device != null && deviceAddress.equals(device.getAddress()));
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index dccfb0346c9c..8c071fe99104 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -37,7 +37,6 @@ import android.util.Log;
import com.android.internal.util.BitUtils;
import com.android.internal.util.ObjectUtils;
-import com.android.internal.util.Preconditions;
import libcore.util.HexEncoding;
@@ -166,21 +165,18 @@ public final class BluetoothLeDeviceFilter implements DeviceFilter<ScanResult> {
/** @hide */
@Override
- public boolean matches(ScanResult device) {
- boolean result = matches(device.getDevice())
+ public boolean matches(ScanResult scanResult) {
+ BluetoothDevice device = scanResult.getDevice();
+ boolean result = getScanFilter().matches(scanResult)
+ && BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device)
&& (mRawDataFilter == null
- || BitUtils.maskedEquals(device.getScanRecord().getBytes(),
+ || BitUtils.maskedEquals(scanResult.getScanRecord().getBytes(),
mRawDataFilter, mRawDataFilterMask));
if (DEBUG) Log.i(LOG_TAG, "matches(this = " + this + ", device = " + device +
") -> " + result);
return result;
}
- private boolean matches(BluetoothDevice device) {
- return BluetoothDeviceFilterUtils.matches(getScanFilter(), device)
- && BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device);
- }
-
/** @hide */
@Override
public int getMediumType() {
diff --git a/core/java/android/content/pm/dex/ArtManagerInternal.java b/core/java/android/content/pm/dex/ArtManagerInternal.java
index 62ab9e02f858..23fef29803e7 100644
--- a/core/java/android/content/pm/dex/ArtManagerInternal.java
+++ b/core/java/android/content/pm/dex/ArtManagerInternal.java
@@ -30,5 +30,5 @@ public abstract class ArtManagerInternal {
* in executes using the specified {@code abi}.
*/
public abstract PackageOptimizationInfo getPackageOptimizationInfo(
- ApplicationInfo info, String abi);
+ ApplicationInfo info, String abi, String activityName);
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 570cc2c11738..2d2dda04b146 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -26,6 +26,8 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.util.Slog;
/**
@@ -82,6 +84,9 @@ public class BiometricManager {
*
* <p>Types may combined via bitwise OR into a single integer representing multiple
* authenticators (e.g. <code>DEVICE_CREDENTIAL | BIOMETRIC_WEAK</code>).
+ *
+ * @see #canAuthenticate(int)
+ * @see BiometricPrompt.Builder#setAllowedAuthenticators(int)
*/
public interface Authenticators {
/**
@@ -118,6 +123,10 @@ public class BiometricManager {
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
* requirements for <strong>Tier 3</strong> (formerly <strong>Strong</strong>), as defined
* by the Android CDD.
+ *
+ * <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
+ *
+ * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
*/
int BIOMETRIC_STRONG = 0x000F;
@@ -156,6 +165,11 @@ public class BiometricManager {
* The non-biometric credential used to secure the device (i.e., PIN, pattern, or password).
* This should typically only be used in combination with a biometric auth type, such as
* {@link #BIOMETRIC_WEAK}.
+ *
+ * <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key
+ * generation.
+ *
+ * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
*/
int DEVICE_CREDENTIAL = 1 << 15;
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 5af7cef3e2b4..74caceae07c9 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -36,6 +36,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -371,6 +373,14 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* button on the prompt, making it an error to also call
* {@link #setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
*
+ * <p>If unlocking cryptographic operation(s), it is the application's responsibility to
+ * request authentication with the proper set of authenticators (e.g. match the
+ * authenticators specified during key generation).
+ *
+ * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see KeyProperties#AUTH_BIOMETRIC_STRONG
+ * @see KeyProperties#AUTH_DEVICE_CREDENTIAL
+ *
* @param authenticators A bit field representing all valid authenticator types that may be
* invoked by the prompt.
* @return This builder.
@@ -606,8 +616,24 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
- * A wrapper class for the crypto objects supported by BiometricPrompt. Currently the framework
- * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * A wrapper class for the cryptographic operations supported by BiometricPrompt.
+ *
+ * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
+ * {@link IdentityCredential}.
+ *
+ * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
+ * time-based. This is specified during key creation via the timeout parameter of the
+ * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API.
+ *
+ * <p>CryptoObjects are used to unlock auth-per-use keys via
+ * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)}, whereas time-based keys are unlocked for their specified duration
+ * any time the user authenticates with the specified authenticators (e.g. unlocking keyguard).
+ * If a time-based key is not available for use (i.e. none of the allowed authenticators have
+ * been unlocked recently), applications can prompt the user to authenticate via
+ * {@link BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
+ *
+ * @see Builder#setAllowedAuthenticators(int)
*/
public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
public CryptoObject(@NonNull Signature signature) {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index ea5cc7f2e8bc..c1ba2094d3cf 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -303,13 +303,25 @@ public final class DisplayManager {
/**
* Virtual display flag: Indicates that the display should support system decorations. Virtual
* displays without this flag shouldn't show home, IME or any other system decorations.
+ * <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
*
* @see #createVirtualDisplay
+ * @see #VIRTUAL_DISPLAY_FLAG_TRUSTED
* @hide
*/
// TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9;
+ /**
+ * Virtual display flags: Indicates that the display is trusted to show system decorations and
+ * receive inputs without users' touch.
+ *
+ * @see #createVirtualDisplay
+ * @see #VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a3fd60e9d3b0..004f84422b44 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -900,9 +900,17 @@ public final class NetworkCapabilities implements Parcelable {
* <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be
* reset to Process.INVALID_UID unless all the following conditions are met:
*
+ * <p>The caller is the network owner, AND one of the following sets of requirements is met:
+ *
+ * <ol>
+ * <li>The described Network is a VPN
+ * </ol>
+ *
+ * <p>OR:
+ *
* <ol>
- * <li>The destination app is the network owner
- * <li>The destination app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The calling app is the network owner
+ * <li>The calling app has the ACCESS_FINE_LOCATION permission granted
* <li>The user's location toggle is on
* </ol>
*
@@ -928,7 +936,16 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Retrieves the UID of the app that owns this network.
*
- * <p>For user privacy reasons, this field will only be populated if:
+ * <p>For user privacy reasons, this field will only be populated if the following conditions
+ * are met:
+ *
+ * <p>The caller is the network owner, AND one of the following sets of requirements is met:
+ *
+ * <ol>
+ * <li>The described Network is a VPN
+ * </ol>
+ *
+ * <p>OR:
*
* <ol>
* <li>The calling app is the network owner
@@ -936,8 +953,8 @@ public final class NetworkCapabilities implements Parcelable {
* <li>The user's location toggle is on
* </ol>
*
- * Instances of NetworkCapabilities sent to apps without the appropriate permissions will
- * have this field cleared out.
+ * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
+ * this field cleared out.
*/
public int getOwnerUid() {
return mOwnerUid;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index e550f85e6b9a..98760761736d 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -26,7 +26,6 @@ import android.net.util.NetUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -554,15 +553,45 @@ public final class RouteInfo implements Parcelable {
}
/**
- * A helper class that contains the destination and the gateway in a {@code RouteInfo},
- * used by {@link ConnectivityService#updateRoutes} or
+ * A helper class that contains the destination, the gateway and the interface in a
+ * {@code RouteInfo}, used by {@link ConnectivityService#updateRoutes} or
* {@link LinkProperties#addRoute} to calculate the list to be updated.
+ * {@code RouteInfo} objects with different interfaces are treated as different routes because
+ * *usually* on Android different interfaces use different routing tables, and moving a route
+ * to a new routing table never constitutes an update, but is always a remove and an add.
*
* @hide
*/
- public static class RouteKey extends Pair<IpPrefix, InetAddress> {
- RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway) {
- super(destination, gateway);
+ public static class RouteKey {
+ @NonNull private final IpPrefix mDestination;
+ @Nullable private final InetAddress mGateway;
+ @Nullable private final String mInterface;
+
+ RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
+ mDestination = destination;
+ mGateway = gateway;
+ mInterface = iface;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof RouteKey)) {
+ return false;
+ }
+ RouteKey p = (RouteKey) o;
+ // No need to do anything special for scoped addresses. Inet6Address#equals does not
+ // consider the scope ID, but the netd route IPCs (e.g., INetd#networkAddRouteParcel)
+ // and the kernel ignore scoped addresses both in the prefix and in the nexthop and only
+ // look at RTA_OIF.
+ return Objects.equals(p.mDestination, mDestination)
+ && Objects.equals(p.mGateway, mGateway)
+ && Objects.equals(p.mInterface, mInterface);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDestination, mGateway, mInterface);
}
}
@@ -574,7 +603,7 @@ public final class RouteInfo implements Parcelable {
*/
@NonNull
public RouteKey getRouteKey() {
- return new RouteKey(mDestination, mGateway);
+ return new RouteKey(mDestination, mGateway, mInterface);
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index e8806a03d00e..0abf8ae352af 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,9 +16,11 @@
package android.os.storage;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO;
import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES;
@@ -1853,7 +1855,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionReadAudio(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
return false;
}
@@ -1864,7 +1866,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionWriteAudio(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
return false;
}
@@ -1875,7 +1877,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionReadVideo(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
return false;
}
@@ -1886,7 +1888,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionWriteVideo(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
return false;
}
@@ -1897,7 +1899,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionReadImages(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) {
return false;
}
@@ -1908,7 +1910,7 @@ public class StorageManager {
/** {@hide} */
public boolean checkPermissionWriteImages(boolean enforce,
int pid, int uid, String packageName, @Nullable String featureId) {
- if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId,
+ if (!checkExternalStoragePermissionAndAppOp(enforce, pid, uid, packageName, featureId,
WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) {
return false;
}
@@ -1916,6 +1918,24 @@ public class StorageManager {
OP_WRITE_MEDIA_IMAGES);
}
+ private boolean checkExternalStoragePermissionAndAppOp(boolean enforce,
+ int pid, int uid, String packageName, @Nullable String featureId, String permission,
+ int op) {
+ // First check if app has MANAGE_EXTERNAL_STORAGE.
+ final int mode = mAppOps.noteOpNoThrow(OP_MANAGE_EXTERNAL_STORAGE, uid, packageName,
+ featureId, null);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ return true;
+ }
+ if (mode == AppOpsManager.MODE_DEFAULT && mContext.checkPermission(
+ MANAGE_EXTERNAL_STORAGE, pid, uid) == PERMISSION_GRANTED) {
+ return true;
+ }
+ // If app doesn't have MANAGE_EXTERNAL_STORAGE, then check if it has requested granular
+ // permission.
+ return checkPermissionAndAppOp(enforce, pid, uid, packageName, featureId, permission, op);
+ }
+
/** {@hide} */
@VisibleForTesting
public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4f0a9728fcf8..cd2467f9157d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1972,6 +1972,10 @@ public final class Settings {
* Input: Nothing.
* <p>
* Output: Nothing.
+ * <p class="note">
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index c2234bad3803..95cc64ae8aab 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -564,9 +564,9 @@ public abstract class AugmentedAutofillService extends Service {
}
void reportResult(@Nullable List<Dataset> inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ @Nullable Bundle clientState, boolean showingFillWindow) {
try {
- mCallback.onSuccess(inlineSuggestionsData, clientState);
+ mCallback.onSuccess(inlineSuggestionsData, clientState, showingFillWindow);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 8ba5c173890c..fc3baf1c9836 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -56,23 +56,24 @@ public final class FillCallback {
if (response == null) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
- mProxy.reportResult(/* inlineSuggestionsData */ null, /* clientState */ null);
+ mProxy.reportResult(/* inlineSuggestionsData */ null, /* clientState */
+ null, /* showingFillWindow */ false);
return;
}
- List<Dataset> inlineSuggestions = response.getInlineSuggestions();
- Bundle clientState = response.getClientState();
- // We need to report result regardless of whether inline suggestions are returned or not.
- mProxy.reportResult(inlineSuggestions, clientState);
+ final List<Dataset> inlineSuggestions = response.getInlineSuggestions();
+ final Bundle clientState = response.getClientState();
+ final FillWindow fillWindow = response.getFillWindow();
+ boolean showingFillWindow = false;
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
- return;
- }
-
- final FillWindow fillWindow = response.getFillWindow();
- if (fillWindow != null) {
+ } else if (fillWindow != null) {
fillWindow.show();
+ showingFillWindow = true;
}
+ // We need to report result regardless of whether inline suggestions are returned or not.
+ mProxy.reportResult(inlineSuggestions, clientState, showingFillWindow);
+
// TODO(b/123099468): must notify the server so it can update the session state to avoid
// showing conflicting UIs (for example, if a new request is made to the main autofill
// service and it now wants to show something).
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 609e382e2b96..4dfdd4db27e5 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -30,7 +30,9 @@ import java.util.List;
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable List<Dataset> inlineSuggestionsData, in @nullable Bundle clientState);
+ void onSuccess(in @nullable List<Dataset> inlineSuggestionsData,
+ in @nullable Bundle clientState,
+ boolean showingFillWindow);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index ef21900dc3e3..4adcd6948f85 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -219,6 +219,9 @@ public class PhoneStateListener {
/**
* Listen for changes to observed cell info.
*
+ * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission.
+ *
* @see #onCellInfoChanged
*/
public static final int LISTEN_CELL_INFO = 0x00000400;
@@ -461,6 +464,9 @@ public class PhoneStateListener {
* <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
* the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
* @see #onRegistrationFailed
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -472,6 +478,9 @@ public class PhoneStateListener {
* <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
* the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
* @see #onBarringInfoChanged
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -569,6 +578,11 @@ public class PhoneStateListener {
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
+ * The instance of {@link ServiceState} passed as an argument here will have various levels of
+ * location information stripped from it depending on the location permissions that your app
+ * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+ * receive all the information in {@link ServiceState}.
+ *
* @see ServiceState#STATE_EMERGENCY_ONLY
* @see ServiceState#STATE_IN_SERVICE
* @see ServiceState#STATE_OUT_OF_SERVICE
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8db1703a627f..0cc469a2d5eb 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -241,13 +241,26 @@ public final class Display {
* This flag identifies secondary displays that should show system decorations, such as status
* bar, navigation bar, home activity or IME.
* </p>
+ * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
*
+ * @see #getFlags()
* @hide
*/
// TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6;
/**
+ * Flag: The display is trusted to show system decorations and receive inputs without users'
+ * touch.
+ * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ *
+ * @see #getFlags()
+ * @hide
+ */
+ @TestApi
+ public static final int FLAG_TRUSTED = 1 << 7;
+
+ /**
* Display flag: Indicates that the contents of the display should not be scaled
* to fit the physical screen dimensions. Used for development only to emulate
* devices with smaller physicals screens while preserving density.
@@ -564,6 +577,7 @@ public final class Display {
* @see #FLAG_SUPPORTS_PROTECTED_BUFFERS
* @see #FLAG_SECURE
* @see #FLAG_PRIVATE
+ * @see #FLAG_ROUND
*/
public int getFlags() {
return mFlags;
@@ -1222,6 +1236,16 @@ public final class Display {
Display.FLAG_PRESENTATION;
}
+ /**
+ * @return {@code true} if the display is a trusted display.
+ *
+ * @see #FLAG_TRUSTED
+ * @hide
+ */
+ public boolean isTrusted() {
+ return (mFlags & FLAG_TRUSTED) == FLAG_TRUSTED;
+ }
+
private void updateDisplayInfoLocked() {
// Note: The display manager caches display info objects on our behalf.
DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId);
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d369883f3ac3..b1ede4102bec 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -717,6 +717,15 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_ROUND) != 0) {
result.append(", FLAG_ROUND");
}
+ if ((flags & Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
+ result.append(", FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD");
+ }
+ if ((flags & Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ result.append(", FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS");
+ }
+ if ((flags & Display.FLAG_TRUSTED) != 0) {
+ result.append(", FLAG_TRUSTED");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 9a9396c45b66..b4dae566ce2a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -260,6 +260,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private final boolean mHasAnimationCallbacks;
private final @InsetsType int mRequestedTypes;
private final long mDurationMs;
+ private final boolean mDisable;
private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
new ThreadLocal<AnimationHandler>() {
@@ -272,11 +273,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
};
public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks,
- int requestedTypes) {
+ int requestedTypes, boolean disable) {
mShow = show;
mHasAnimationCallbacks = hasAnimationCallbacks;
mRequestedTypes = requestedTypes;
mDurationMs = calculateDurationMs();
+ mDisable = disable;
}
@Override
@@ -284,6 +286,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
mController = controller;
if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
+ if (mDisable) {
+ onAnimationFinish();
+ return;
+ }
mAnimator = ValueAnimator.ofFloat(0f, 1f);
mAnimator.setDuration(mDurationMs);
mAnimator.setInterpolator(new LinearInterpolator());
@@ -477,6 +483,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private DisplayCutout mLastDisplayCutout;
private boolean mStartingAnimation;
private int mCaptionInsetsHeight = 0;
+ private boolean mAnimationsDisabled;
private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
@@ -1163,8 +1170,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
- final InternalAnimationControlListener listener =
- new InternalAnimationControlListener(show, hasAnimationCallbacks, types);
+ final InternalAnimationControlListener listener = new InternalAnimationControlListener(
+ show, hasAnimationCallbacks, types, mAnimationsDisabled);
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
@@ -1279,6 +1286,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return mHost.getSystemBarsBehavior();
}
+ @Override
+ public void setAnimationsDisabled(boolean disable) {
+ mAnimationsDisabled = disable;
+ }
+
private @InsetsType int calculateControllableTypes() {
@InsetsType int result = 0;
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 3aa246441dbc..565846638acc 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -18,8 +18,8 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
-import static android.view.InsetsController.DEBUG;
import static android.view.InsetsState.getDefaultVisibility;
+import static android.view.InsetsController.DEBUG;
import static android.view.InsetsState.toPublicType;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 0283ada0dd40..c018d1cf1782 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -38,6 +38,7 @@ public class PendingInsetsController implements WindowInsetsController {
private @Appearance int mAppearance;
private @Appearance int mAppearanceMask;
private @Behavior int mBehavior = KEEP_BEHAVIOR;
+ private boolean mAnimationsDisabled;
private final InsetsState mDummyState = new InsetsState();
private InsetsController mReplayedInsetsController;
private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
@@ -103,6 +104,15 @@ public class PendingInsetsController implements WindowInsetsController {
}
@Override
+ public void setAnimationsDisabled(boolean disable) {
+ if (mReplayedInsetsController != null) {
+ mReplayedInsetsController.setAnimationsDisabled(disable);
+ } else {
+ mAnimationsDisabled = disable;
+ }
+ }
+
+ @Override
public InsetsState getState() {
return mDummyState;
}
@@ -151,6 +161,9 @@ public class PendingInsetsController implements WindowInsetsController {
if (mCaptionInsetsHeight != 0) {
controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
}
+ if (mAnimationsDisabled) {
+ controller.setAnimationsDisabled(true);
+ }
int size = mRequests.size();
for (int i = 0; i < size; i++) {
mRequests.get(i).replay(controller);
@@ -167,6 +180,7 @@ public class PendingInsetsController implements WindowInsetsController {
mBehavior = KEEP_BEHAVIOR;
mAppearance = 0;
mAppearanceMask = 0;
+ mAnimationsDisabled = false;
// After replaying, we forward everything directly to the replayed instance.
mReplayedInsetsController = controller;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6f73e8985a8a..e455155a319e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -230,7 +230,6 @@ public final class SurfaceControl implements Parcelable {
*/
public long mNativeObject;
private long mNativeHandle;
- private Throwable mReleaseStack = null;
// TODO: Move this to native.
private final Object mSizeLock = new Object();
@@ -442,13 +441,6 @@ public final class SurfaceControl implements Parcelable {
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
- if (mNativeObject == 0) {
- if (Build.IS_DEBUGGABLE) {
- mReleaseStack = new Throwable("assigned zero nativeObject here");
- }
- } else {
- mReleaseStack = null;
- }
}
/**
@@ -1024,22 +1016,11 @@ public final class SurfaceControl implements Parcelable {
nativeRelease(mNativeObject);
mNativeObject = 0;
mNativeHandle = 0;
- if (Build.IS_DEBUGGABLE) {
- mReleaseStack = new Throwable("released here");
- }
mCloseGuard.close();
}
}
/**
- * Returns the call stack that assigned mNativeObject to zero.
- * @hide
- */
- public Throwable getReleaseStack() {
- return mReleaseStack;
- }
-
- /**
* Disconnect any client still connected to the surface.
* @hide
*/
@@ -1050,11 +1031,8 @@ public final class SurfaceControl implements Parcelable {
}
private void checkNotReleased() {
- if (mNativeObject == 0) {
- Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
- throw new NullPointerException(
- "mNativeObject of " + this + " is null. Have you called release() already?");
- }
+ if (mNativeObject == 0) throw new NullPointerException(
+ "Invalid " + this + ", mNativeObject is null. Have you called release() already?");
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index c098fae11b8c..809a9cfde587 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1620,9 +1620,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private void updateRelativeZ(Transaction t) {
- SurfaceControl viewRoot = getViewRootImpl().getSurfaceControl();
- t.setRelativeLayer(mBackgroundControl, viewRoot, Integer.MIN_VALUE);
- t.setRelativeLayer(mSurfaceControl, viewRoot, mSubLayer);
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot == null) {
+ // We were just detached.
+ return;
+ }
+ final SurfaceControl viewRootControl = viewRoot.getSurfaceControl();
+ t.setRelativeLayer(mBackgroundControl, viewRootControl, Integer.MIN_VALUE);
+ t.setRelativeLayer(mSurfaceControl, viewRootControl, mSubLayer);
}
/**
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 3d348efc7f0f..1a9003581078 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -222,6 +222,13 @@ public interface WindowInsetsController {
@Behavior int getSystemBarsBehavior();
/**
+ * Disables or enables the animations.
+ *
+ * @hide
+ */
+ void setAnimationsDisabled(boolean disable);
+
+ /**
* @hide
*/
InsetsState getState();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 99013aa32ce6..dc4c8fd76e8e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1070,6 +1070,10 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ ViewPager viewPager = findViewById(R.id.profile_pager);
+ if (shouldShowTabs() && viewPager.isLayoutRtl()) {
+ mMultiProfilePagerAdapter.setupViewPager(viewPager);
+ }
mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
adjustPreviewWidth(newConfig.orientation, null);
@@ -2807,17 +2811,7 @@ public class ChooserActivity extends ResolverActivity implements
|| chooserListAdapter.mDisplayList.isEmpty()) {
chooserListAdapter.notifyDataSetChanged();
} else {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... voids) {
- chooserListAdapter.updateAlphabeticalList();
- return null;
- }
- @Override
- protected void onPostExecute(Void aVoid) {
- chooserListAdapter.notifyDataSetChanged();
- }
- }.execute();
+ chooserListAdapter.updateAlphabeticalList();
}
// don't support direct share on low ram devices
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index d6ff7b13c934..05169023000f 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -275,33 +275,43 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
void updateAlphabeticalList() {
- mSortedList.clear();
- List<DisplayResolveInfo> tempList = new ArrayList<>();
- tempList.addAll(mDisplayList);
- tempList.addAll(mCallerTargets);
- if (mEnableStackedApps) {
- // Consolidate multiple targets from same app.
- Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
- for (DisplayResolveInfo info : tempList) {
- String packageName = info.getResolvedComponentName().getPackageName();
- DisplayResolveInfo multiDri = consolidated.get(packageName);
- if (multiDri == null) {
- consolidated.put(packageName, info);
- } else if (multiDri instanceof MultiDisplayResolveInfo) {
- ((MultiDisplayResolveInfo) multiDri).addTarget(info);
- } else {
- // create consolidated target from the single DisplayResolveInfo
- MultiDisplayResolveInfo multiDisplayResolveInfo =
+ new AsyncTask<Void, Void, List<DisplayResolveInfo>>() {
+ @Override
+ protected List<DisplayResolveInfo> doInBackground(Void... voids) {
+ List<DisplayResolveInfo> allTargets = new ArrayList<>();
+ allTargets.addAll(mDisplayList);
+ allTargets.addAll(mCallerTargets);
+ if (!mEnableStackedApps) {
+ return allTargets;
+ }
+ // Consolidate multiple targets from same app.
+ Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
+ for (DisplayResolveInfo info : allTargets) {
+ String packageName = info.getResolvedComponentName().getPackageName();
+ DisplayResolveInfo multiDri = consolidated.get(packageName);
+ if (multiDri == null) {
+ consolidated.put(packageName, info);
+ } else if (multiDri instanceof MultiDisplayResolveInfo) {
+ ((MultiDisplayResolveInfo) multiDri).addTarget(info);
+ } else {
+ // create consolidated target from the single DisplayResolveInfo
+ MultiDisplayResolveInfo multiDisplayResolveInfo =
new MultiDisplayResolveInfo(packageName, multiDri);
- multiDisplayResolveInfo.addTarget(info);
- consolidated.put(packageName, multiDisplayResolveInfo);
+ multiDisplayResolveInfo.addTarget(info);
+ consolidated.put(packageName, multiDisplayResolveInfo);
+ }
}
+ List<DisplayResolveInfo> groupedTargets = new ArrayList<>();
+ groupedTargets.addAll(consolidated.values());
+ Collections.sort(groupedTargets, new ChooserActivity.AzInfoComparator(mContext));
+ return groupedTargets;
}
- mSortedList.addAll(consolidated.values());
- } else {
- mSortedList.addAll(tempList);
- }
- Collections.sort(mSortedList, new ChooserActivity.AzInfoComparator(mContext));
+ @Override
+ protected void onPostExecute(List<DisplayResolveInfo> newList) {
+ mSortedList = newList;
+ notifyDataSetChanged();
+ }
+ }.execute();
}
@Override
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index daacd459a1a7..86c13a0581c2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1938,7 +1938,7 @@ public class ResolverActivity extends Activity implements
ResolverListAdapter activeListAdapter =
mMultiProfilePagerAdapter.getActiveListAdapter();
activeListAdapter.notifyDataSetChanged();
- if (activeListAdapter.getCount() == 0) {
+ if (activeListAdapter.getCount() == 0 && !inactiveListAdapterHasItems()) {
// We no longer have any items... just finish the activity.
finish();
}
@@ -1948,6 +1948,13 @@ public class ResolverActivity extends Activity implements
}
}
+ private boolean inactiveListAdapterHasItems() {
+ if (!shouldShowTabs()) {
+ return false;
+ }
+ return mMultiProfilePagerAdapter.getInactiveListAdapter().getCount() > 0;
+ }
+
private BroadcastReceiver createWorkProfileStateReceiver() {
return new BroadcastReceiver() {
@Override
diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
index 9cdfc2f5c763..478cc18f13ee 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -74,12 +74,16 @@ public class ResolverViewPager extends ViewPager {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+ /**
+ * Sets whether swiping sideways should happen.
+ * <p>Note that swiping is always disabled for RTL layouts (b/159110029 for context).
+ */
void setSwipingEnabled(boolean swipingEnabled) {
mSwipingEnabled = swipingEnabled;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- return mSwipingEnabled && super.onInterceptTouchEvent(ev);
+ return !isLayoutRtl() && mSwipingEnabled && super.onInterceptTouchEvent(ev);
}
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 7455ad009873..11e55b852516 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -2232,24 +2232,43 @@ public final class ProcessStats implements Parcelable {
}
/** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
- public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto) {
- dumpProtoPreamble(proto);
+ public void dumpAggregatedProtoForStatsd(ProtoOutputStream[] protoStreams,
+ long maxRawShardSizeBytes) {
+ int shardIndex = 0;
+ dumpProtoPreamble(protoStreams[shardIndex]);
+
final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
final ProcessMap<ArraySet<PackageState>> procToPkgMap = new ProcessMap<>();
final SparseArray<ArraySet<String>> uidToPkgMap = new SparseArray<>();
collectProcessPackageMaps(null, false, procToPkgMap, uidToPkgMap);
+
for (int ip = 0; ip < procMap.size(); ip++) {
final String procName = procMap.keyAt(ip);
+ if (protoStreams[shardIndex].getRawSize() > maxRawShardSizeBytes) {
+ shardIndex++;
+ if (shardIndex >= protoStreams.length) {
+ // We have run out of space; we'll drop the rest of the processes.
+ Slog.d(TAG, String.format("Dropping process indices from %d to %d from "
+ + "statsd proto (too large)", ip, procMap.size()));
+ break;
+ }
+ dumpProtoPreamble(protoStreams[shardIndex]);
+ }
+
final SparseArray<ProcessState> uids = procMap.valueAt(ip);
for (int iu = 0; iu < uids.size(); iu++) {
final int uid = uids.keyAt(iu);
final ProcessState procState = uids.valueAt(iu);
- procState.dumpAggregatedProtoForStatsd(proto,
+ procState.dumpAggregatedProtoForStatsd(protoStreams[shardIndex],
ProcessStatsSectionProto.PROCESS_STATS,
procName, uid, mTimePeriodEndRealtime,
procToPkgMap, uidToPkgMap);
}
}
+
+ for (int i = 0; i <= shardIndex; i++) {
+ protoStreams[i].flush();
+ }
}
private void dumpProtoPreamble(ProtoOutputStream proto) {
@@ -2403,10 +2422,11 @@ public final class ProcessStats implements Parcelable {
final SourceKey key = assocVals.keyAt(i);
final long[] vals = assocVals.valueAt(i);
final long token = proto.start(fieldId);
+ final int idx = uidToPkgMap.indexOfKey(key.mUid);
ProcessState.writeCompressedProcessName(proto,
ProcessStatsAssociationProto.ASSOC_PROCESS_NAME,
key.mProcess, key.mPackage,
- uidToPkgMap.get(key.mUid).size() > 1);
+ idx >= 0 && uidToPkgMap.valueAt(idx).size() > 1);
proto.write(ProcessStatsAssociationProto.ASSOC_UID, key.mUid);
proto.write(ProcessStatsAssociationProto.TOTAL_COUNT, (int) vals[1]);
proto.write(ProcessStatsAssociationProto.TOTAL_DURATION_SECS,
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index e9d7d05fe68d..167d128a76f8 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -709,7 +709,7 @@ public interface ServiceConnector<I extends IInterface> {
if (DEBUG) {
return mDebugName;
}
- return mDelegate.toString() + " wrapped into " + super.toString();
+ return mDelegate + " wrapped into " + super.toString();
}
@Override
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java b/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
index c11b939098c4..69ca9922e81f 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderDiff.java
@@ -59,7 +59,7 @@ import java.util.Objects;
* #getProcessCpuUsageDiffed()} result.
*
* <p>Thresholding is done in this class, instead of {@link KernelCpuThreadReader}, and instead of
- * WestWorld, because the thresholding should be done after diffing, not before. This is because of
+ * statsd, because the thresholding should be done after diffing, not before. This is because of
* two issues with thresholding before diffing:
*
* <ul>
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 7bfed91c42b9..6fe1d8140371 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -16,6 +16,7 @@
package com.android.internal.policy;
+import android.graphics.Insets;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -69,16 +70,14 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
private ColorDrawable mNavigationBarColor;
private boolean mOldFullscreen;
private boolean mFullscreen;
- private final Rect mOldSystemInsets = new Rect();
- private final Rect mOldStableInsets = new Rect();
- private final Rect mSystemInsets = new Rect();
- private final Rect mStableInsets = new Rect();
+ private final Rect mOldSystemBarInsets = new Rect();
+ private final Rect mSystemBarInsets = new Rect();
private final Rect mTmpRect = new Rect();
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
- boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+ boolean fullscreen, Insets systemBarInsets) {
setName("ResizeFrame");
mRenderer = renderer;
@@ -95,10 +94,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
mTargetRect.set(initialBounds);
mFullscreen = fullscreen;
mOldFullscreen = fullscreen;
- mSystemInsets.set(systemInsets);
- mStableInsets.set(stableInsets);
- mOldSystemInsets.set(systemInsets);
- mOldStableInsets.set(stableInsets);
+ mSystemBarInsets.set(systemBarInsets.toRect());
+ mOldSystemBarInsets.set(systemBarInsets.toRect());
// Kick off our draw thread.
start();
@@ -154,16 +151,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
*
* @param newTargetBounds The new target bounds.
* @param fullscreen Whether the window is currently drawing in fullscreen.
- * @param systemInsets The current visible system insets for the window.
- * @param stableInsets The stable insets for the window.
+ * @param systemBarInsets The current visible system insets for the window.
*/
- public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemBarInsets) {
synchronized (this) {
mFullscreen = fullscreen;
mTargetRect.set(newTargetBounds);
- mSystemInsets.set(systemInsets);
- mStableInsets.set(stableInsets);
+ mSystemBarInsets.set(systemBarInsets);
// Notify of a bounds change.
pingRenderLocked(false /* drawImmediate */);
}
@@ -247,14 +241,12 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
mNewTargetRect.set(mTargetRect);
if (!mNewTargetRect.equals(mOldTargetRect)
|| mOldFullscreen != mFullscreen
- || !mStableInsets.equals(mOldStableInsets)
- || !mSystemInsets.equals(mOldSystemInsets)
+ || !mSystemBarInsets.equals(mOldSystemBarInsets)
|| mReportNextDraw) {
mOldFullscreen = mFullscreen;
mOldTargetRect.set(mNewTargetRect);
- mOldSystemInsets.set(mSystemInsets);
- mOldStableInsets.set(mStableInsets);
- redrawLocked(mNewTargetRect, mFullscreen, mSystemInsets, mStableInsets);
+ mOldSystemBarInsets.set(mSystemBarInsets);
+ redrawLocked(mNewTargetRect, mFullscreen);
}
}
@@ -304,11 +296,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
*
* @param newBounds The window bounds which needs to be drawn.
* @param fullscreen Whether the window is currently drawing in fullscreen.
- * @param systemInsets The current visible system insets for the window.
- * @param stableInsets The stable insets for the window.
*/
- private void redrawLocked(Rect newBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ private void redrawLocked(Rect newBounds, boolean fullscreen) {
// While a configuration change is taking place the view hierarchy might become
// inaccessible. For that case we remember the previous metrics to avoid flashes.
@@ -355,7 +344,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
}
mFrameAndBackdropNode.endRecording();
- drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets);
+ drawColorViews(left, top, width, height, fullscreen);
// We need to render the node explicitly
mRenderer.drawRenderNode(mFrameAndBackdropNode);
@@ -363,14 +352,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
reportDrawIfNeeded();
}
- private void drawColorViews(int left, int top, int width, int height,
- boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+ private void drawColorViews(int left, int top, int width, int height, boolean fullscreen) {
if (mSystemBarBackgroundNode == null) {
return;
}
RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height);
mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
- final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
+ final int topInset = mSystemBarInsets.top;
if (mStatusBarColor != null) {
mStatusBarColor.setBounds(0, 0, left + width, topInset);
mStatusBarColor.draw(canvas);
@@ -380,7 +368,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
- DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect, 1f);
+ DecorView.getNavigationBarRect(width, height, mSystemBarInsets, mTmpRect, 1f);
mNavigationBarColor.setBounds(mTmpRect);
mNavigationBarColor.draw(canvas);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index c6135f2c81d3..b12c5e9ba5b0 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1050,22 +1050,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return false;
}
- public static int getColorViewTopInset(int stableTop, int systemTop) {
- return Math.min(stableTop, systemTop);
- }
-
- public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
- return Math.min(stableBottom, systemBottom);
- }
-
- public static int getColorViewRightInset(int stableRight, int systemRight) {
- return Math.min(stableRight, systemRight);
- }
-
- public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
- return Math.min(stableLeft, systemLeft);
- }
-
public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
return bottomInset == 0 && rightInset > 0;
}
@@ -1079,14 +1063,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
: isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
}
- public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
- Rect contentInsets, Rect outRect, float scale) {
- final int bottomInset =
- (int) (getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom) * scale);
- final int leftInset =
- (int) (getColorViewLeftInset(stableInsets.left, contentInsets.left) * scale);
- final int rightInset =
- (int) (getColorViewLeftInset(stableInsets.right, contentInsets.right) * scale);
+ public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect systemBarInsets,
+ Rect outRect, float scale) {
+ final int bottomInset = (int) (systemBarInsets.bottom * scale);
+ final int leftInset = (int) (systemBarInsets.left * scale);
+ final int rightInset = (int) (systemBarInsets.right * scale);
final int size = getNavBarSize(bottomInset, rightInset, leftInset);
if (isNavBarToRightEdge(bottomInset, rightInset)) {
outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
@@ -1113,31 +1094,30 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mLastWindowFlags = attrs.flags;
if (insets != null) {
- mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
- insets.getSystemWindowInsetTop());
- mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
- insets.getSystemWindowInsetBottom());
- mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
- insets.getSystemWindowInsetRight());
- mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
- insets.getSystemWindowInsetLeft());
+ final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars());
+ final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.systemBars());
+ mLastTopInset = systemBarInsets.top;
+ mLastBottomInset = systemBarInsets.bottom;
+ mLastRightInset = systemBarInsets.right;
+ mLastLeftInset = systemBarInsets.left;
// Don't animate if the presence of stable insets has changed, because that
// indicates that the window was either just added and received them for the
// first time, or the window size or position has changed.
- boolean hasTopStableInset = insets.getStableInsetTop() != 0;
+ boolean hasTopStableInset = stableBarInsets.top != 0;
disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
mLastHasTopStableInset = hasTopStableInset;
- boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
+ boolean hasBottomStableInset = stableBarInsets.bottom != 0;
disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
mLastHasBottomStableInset = hasBottomStableInset;
- boolean hasRightStableInset = insets.getStableInsetRight() != 0;
+ boolean hasRightStableInset = stableBarInsets.right != 0;
disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
mLastHasRightStableInset = hasRightStableInset;
- boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
+ boolean hasLeftStableInset = stableBarInsets.left != 0;
disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
mLastHasLeftStableInset = hasLeftStableInset;
@@ -2296,7 +2276,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
Rect stableInsets) {
if (mBackdropFrameRenderer != null) {
- mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
+ mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets);
}
}
@@ -2314,11 +2294,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
final ThreadedRenderer renderer = getThreadedRenderer();
if (renderer != null) {
loadBackgroundDrawablesIfNeeded();
+ WindowInsets rootInsets = getRootWindowInsets();
mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
- getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
- stableInsets);
+ getCurrentColor(mNavigationColorViewState), fullscreen,
+ rootInsets.getInsets(WindowInsets.Type.systemBars()));
// Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
// If we want to get the shadow shown while resizing, we would need to elevate a new
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 3f03f2a3e754..d22f94213338 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -71,4 +71,6 @@ interface IInputMethodManager {
void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
in float[] matrixValues);
+
+ void removeImeSurface();
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index b64923fb5bf8..5d4407bf8370 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -233,13 +233,20 @@ public class ConversationLayout extends FrameLayout
oldVisibility = mImportanceRingView.getVisibility();
wasGone = oldVisibility == GONE;
visibility = !mImportantConversation ? GONE : visibility;
- isGone = visibility == GONE;
- if (wasGone != isGone) {
+ boolean isRingGone = visibility == GONE;
+ if (wasGone != isRingGone) {
// Keep the badge visibility in sync with the icon. This is necessary in cases
// Where the icon is being hidden externally like in group children.
mImportanceRingView.animate().cancel();
mImportanceRingView.setVisibility(visibility);
}
+
+ oldVisibility = mConversationIconBadge.getVisibility();
+ wasGone = oldVisibility == GONE;
+ if (wasGone != isGone) {
+ mConversationIconBadge.animate().cancel();
+ mConversationIconBadge.setVisibility(visibility);
+ }
});
// When the small icon is gone, hide the rest of the badge
mIcon.setOnForceHiddenChangedListener((forceHidden) -> {
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 53272f7eebf9..f312d1d4f25d 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -42,6 +42,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
@@ -109,6 +110,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
private ViewGroup mMessagingIconContainer;
private int mConversationContentStart;
private int mNonConversationMarginEnd;
+ private int mNotificationTextMarginTop;
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -148,6 +150,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
R.dimen.conversation_content_start);
mNonConversationMarginEnd = getResources().getDimensionPixelSize(
R.dimen.messaging_layout_margin_end);
+ mNotificationTextMarginTop = getResources().getDimensionPixelSize(
+ R.dimen.notification_text_margin_top);
}
public void updateClipRect() {
@@ -612,7 +616,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
return 0;
}
- public View getSenderView() {
+ public TextView getSenderView() {
return mSenderView;
}
@@ -664,10 +668,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
public void setSingleLine(boolean singleLine) {
if (singleLine != mSingleLine) {
mSingleLine = singleLine;
+ MarginLayoutParams p = (MarginLayoutParams) mMessageContainer.getLayoutParams();
+ p.topMargin = singleLine ? 0 : mNotificationTextMarginTop;
+ mMessageContainer.setLayoutParams(p);
mContentContainer.setOrientation(
singleLine ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
MarginLayoutParams layoutParams = (MarginLayoutParams) mSenderView.getLayoutParams();
layoutParams.setMarginEnd(singleLine ? mSenderTextPaddingSingleLine : 0);
+ mSenderView.setSingleLine(singleLine);
updateMaxDisplayedLines();
updateClipRect();
updateSenderVisibility();
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index ac04862d9a7d..4ee647a42116 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -300,6 +300,27 @@ public class MessagingLinearLayout extends ViewGroup {
return mMessagingLayout;
}
+ @Override
+ public int getBaseline() {
+ // When placed in a horizontal linear layout (as is the case in a single-line MessageGroup),
+ // align with the last visible child (which is the one that will be displayed in the single-
+ // line group.
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = getChildAt(i);
+ if (isGone(child)) {
+ continue;
+ }
+ final int childBaseline = child.getBaseline();
+ if (childBaseline == -1) {
+ return -1;
+ }
+ MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+ return lp.topMargin + childBaseline;
+ }
+ return super.getBaseline();
+ }
+
public interface MessagingChild {
int MEASURED_NORMAL = 0;
int MEASURED_SHORTENED = 1;
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index a3c455bfc111..b1b39f3e36ff 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -47,6 +47,7 @@
#define CHANNEL_INVALID 0
#define CHANNEL_OUT_DEFAULT 1
+#define CHANNEL_IN_DEFAULT 1
static inline audio_format_t audioFormatToNative(int audioFormat)
{
@@ -196,12 +197,22 @@ static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask)
static inline audio_channel_mask_t inChannelMaskToNative(int channelMask)
{
- return (audio_channel_mask_t)channelMask;
+ switch (channelMask) {
+ case CHANNEL_IN_DEFAULT:
+ return AUDIO_CHANNEL_NONE;
+ default:
+ return (audio_channel_mask_t)channelMask;
+ }
}
static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask)
{
- return (int)nativeMask;
+ switch (nativeMask) {
+ case AUDIO_CHANNEL_NONE:
+ return CHANNEL_IN_DEFAULT;
+ default:
+ return (int)nativeMask;
+ }
}
#endif // ANDROID_MEDIA_AUDIOFORMAT_H
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index 563ef145b79c..bd5cb62f7fde 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -206,4 +206,5 @@ enum AppOpEnum {
APP_OP_DEPRECATED_1 = 96 [deprecated = true];
APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
+ APP_OP_NO_ISOLATED_STORAGE = 99;
}
diff --git a/core/proto/android/stats/launcher/launcher.proto b/core/proto/android/stats/launcher/launcher.proto
index dbd0e038c40c..fc177d57b193 100644
--- a/core/proto/android/stats/launcher/launcher.proto
+++ b/core/proto/android/stats/launcher/launcher.proto
@@ -32,10 +32,12 @@ enum LauncherAction {
}
enum LauncherState {
- BACKGROUND = 0;
- HOME = 1;
- OVERVIEW = 2;
- ALLAPPS = 3;
+ LAUNCHER_STATE_UNSPECIFIED = 0;
+ BACKGROUND = 1;
+ HOME = 2;
+ OVERVIEW = 3;
+ ALLAPPS = 4;
+ UNCHANGED = 5;
}
message LauncherTarget {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 99b46ecee28f..9c1ecf2e48e0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5028,6 +5028,10 @@
<permission android:name="android.permission.ACCESS_TV_DESCRAMBLER"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Allows an application to create trusted displays. @hide -->
+ <permission android:name="android.permission.ADD_TRUSTED_DISPLAY"
+ android:protectionLevel="signature" />
+
<!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 3188861a52a5..5e3b657353b6 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -37,6 +37,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:baselineAligned="true"
android:orientation="vertical">
<com.android.internal.widget.ImageFloatingTextView
android:id="@+id/message_name"
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index c2d501355d5f..56e7a2703d5e 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1665,7 +1665,7 @@
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odbij"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite neku funkciju da biste počeli da je koristite:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije koje ćete koristiti sa tasterom jačine zvuka kao prečicom"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije za prečicu tasterom jačine zvuka"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Izmenite prečice"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
@@ -1673,8 +1673,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Zadržali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Zadržali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Izaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Odaberite funkciju koja će se koristiti pomoću pokreta za pristupačnost (pomoću dva prsta prevucite nagore od dna ekrana):"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2bdd4f7ad230..84136225de38 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1687,7 +1687,7 @@
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Адмовіць"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Каб пачаць выкарыстоўваць функцыю, націсніце на яе:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберыце функцыі, якія будзеце выкарыстоўваць з кнопкай спецыяльных магчымасцей"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберыце функцыі, якія будзеце выкарыстоўваць са спалучэннем клавішы гучнасці"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберыце функцыі для выкарыстання з клавішай гучнасці"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сэрвіс \"<xliff:g id="SERVICE_NAME">%s</xliff:g>\" выключаны"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змяніць ярлыкі"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Гатова"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index e18fc11bc7c3..8e182a24c7e3 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1311,7 +1311,7 @@
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Seleccionar para inhabilitar la depuración USB"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Depuración inalámbrica conectada"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Toca para desactivar la depuración inalámbrica"</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecciona para inhabilitar la depuración inalámbrica."</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Toca para desactivar la depuración inalámbrica."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modo de agente de prueba habilitado"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Restablece los ajustes de fábrica para inhabilitar el modo de agente de prueba."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Se ha habilitado la consola en serie"</string>
@@ -1623,8 +1623,8 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos fuertes durante mucho tiempo puede dañar los oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Utilizar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si el acceso directo está activado, pulsa los dos botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Quieres activar las funciones de accesibilidad?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Al mantener pulsadas ambas teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Ajustes &gt; Accesibilidad."</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Activar funciones de accesibilidad?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Al mantener pulsadas las dos teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Ajustes &gt; Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Al mantener pulsadas ambas teclas de volumen durante unos segundos se activa <xliff:g id="SERVICE">%1$s</xliff:g>, una función de accesibilidad. Esta función puede modificar el funcionamiento del dispositivo.\n\nPuedes asignar este acceso directo a otra función en Ajustes &gt; Accesibilidad."</string>
@@ -1642,8 +1642,8 @@
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permitir"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Denegar"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una función para empezar a usarla:"</string>
- <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Seleccionar qué funciones usar con el botón Accesibilidad"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Seleccionar qué funciones usar con la tecla de volumen"</string>
+ <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona qué funciones usar con el botón Accesibilidad"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona qué funciones usar con la tecla de volumen"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Se ha desactivado <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar accesos directos"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Listo"</string>
@@ -1651,8 +1651,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> se ha activado."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Al mantener pulsadas las teclas de volumen, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> se ha desactivado."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Selecciona la función que se utilizará cuando toques el botón Accesibilidad:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Elige la función que se utilizará con el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 56887d822483..e7239eb46e41 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1624,10 +1624,10 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Kas kasutada juurdepääsetavuse otseteed?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Kas lülitada juurdepääsufunktsioonid sisse?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hoidke mõlemat helitugevuse nuppu mõni sekund all, et juurdepääsufunktsioonid sisse lülitada. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nValitud funktsioone saab muuta jaotises Seaded &gt; Juurdepääsetavus."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hoidke juurdepääsufunktsioonide sisselülitamiseks mõlemat helitugevuse klahvi mõni sekund all. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nValitud funktsioone saab muuta jaotises Seaded &gt; Juurdepääsetavus."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Kas lülitada <xliff:g id="SERVICE">%1$s</xliff:g> sisse?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Kui hoiate mõlemat helitugevuse nuppu mõni sekund all, lülitatakse sisse juurdepääsufunktsioon <xliff:g id="SERVICE">%1$s</xliff:g>. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded &gt; Juurdepääsetavus."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Kui hoiate mõlemat helitugevuse klahvi mõni sekund all, lülitatakse juurdepääsufunktsioon <xliff:g id="SERVICE">%1$s</xliff:g> sisse. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded &gt; Juurdepääsetavus."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Lülita sisse"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ära lülita sisse"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"SEES"</string>
@@ -1643,7 +1643,7 @@
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Keela"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Puudutage funktsiooni, et selle kasutamist alustada."</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valige funktsioonid, mida juurdepääsetavuse nupuga kasutada"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valige funktsioonid, mida helitugevuse nupu otseteega kasutada"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valige helitugevuse nupu otsetee funktsioonid"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> on välja lülitatud"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Muuda otseteid"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Valmis"</string>
@@ -1651,13 +1651,13 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse nuppe hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse nuppe hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Valige, millist funktsiooni kasutada, kui vajutate juurdepääsetavuse nuppu:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Valige, millist funktsiooni juurdepääsetavuse liigutusega (kahe sõrmega ekraanikuval alt üles pühkimine) kasutada:"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Valige, millist funktsiooni juurdepääsetavuse liigutusega (kolme sõrmega ekraanikuval alt üles pühkimine) kasutada:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funktsioonide vahel vahetamiseks vajutage pikalt juurdepääsetavuse nuppu."</string>
+ <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funktsioonide vahel vahetamiseks vajutage juurdepääsetavuse nuppu pikalt."</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funktsioonide vahel vahetamiseks pühkige kahe sõrmega üles ja hoidke."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funktsioonide vahel vahetamiseks pühkige kolme sõrmega üles ja hoidke."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Suurendus"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 0890ae13e421..9f618d5accbd 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -184,7 +184,7 @@
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"किसी अज्ञात तृतीय पक्ष के द्वारा"</string>
<string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"आपकी वर्क प्रोफ़ाइल का व्यवस्थापक करता है"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"<xliff:g id="MANAGING_DOMAIN">%s</xliff:g> के द्वारा"</string>
- <string name="work_profile_deleted" msgid="5891181538182009328">"वर्क प्रोफ़ाइल हटाई गई"</string>
+ <string name="work_profile_deleted" msgid="5891181538182009328">"वर्क प्रोफ़ाइल मिटाई गई"</string>
<string name="work_profile_deleted_details" msgid="3773706828364418016">"वर्क प्रोफ़ाइल व्यवस्थापक ऐप्लिकेशन या तो मौजूद नहीं है या वह खराब हो गया है. परिणामस्वरूप, आपकी वर्क प्रोफ़ाइल और उससे जुड़े डेटा को हटा दिया गया है. सहायता के लिए अपने व्यवस्थापक से संपर्क करें."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"आपकी वर्क प्रोफ़ाइल अब इस डिवाइस पर उपलब्‍ध नहीं है"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"कई बार गलत पासवर्ड डाला गया"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 1fb9a62025bb..ec56952ea4a2 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1841,7 +1841,7 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Աշխատանքային օր"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Շաբաթ-կիրակի"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Միջոցառում"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնելիս"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնի ժամանակ"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ն անջատում է որոշ ձայներ"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:"</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Սարքում ներքին խնդիր է առաջացել: Մանրամասների համար կապվեք արտադրողի հետ:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9fbd73ea1269..3fb2a7732f0e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -339,7 +339,7 @@
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Mengizinkan aplikasi menambahkan pintasan Layar Utama tanpa tindakan dari pengguna."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"meng-uninstal pintasan"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Mengizinkan aplikasi menghapus pintasan Layar Utama tanpa tindakan dari pengguna."</string>
- <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"ubah rute panggilan keluar"</string>
+ <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"alihkan panggilan keluar"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Memungkinkan aplikasi melihat nomor yang dihubungi saat melakukan panggilan keluar dengan opsi untuk mengalihkan panggilan ke nomor lain atau membatalkan panggilan sepenuhnya."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"jawab panggilan telepon"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Mengizinkan aplikasi menjawab panggilan telepon masuk."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b6a850d10fef..80e34ef648cf 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -222,7 +222,7 @@
<string name="reboot_to_update_reboot" msgid="4474726009984452312">"Riavvio in corso…"</string>
<string name="reboot_to_reset_title" msgid="2226229680017882787">"Ripristino dati di fabbrica"</string>
<string name="reboot_to_reset_message" msgid="3347690497972074356">"Riavvio in corso…"</string>
- <string name="shutdown_progress" msgid="5017145516412657345">"Spegnimento..."</string>
+ <string name="shutdown_progress" msgid="5017145516412657345">"Spegnimento…"</string>
<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"Il tablet verrà spento."</string>
<string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"Il dispositivo Android TV verrà spento."</string>
<string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"L\'orologio verrà spento."</string>
@@ -856,7 +856,7 @@
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"La SIM è bloccata tramite PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta la Guida dell\'utente o contatta il servizio clienti."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"La SIM è bloccata."</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Sblocco SIM..."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Sblocco SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Hai digitato la tua password <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
@@ -879,7 +879,7 @@
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Accedi"</string>
<string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Password o nome utente non valido."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Hai dimenticato il nome utente o la password?\nVisita "<b>"google.com/accounts/recovery"</b>"."</string>
- <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Verifica..."</string>
+ <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Verifica…"</string>
<string name="lockscreen_unlock_label" msgid="4648257878373307582">"Sblocca"</string>
<string name="lockscreen_sound_on_label" msgid="1660281470535492430">"Audio attivato"</string>
<string name="lockscreen_sound_off_label" msgid="2331496559245450053">"Audio disattivato"</string>
@@ -1095,7 +1095,7 @@
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Impossibile copiare negli appunti"</string>
<string name="paste" msgid="461843306215520225">"Incolla"</string>
<string name="paste_as_plain_text" msgid="7664800665823182587">"Incolla come testo normale"</string>
- <string name="replace" msgid="7842675434546657444">"Sostituisci..."</string>
+ <string name="replace" msgid="7842675434546657444">"Sostituisci…"</string>
<string name="delete" msgid="1514113991712129054">"Elimina"</string>
<string name="copyUrl" msgid="6229645005987260230">"Copia URL"</string>
<string name="selectTextMode" msgid="3225108910999318778">"Seleziona testo"</string>
@@ -1117,7 +1117,7 @@
<string name="yes" msgid="9069828999585032361">"OK"</string>
<string name="no" msgid="5122037903299899715">"Annulla"</string>
<string name="dialog_alert_title" msgid="651856561974090712">"Attenzione"</string>
- <string name="loading" msgid="3138021523725055037">"Caricamento..."</string>
+ <string name="loading" msgid="3138021523725055037">"Caricamento…"</string>
<string name="capital_on" msgid="2770685323900821829">"ON"</string>
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"selezionato"</string>
@@ -1544,7 +1544,7 @@
<string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Mostra tutto"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Scegli attività"</string>
<string name="share_action_provider_share_with" msgid="1904096863622941880">"Condividi con"</string>
- <string name="sending" msgid="206925243621664438">"Invio..."</string>
+ <string name="sending" msgid="206925243621664438">"Invio…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"Avviare l\'applicazione Browser?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Accettare la chiamata?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"Sempre"</string>
@@ -1591,7 +1591,7 @@
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"La scheda SIM è disattivata. Inserisci il codice PUK per continuare. Contatta l\'operatore per avere informazioni dettagliate."</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Inserisci il codice PIN desiderato"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Conferma il codice PIN desiderato"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8871937892678885545">"Sblocco scheda SIM..."</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8871937892678885545">"Sblocco scheda SIM…"</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Codice PIN errato."</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Il PIN deve essere di 4-8 numeri."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"Il codice PUK deve essere di 8 cifre."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 07579ad0ae0f..9ada4376e50d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1311,7 +1311,7 @@
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB арқылы түзетуді өшіру үшін таңдаңыз."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Сымсыз түзету байланыстырылды"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Сымсыз түзетуді өшіру үшін түртіңіз."</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Сымсыз түзетуді өшіріңіз."</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Сымсыз түзетуді өшіру үшін басыңыз."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Сынақ бағдарламасы режимі қосылды"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Сынақ бағдарламасы режимін өшіру үшін зауыттық күйіне қайтарыңыз."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Сериялық консоль қосылды"</string>
@@ -1624,7 +1624,7 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Арнайы мүмкіндік төте жолын пайдалану керек пе?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Түймелер тіркесімі қосулы кезде, екі дыбыс түймесін 3 секунд басып тұрсаңыз, \"Арнайы мүмкіндіктер\" функциясы іске қосылады."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Арнайы мүмкіндіктер іске қосылсын ба?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nФункциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТаңдалған функцияларды \"Параметрлер &gt; Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nҚазіргі функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТаңдалған функцияларды \"Параметрлер &gt; Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> қосылсын ба?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, <xliff:g id="SERVICE">%1$s</xliff:g> арнайы қызметі іске қосылады. Бұл – құрылғының жүмысына әсер етуі мүмкін.\n\nБұл таңбашаны басқа функцияға \"Параметрлер &gt; Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
@@ -1643,16 +1643,16 @@
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Қабылдамау"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны пайдалана бастау үшін түртіңіз:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дыбыс деңгейі пернелері таңбашасымен қолданылатын функцияларды таңдаңыз"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> қызметі өшірулі."</string>
- <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Таңбашаларды өзгерту"</string>
+ <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Жылдам пәрмендерді өзгерту"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Дайын"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Төте жолды өшіру"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстер инверсиясы"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірулі."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"\"Арнайы мүмкіндіктер\" түймесін түрткенде пайдаланатын функцияны таңдаңыз:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Арнайы мүмкіндіктер қимылымен (екі саусақпен экранның төменгі жағынан жоғары қарай сырғытыңыз) пайдаланатын функцияны таңдаңыз:"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 0fe5925e3e25..7ea8b4313f81 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1624,11 +1624,11 @@
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"접근성 단축키를 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"단축키가 사용 설정된 경우 볼륨 버튼 두 개를 동시에 3초간 누르면 접근성 기능이 시작됩니다."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"접근성 기능을 사용하시겠습니까?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n설정 &gt; 접근성에서 선택한 기능을 변경할 수 있습니다."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이때 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n설정 &gt; 접근성에서 선택한 기능을 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"볼륨 키 2개를 몇 초 동안 길게 누르면 <xliff:g id="SERVICE">%1$s</xliff:g> 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n설정 &gt; 접근성에서 이 단축키를 다른 기능으로 변경할 수 있습니다."</string>
- <string name="accessibility_shortcut_on" msgid="5463618449556111344">"사용 설정"</string>
+ <string name="accessibility_shortcut_on" msgid="5463618449556111344">"사용"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"사용 안 함"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"사용"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"사용 안함"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index d910fc03e165..c21a1d78db45 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -142,7 +142,7 @@
<string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"WiFi ခေါ်ဆိုမှု"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
- <string name="wifi_calling_off_summary" msgid="5626710010766902560">"ပိတ်ထားရသည်"</string>
+ <string name="wifi_calling_off_summary" msgid="5626710010766902560">"ပိတ်ထားသည်"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi သုံး၍ ခေါ်ဆိုသည်"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"မိုဘိုင်းကွန်ရက်သုံး၍ ခေါ်ဆိုသည်"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ကြိုးမဲ့အင်တာနက် သာလျှင်"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 25cc45b9e964..a36035e817a3 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -175,7 +175,7 @@
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"अति धेरै <xliff:g id="CONTENT_TYPE">%s</xliff:g> मेटाउने प्रयास गरियो।"</string>
<string name="low_memory" product="tablet" msgid="5557552311566179924">"ट्याब्लेट भण्डारण खाली छैन! ठाउँ खाली गर्नको लागि केही फाइलहरू मेटाउनुहोस्।"</string>
<string name="low_memory" product="watch" msgid="3479447988234030194">"भण्डारण भरिएको छ हेर्नुहोस्। ठाउँ खाली गर्न केही फाइलहरू मेटाउनुहोस्।"</string>
- <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV यन्त्रको भण्डारण भरिएको छ। ठाउँ खाली गर्न केही फाइलहरू मेट्नुहोस्।"</string>
+ <string name="low_memory" product="tv" msgid="6663680413790323318">"Android टिभी यन्त्रको भण्डारण भरिएको छ। ठाउँ खाली गर्न केही फाइलहरू मेट्नुहोस्।"</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"फोन भण्डारण भरिएको छ! ठाउँ खाली गर्नको लागि केही फाइलहरू मेटाउनुहोस्।"</string>
<plurals name="ssl_ca_cert_warning" formatted="false" msgid="2288194355006173029">
<item quantity="other">प्रमाणपत्रका अख्तियारीहरूलाई स्थापना गरियो</item>
@@ -206,7 +206,7 @@
<string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"सक्रिय गर्नुहोस्"</string>
<string name="me" msgid="6207584824693813140">"मलाई"</string>
<string name="power_dialog" product="tablet" msgid="8333207765671417261">"ट्याब्लेट विकल्पहरू"</string>
- <string name="power_dialog" product="tv" msgid="7792839006640933763">"Android TV सम्बन्धी विकल्पहरू"</string>
+ <string name="power_dialog" product="tv" msgid="7792839006640933763">"Android टिभी सम्बन्धी विकल्पहरू"</string>
<string name="power_dialog" product="default" msgid="1107775420270203046">"फोन विकल्पहरू"</string>
<string name="silent_mode" msgid="8796112363642579333">"मौन मोड"</string>
<string name="turn_on_radio" msgid="2961717788170634233">"वायरलेस अन गर्नुहोस्"</string>
@@ -224,7 +224,7 @@
<string name="reboot_to_reset_message" msgid="3347690497972074356">"पुनःसुरु हुँदै ..."</string>
<string name="shutdown_progress" msgid="5017145516412657345">"बन्द गर्दै..."</string>
<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"तपाईँको ट्याब्लेट बन्द हुने छ।"</string>
- <string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"तपाईंको Android TV यन्त्र बन्द हुने छ।"</string>
+ <string name="shutdown_confirm" product="tv" msgid="7975942887313518330">"तपाईंको Android टिभी यन्त्र बन्द हुने छ।"</string>
<string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"तपाईँको घडी बन्द गरिने छ।"</string>
<string name="shutdown_confirm" product="default" msgid="136816458966692315">"तपाईँको फोन बन्द हुने छ।"</string>
<string name="shutdown_confirm_question" msgid="796151167261608447">"के तपाईं बन्द गर्न चाहनुहुन्छ?"</string>
@@ -233,7 +233,7 @@
<string name="recent_tasks_title" msgid="8183172372995396653">"नयाँ"</string>
<string name="no_recent_tasks" msgid="9063946524312275906">"कुनै नयाँ एपहरू छैनन्।"</string>
<string name="global_actions" product="tablet" msgid="4412132498517933867">"ट्याब्लेट विकल्पहरू"</string>
- <string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV सम्बन्धी विकल्पहरू"</string>
+ <string name="global_actions" product="tv" msgid="3871763739487450369">"Android टिभी सम्बन्धी विकल्पहरू"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"फोन विकल्पहरू"</string>
<string name="global_action_lock" msgid="6949357274257655383">"स्क्रिन बन्द"</string>
<string name="global_action_power_off" msgid="4404936470711393203">"बन्द गर्नुहोस्"</string>
@@ -357,7 +357,7 @@
<string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string>
<string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका पाठ सन्देशहरू (SMS वा MMS) पढ्नुहोस्"</string>
<string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस अनुप्रयोगले तपाईंको Android TV यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string>
<string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
<string name="permlab_receiveWapPush" msgid="4223747702856929056">"पाठ सन्देशहरू (WAP) प्राप्त गर्नुहोस्"</string>
<string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
@@ -379,7 +379,7 @@
<string name="permdesc_useDataInBackground" msgid="1230753883865891987">"यो अनुप्रयोगले पृष्ठभूमिमा डेटा प्रयोग गर्नसक्छ। यसले गर्दा धेरै डेटा प्रयोग हुनसक्छ।"</string>
<string name="permlab_persistentActivity" msgid="464970041740567970">"एपहरू जहिले पनि चल्ने बनाउनुहोस्"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"यसको आफ्नै मेमोरीमा दृढ भएकोको अंश बनाउनको लागि एपलाई अनुमति दिन्छ। ट्याब्लेटलाई ढिलो गराउँदै गरेका अन्य अनुप्रयोगहरूलाई सीमित मात्रामा यसले मेमोरी उपलब्ध गराउन सक्छ।"</string>
- <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"एपलाई आफ्ना केही अंशहरू मेमोरीमा स्थायी रूपमा राख्ने अनुमति दिन्छ। यसले गर्दा अन्य अनुप्रयोगहरूका लागि मेमोरीको अभाव हुन सक्ने भएकाले तपाईंको Android TV यन्त्र सुस्त हुन सक्छ।"</string>
+ <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"एपलाई आफ्ना केही अंशहरू मेमोरीमा स्थायी रूपमा राख्ने अनुमति दिन्छ। यसले गर्दा अन्य अनुप्रयोगहरूका लागि मेमोरीको अभाव हुन सक्ने भएकाले तपाईंको Android टिभी यन्त्र सुस्त हुन सक्छ।"</string>
<string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"एपलाई मेमोरीमा आफैंको निरन्तरको अंश बनाउन अनुमति दिन्छ। यसले फोनलाई ढिला बनाएर अन्य एपहरूमा मेमोरी SIMित गर्न सक्दछन्।"</string>
<string name="permlab_foregroundService" msgid="1768855976818467491">"अग्रभूमिको सेवा सञ्चालन गर्नुहोस्"</string>
<string name="permdesc_foregroundService" msgid="8720071450020922795">"एपलाई अग्रभूमिका सेवाहरू प्रयोग गर्ने अनुमति दिन्छ।"</string>
@@ -389,35 +389,35 @@
<string name="permdesc_writeSettings" msgid="8293047411196067188">"प्रणालीका सेटिङ डेटालाई परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। खराब एपहरूले सायद तपाईँको प्रणालीको कन्फिगरेसनलाई क्षति पुर्‍याउन सक्छन्।"</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"स्टार्टअपमा चलाउनुहोस्"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"आनुप्रयोगलाई प्रणाली बुट प्रक्रिया पूर्ण हुने बितिकै आफैलाई सुरु गर्ने अनुमति दिन्छ। यसले ट्याब्लेट सुरु गर्नमा ढिला गर्न सक्दछ र एपलाई समग्रमा ट्याब्लेट सधैँ चालु गरेर ढिला बनाउँदछ।"</string>
- <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"एपलाई प्रणाली बुट हुने बित्तिकै स्वत: सुरु हुने अनुमति दिन्छ। यसो गर्दा एप सधैँ चलिरहने भएकाले तपाईंको Android TV यन्त्र सुरु हुन बढी समय लाग्नुका साथै यन्त्रको समग्र कार्यसम्पादन सुस्त हुन सक्छ।"</string>
+ <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"एपलाई प्रणाली बुट हुने बित्तिकै स्वत: सुरु हुने अनुमति दिन्छ। यसो गर्दा एप सधैँ चलिरहने भएकाले तपाईंको Android टिभी यन्त्र सुरु हुन बढी समय लाग्नुका साथै यन्त्रको समग्र कार्यसम्पादन सुस्त हुन सक्छ।"</string>
<string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"एपलाई प्रणाली बुट गरी सकेपछि जति सक्दो चाँडो आफैंमा सुरु गर्न अनुमति दिन्छ। यसले फोन सुरु गर्नमा ढिला गर्न सक्दछ र अनप्रयोगलाई समग्रमा फोन सधैँ चालु गरेर ढिला बनाउँदछ।"</string>
<string name="permlab_broadcastSticky" msgid="4552241916400572230">"स्टिकि प्रसारण पठाउनुहोस्"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"औपचारिक प्रसारणलाई पठाउनको लागि एउटा एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले ट्याब्लेटलाई ढिलो र अस्थिर बनाउन सक्छ।"</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android TV यन्त्र सुस्त वा अस्थिर हुन सक्छ।"</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android टिभी यन्त्र सुस्त वा अस्थिर हुन सक्छ।"</string>
<string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"औपचारिक प्रसारणलाई पठाउनको लागि एक एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले फोनलाई ढिलो र अस्थिर बनाउन सक्छ।"</string>
<string name="permlab_readContacts" msgid="8776395111787429099">"तपाईँका सम्पर्कहरू पढ्नुहोस्"</string>
<string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको ट्याब्लेटमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
- <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android TV यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android TV यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
+ <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
<string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"एपलाई तपाईंको फोनमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको फोनमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string>
<string name="permlab_writeContacts" msgid="8919430536404830430">"तपाईँका सम्पर्कहरू परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"एपलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
- <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"एपलाई तपाईंको Android TV यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"एपलाई तपाईंको फोनमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"कल लग पढ्नुहोस्"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"यस अनुप्रयोगले तपाईंको फोन सम्पर्कको इतिहास पढ्न सक्छ।"</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"कल लग लेख्‍नुहोस्"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"आगमन तथा बहर्गमन डेटासहित तपाईँको ट्याब्लेटको कल लगको परिमार्जन गर्न एपलाई अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईँको कल लग परिमार्जन गर्न वा मेटाउन प्रयोग गर्न सक्छन्।"</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"एपलाई तपाईंको Android TV यन्त्रको आगमन र बहिर्गमन कलसम्बन्धी डेटासहित कल लग परिमार्जन गर्ने अनुमति दिन्छ। हानिकारक एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्छन्।"</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"एपलाई तपाईंको Android टिभी यन्त्रको आगमन र बहिर्गमन कलसम्बन्धी डेटासहित कल लग परिमार्जन गर्ने अनुमति दिन्छ। हानिकारक एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्छन्।"</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"एपलाई तपाईंको फोनको आउने र बाहिर जाने कलहरूको बारेको डेटा सहित कल लग परिमार्जन गर्न अनुमति दिन्छ। खराब एपहरूले यसलाई तपाईंको कल लग मेटाउन वा परिमार्जन गर्न प्रयोग गर्न सक्दछ।"</string>
<string name="permlab_bodySensors" msgid="3411035315357380862">"शरीरका सेन्सरहरूमा पहुँच गराउनुहोस् (जस्तै हृदय धड्कन निगरानीहरू)"</string>
<string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"तपाईँको हृदय गति जस्तो सेंसर बाट डेटा पहुँचको लागि एप अनुमति दिन्छ जसले तपाईँको भौतिक अवस्था अनुगमन गर्छ।"</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"पात्रोका कार्यक्रम र विवरणहरू पढ्ने"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
- <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"यस अनुप्रयोगले तपाईंको Android TV यन्त्रमा भण्डार गरिएका पात्रोसम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका पात्रोसम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका पात्रो सम्बन्धी सबै कार्यक्रमहरू पढ्न र तपाईंको पात्रोको डेटा आदान प्रदान वा सुरक्षित गर्न सक्छ।"</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"पात्रो घटनाहरू थप्नुहोस् वा परिमार्जन गर्नुहोस् र मालिकको ज्ञान बिना नै पाहुनाहरूलाई इमेल पठाउनुहोस्"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"यस अनुप्रयोगले तपाईंको Android TV यन्त्रमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरूले पठाएको जस्तै देखिने सन्देशहरू पठाउन वा कार्यक्रमका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरूले पठाएको जस्तै देखिने सन्देशहरू पठाउन वा कार्यक्रमका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
<string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"यस अनुप्रयोगले तपाईंको फोनमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"एपलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो एपलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"</string>
@@ -462,15 +462,15 @@
<string name="permdesc_readPhoneNumbers" msgid="7368652482818338871">"उक्त एपलाई यस यन्त्रको फोन नम्बरहरूमाथि पहुँच राख्न दिनुहोस्।"</string>
<string name="permlab_wakeLock" product="automotive" msgid="1904736682319375676">"कारको स्क्रिन सक्रिय राख्नुहोस्"</string>
<string name="permlab_wakeLock" product="tablet" msgid="1527660973931694000">"ट्याब्लेटलाई निन्द्रामा जानबाट रोक्नुहोस्"</string>
- <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"आफ्नो Android TV यन्त्रलाई शयन अवस्थामा जान नदिनुहोस्"</string>
+ <string name="permlab_wakeLock" product="tv" msgid="2856941418123343518">"आफ्नो Android टिभी यन्त्रलाई शयन अवस्थामा जान नदिनुहोस्"</string>
<string name="permlab_wakeLock" product="default" msgid="569409726861695115">"फोनलाई निदाउनबाट रोक्नुहोस्"</string>
<string name="permdesc_wakeLock" product="automotive" msgid="5995045369683254571">"यो अनुमतिले यस एपलाई कारको स्क्रिन सक्रिय राख्न दिन्छ।"</string>
<string name="permdesc_wakeLock" product="tablet" msgid="2441742939101526277">"ट्याब्लेटलाई निस्क्रिय हुनबाट रोक्नको लागि एपलाई अनुमति दिन्छ।"</string>
- <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"एपलाई तपाईंको Android TV यन्त्रलाई शयन अवस्थामा जानबाट रोक्ने अनुमति दिन्छ।"</string>
+ <string name="permdesc_wakeLock" product="tv" msgid="2329298966735118796">"एपलाई तपाईंको Android टिभी यन्त्रलाई शयन अवस्थामा जानबाट रोक्ने अनुमति दिन्छ।"</string>
<string name="permdesc_wakeLock" product="default" msgid="3689523792074007163">"फोनलाई निस्क्रिय हुनबाट रोक्नको लागि एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_transmitIr" msgid="8077196086358004010">"infrared ट्रान्समिट गर्नुहोस्"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5884738958581810253">"ट्याबलेटको infrared transmitter प्रयोगको लागि एप अनुमति दिन्छ।"</string>
- <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"एपलाई तपाईंको Android TV यन्त्रको इन्फ्रारेड ट्रान्समिटर प्रयोग गर्ने अनुमति दिन्छ।"</string>
+ <string name="permdesc_transmitIr" product="tv" msgid="3278506969529173281">"एपलाई तपाईंको Android टिभी यन्त्रको इन्फ्रारेड ट्रान्समिटर प्रयोग गर्ने अनुमति दिन्छ।"</string>
<string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"फोनको infrared transmitter प्रयोगको लागि एप अनुमति दिन्छ।"</string>
<string name="permlab_setWallpaper" msgid="6959514622698794511">"वालपेपर सेट गर्नुहोस्"</string>
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"एपलाई प्रणाली वालपेपर सेट गर्न अनुमति दिन्छ।"</string>
@@ -478,11 +478,11 @@
<string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"प्रणाली वालपेपरको आकार सङ्केतहरू मिलाउन एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"समय क्षेत्र सेट गर्नुहोस्"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1788868809638682503">"एपलाई ट्याब्लेटको समय क्षेत्र परिवर्तन गर्न अनुमति दिन्छ।"</string>
- <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"एपलाई तपाईंको Android TV यन्त्रको समय क्षेत्र परिवर्तन गर्ने अनुमति दिन्छ।"</string>
+ <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"एपलाई तपाईंको Android टिभी यन्त्रको समय क्षेत्र परिवर्तन गर्ने अनुमति दिन्छ।"</string>
<string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"एपलाई फोनको समय क्षेत्र परिवर्तन गर्न अनुमति दिन्छ।"</string>
<string name="permlab_getAccounts" msgid="5304317160463582791">"उपकरणमा खाताहरू भेट्टाउनुहोस्"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"एपलाई ट्याब्लेटद्वारा ज्ञात खाताहरूको सूची पाउन अनुमति दिन्छ। यसले अनुप्रयोगद्वारा तपाईंले स्थापित गर्नुभएको कुनै पनि खाताहरू समावेश गर्न सक्दछ।"</string>
- <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"एपलाई तपाईंको Android TV यन्त्रले चिनेका खाताहरूको सूची प्राप्त गर्ने अनुमति दिन्छ। उक्त सूचीमा तपाईंले स्थापना गर्नुभएका एपहरूले बनाएका कुनै पनि खाताहरू पर्न सक्छन्।"</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"एपलाई तपाईंको Android टिभी यन्त्रले चिनेका खाताहरूको सूची प्राप्त गर्ने अनुमति दिन्छ। उक्त सूचीमा तपाईंले स्थापना गर्नुभएका एपहरूले बनाएका कुनै पनि खाताहरू पर्न सक्छन्।"</string>
<string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"फोनलाई थाहा भएका खाताहरूको सूची प्राप्त गर्न एपलाई अनुमति दिन्छ। यसले तपाईँले स्थापना गर्नु भएका अनुप्रयोगहरूबाट सृजित कुनै खाताहरू समावेश हुन सक्छ।"</string>
<string name="permlab_accessNetworkState" msgid="2349126720783633918">"नेटवर्क जडानहरू हेर्नहोस्"</string>
<string name="permdesc_accessNetworkState" msgid="4394564702881662849">"एपलाई नेटवर्क जडानहरू जस्तै कुन नेटवर्कहरू अवस्थित हुन्छन् र जडित छन् जसले हेर्नलाई अनुमति दिन्छ।"</string>
@@ -498,21 +498,21 @@
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"एपलाई Wi-Fi पहुँच बिन्दुबाट जडान गर्न र विच्छेदन गर्न र Wi-Fi नेटवर्कहरूको लागि उपकरण कन्फिगरेसनमा परिवर्तनहरू गर्न अनुमति दिन्छ।"</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"Wi-Fi Multicast स्विकृतिलाई अनुमति दिनुहोस्"</string>
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"एपलाई मल्टिकाष्ट ठेगानाहरू प्रयोग गरेर Wi-Fi नेटवर्कमा पठाइएको प्याकेटहरू प्राप्त गर्न अनुमति दिन्छ, केवल तपाईंको ट्याब्लेट मात्र होइन। यसले गैर-मल्टिकाष्ट मोड भन्दा बढी उर्जा प्रयोग गर्दछ।"</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"एपलाई मल्टिकास्ट ठेगानाहरू प्रयोग गरी तपाईंको Android TV यन्त्रमा मात्र नभई कुनै Wi-Fi नेटवर्कमा जोडिएका सबै यन्त्रहरूमा पठाइएका प्याकेटहरू प्राप्त गर्ने अनुमति दिन्छ। यसले गैर मल्टिकास्ट मोडभन्दा बढी पावर खपत गर्छ।"</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"एपलाई मल्टिकास्ट ठेगानाहरू प्रयोग गरी तपाईंको Android टिभी यन्त्रमा मात्र नभई कुनै Wi-Fi नेटवर्कमा जोडिएका सबै यन्त्रहरूमा पठाइएका प्याकेटहरू प्राप्त गर्ने अनुमति दिन्छ। यसले गैर मल्टिकास्ट मोडभन्दा बढी पावर खपत गर्छ।"</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"तपाईँको फोन मात्र होइन, मल्टिकास्ट ठेगानाहरूको प्रयोग गरे Wi-Fi नेटवर्कका सबै उपकरणहरूमा पठाइएका प्याकेटहरू प्राप्त गर्न एपलाई अनुमति दिन्छ। यसले गैर-मल्टिकास्ट मोडभन्दा बढी उर्जा प्रयोग गर्छ।"</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ब्लुटुथ सेटिङहरूमा पहुँच गर्नुहोस्"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानीय ब्लुटुथ ट्याब्लेटलाई कन्फिगर गर्नको लागि र टाढाका उपकरणहरूलाई पत्ता लगाउन र जोड्नको लागि एपलाई अनुमति दिन्छ।"</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"एपलाई तपाईंको Android TV यन्त्रको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग जोडा बनाउने अनुमति दिन्छ।"</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"एपलाई तपाईंको Android टिभी यन्त्रको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग जोडा बनाउने अनुमति दिन्छ।"</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"एपलाई स्थानीय ब्लुटुथ फोन कन्फिगर गर्न र टाढाका उपकरणहरूसँग खोज गर्न र जोडी गर्न अनुमति दिन्छ।"</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAXसँग जोड्नुहोस् वा छुटाउनुहोस्"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"एपलाई वाइम्याक्स सक्षम छ कि छैन र जडान भएको कुनै पनि वाइम्याक्स नेटवर्कहरूको बारेमा जानकारी निर्धारिण गर्न अनुमति दिन्छ।"</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"वाइम्याक्स अवस्था परिवर्तन गर्नुहोस्"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"एपलाई वाइम्याक्स नेटवर्कहरूबाट ट्याब्लेट जडान गर्न र ट्याब्लेट विच्छेदन गर्न अनुमति दिन्छ।"</string>
- <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"एपलाई तपाईंको Android TV यन्त्र WiMAX नेटवर्कहरूमा जोड्ने वा ती नेटवर्कहरूबाट विच्छेद गर्ने अनुमति दिन्छ।"</string>
+ <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"एपलाई तपाईंको Android टिभी यन्त्र WiMAX नेटवर्कहरूमा जोड्ने वा ती नेटवर्कहरूबाट विच्छेद गर्ने अनुमति दिन्छ।"</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"वाइम्याक्स नेटवर्कहरूसँग फोन जोड्न र छुटाउन एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"ब्लुटुथ उपकरणहरूसँग जोडी मिलाउनुहोस्"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"ट्याब्लेटमा ब्लुटुथको कन्फिगुरेसनलाई हेर्न र बनाउन र जोडी उपकरणहरूसँग जडानहरूलाई स्वीकार गर्न एपलाई अनुमति दिन्छ।"</string>
- <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"एपलाई तपाईंको Android TV यन्त्रको ब्लुटुथको कन्फिगुरेसन हेर्ने तथा जोडा बनाइएका यन्त्रहरूसँग जोडिने वा ती यन्त्रहरूले पठाएका जोडिने अनुरोध स्वीकार्ने अनुमति दिन्छ।"</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"एपलाई तपाईंको Android टिभी यन्त्रको ब्लुटुथको कन्फिगुरेसन हेर्ने तथा जोडा बनाइएका यन्त्रहरूसँग जोडिने वा ती यन्त्रहरूले पठाएका जोडिने अनुरोध स्वीकार्ने अनुमति दिन्छ।"</string>
<string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"एपलाई फोनमा ब्लुटुथको कन्फिगरेसन हेर्न र जोडी भएका उपकरणहरूसँग जडानहरू बनाउन र स्वीकार गर्न अनुमति दिन्छ।"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC भुक्तानी सेवासम्बन्धी रुचाइएको जानकारी"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"यसले एपलाई दर्ता गरिएका सहायता तथा मार्गको गन्तव्य जस्ता रुचाइएका NFC भुक्तानी सेवासम्बन्धी जानकारी प्राप्त गर्न दिन्छ।"</string>
@@ -673,10 +673,10 @@
<string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रिन लक पासवर्ड र PIN हरूमा अनुमति दिइएको लम्बाइ र वर्णहरूको नियन्त्रण गर्नुहोस्।"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने ट्याब्लेट लक गर्नुहोस् वा ट्याब्लेटका सबै डेटा मेट्नुहोस्।"</string>
- <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android TV यन्त्र लक गर्नुहोस् वा यन्त्रमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यन्त्रमा भएको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रिनअनलक गर्दा गलत पासवर्ड टाइप भएको संख्या निरीक्षण गर्नुहोस् र यदि निकै धेरै गलत पासवर्डहरू टाइप भएका छन भने फोन लक गर्नुहोस् वा फोनका सबै डेटा मेट्नुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा ट्याब्लेट लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android TV यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप गरेको सङ्ख्या निरीक्षण गर्नुहोस्, र धेरै पटक गलत पासवर्डहरू टाइप गरिएको खण्डमा आफ्नो Android टिभी यन्त्र लक गर्नुहोस् वा यो प्रयोगकर्ताको सम्पूर्ण डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"स्क्रिन अनलक गर्दा गलत पासवर्ड टाइप संख्या अनुगमन गर्नुहोस्, र यदि निकै धेरै गलत पासवर्डहरू टाइप गरिएमा फोन लक गर्नुहोस् वा प्रयोगकर्ताको डेटा मेटाउनुहोस्।"</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"स्क्रिन लक परिवर्तन गर्ने"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"स्क्रिन लक परिवर्तन गर्नुहोस्।"</string>
@@ -684,11 +684,11 @@
<string name="policydesc_forceLock" msgid="1008844760853899693">"कसरी र कहिले स्क्रिन लक गर्ने नियन्त्रण गर्नुहोस्।"</string>
<string name="policylab_wipeData" msgid="1359485247727537311">"सबै डेटा मेट्नुहोस्"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नआउँदै ट्याबल्टको डेटा मेट्नुहोस्।"</string>
- <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android TV यन्त्रको डेटा मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ्याक्ट्री डेटा रिसेट गरेर चेतावनी नदिइकन आफ्नो Android टिभी यन्त्रको डेटा मेटाउनुहोस्।"</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"एउटा फ्याक्ट्रि डेटा रिसेट गरेर चेतावनी नदिइकन फोनको डेटा मेट्न।"</string>
<string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"प्रयोगकर्ता डेटा मेट्नुहोस्"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"चेतावनी बिना यो ट्याब्लेटमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
- <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"यो Android TV यन्त्रमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"यो Android टिभी यन्त्रमा भएको यस प्रयोगकर्ताको डेटा चेतावनी नदिइकन मेटाउनुहोस्।"</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"चेतावनी बिना यो फोनमा यस प्रयोगकर्ताको डेटा मेट्नुहोस्।"</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"उपकरण विश्वव्यापी प्रोक्सी मिलाउनुहोस्"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति सक्षम हुँदा प्रयोग गरिनको लागि यन्त्र ग्लोवल प्रोक्सी सेट गर्नुहोस्। केवल यन्त्र मालिकले ग्लोवल प्रोक्सी सेट गर्न सक्नुहुन्छ।"</string>
@@ -838,7 +838,7 @@
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"अत्यधिक मोहडा खोल्ने प्रयासहरू बढी भए।"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"SIM कार्ड छैन"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"ट्याब्लेटमा SIM कार्ड छैन।"</string>
- <string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"तपाईंको Android TV यन्त्रमा SIM कार्ड छैन।"</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"तपाईंको Android टिभी यन्त्रमा SIM कार्ड छैन।"</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="1408695081255172556">"फोनमा SIM कार्ड छैन।"</string>
<string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"SIM कार्ड घुसाउनुहोस्"</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"SIM कार्ड छैन वा पढ्न मिल्दैन। SIM कार्ड हाल्नुहोस्।"</string>
@@ -861,13 +861,13 @@
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"तपाईंले गलत तरिकाले आफ्नो पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नुभयो। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल कोसिसहरू, तपाईँको Google साइन इन प्रयोग गरी तपाईँको ट्याब्लेट अनलक गर्न भनिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा फरि प्रयास गर्नुहोस्।"</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android TV यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android टिभी यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक ट्याब्लेटलाई अनलक गर्नको लागि गलत तरिकाले कोशिस गर्नुभएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, ट्याब्लेट फ्याट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"तपाईंले आफ्नो Android TV यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"तपाईंले गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक फोन अनलक गर्ने प्रयत्न गर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> बढी असफल प्रयत्नहरू पछि, फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ।"</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"तपाईंले आफ्नो Android TV यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"ढाँचा बिर्सनु भयो?"</string>
@@ -954,7 +954,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"ब्राउजरले भ्रमण गरेको सबै URL हरूको इतिहास र ब्राउजरका सबै बुकमार्कहरू पढ्नको लागि एपलाई अनुमति दिन्छ। नोट: यो अनुमतिलाई तेस्रो पक्ष ब्राउजरहरूद्वारा वा वेब ब्राउज गर्ने क्षमताद्वारा बलपूर्वक गराउन सकिँदैन।"</string>
<string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"वेब बुकमार्कहरू र इतिहास लेख्नुहोस्"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएको ब्राउजरको इतिहास वा बुकमार्कहरू परिमार्जन गर्न अनुमति दिन्छ। यसले एपलाई ब्राजर डेटा मेटाउन वा परिमार्जन गर्न अनुमति दिन सक्दछ। टिप्पणी: यो अनुमति वेब ब्राउज गर्ने क्षमताहरूको साथ तेस्रो-पार्टी ब्राउजर वा अन्य अनुप्रयोगहरूद्वारा लागू गरिएको होइन।"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"एपलाई तपाईंको Android TV यन्त्रमा भण्डार गरिएका ब्राउजरको इतिहास र पुस्तक चिन्हहरू परिमार्जन गर्ने अनुमति दिन्छ। यसले एपलाई ब्राउजरको डेटा मेटाउने वा परिमार्जन गर्ने अनुमति दिन सक्छ। ध्यान दिनुहोस्: तेस्रो पक्षीय ब्राउजर वा वेब ब्राउज गर्ने सुविधा प्रदान गर्ने अन्य एपहरूले यो अनुमति लागू गर्न सक्दैनन्।"</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका ब्राउजरको इतिहास र पुस्तक चिन्हहरू परिमार्जन गर्ने अनुमति दिन्छ। यसले एपलाई ब्राउजरको डेटा मेटाउने वा परिमार्जन गर्ने अनुमति दिन सक्छ। ध्यान दिनुहोस्: तेस्रो पक्षीय ब्राउजर वा वेब ब्राउज गर्ने सुविधा प्रदान गर्ने अन्य एपहरूले यो अनुमति लागू गर्न सक्दैनन्।"</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"तपाईँको फोनमा भण्डारण भएको ब्राउजरको इतिहास वा बुकमार्कहरू परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। यसले सायद ब्राउजर डेटालाई मेट्न वा परिवर्तन गर्नको लागि एपलाई अनुमति दिन्छ। नोट: वेब ब्राउज गर्ने क्षमतासहितका अन्य एपहरू वा तेस्रो- पक्ष ब्राउजरद्वारा सायद यस अनुमतिलाई लागु गर्न सकिंदैन।"</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"एउटा आलर्म सेट गर्नुहोस्"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"स्थापना गरिएको सङ्केत घडी अनुप्रयोगमा सङ्केत समय मिलाउन एपलाई अनुमति दिन्छ। केही सङ्केत घडी एपहरूले यो सुविधा कार्यान्वयन नगर्न सक्छन्।"</string>
@@ -1555,7 +1555,7 @@
<string name="activity_resolver_use_once" msgid="948462794469672658">"एक पटक मात्र"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s कार्य प्रोफाइल समर्थन गर्दैन"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"ट्याब्लेट"</string>
- <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
+ <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टिभी"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"फोन"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"डक स्पिकरहरू"</string>
<string name="default_audio_route_name_hdmi" msgid="5474470558160717850">"HDMI"</string>
@@ -1613,13 +1613,13 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"तपाईँले तपाईँक पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत टाइप गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"तपाईँले तपाईँको अनलक ढाँचा गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल प्रयासहरू, ट्याब्लेट पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"तपाईंले आफ्नो Android TV यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"तपाईँले गलतसँग फोनलाई अनलक गर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल कोसिसहरू, फोनलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ।"</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"तपाईंले आफ्नो Android TV यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android TV यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।"</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"तपाईंले गलत तरिकाले आफ्नो अनलक ढाँचा <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डहरूमा।"</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android TV यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android टिभी यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"तपाईँले आफ्नो अनलक ढाँचा गलत रूपमा <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक तान्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा प्रयास गर्नुहोस्।"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"हटाउनुहोस्"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 269755a8cf76..b4b1523913fe 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1665,7 +1665,7 @@
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Одбиј"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Додирните неку функцију да бисте почели да је користите:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Одаберите функције које ћете користити са дугметом Приступачност"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Одаберите функције које ћете користити са тастером јачине звука као пречицом"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Одаберите функције за пречицу тастером јачине звука"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Услуга <xliff:g id="SERVICE_NAME">%s</xliff:g> је искључена"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Измените пречице"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
@@ -1673,8 +1673,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција боја"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задржали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
- <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задржали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
+ <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Изаберите функцију која ће се користити када додирнете дугме Приступачност:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Одаберите функцију која ће се користити помоћу покрета за приступачност (помоћу два прста превуците нагоре од дна екрана):"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 45c8e349e5f0..b7e8b9a94e30 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1417,7 +1417,7 @@
<string name="forward_intent_to_work" msgid="3620262405636021151">"Ginagamit mo ang app na ito sa iyong profile sa trabaho"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"Pamamaraan ng pag-input"</string>
<string name="sync_binding_label" msgid="469249309424662147">"I-sync"</string>
- <string name="accessibility_binding_label" msgid="1974602776545801715">"Pagiging Accessible"</string>
+ <string name="accessibility_binding_label" msgid="1974602776545801715">"Accessibility"</string>
<string name="wallpaper_binding_label" msgid="1197440498000786738">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="3082405680079923708">"Baguhin ang wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"Notification listener"</string>
@@ -1621,13 +1621,13 @@
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Alisin"</string>
<string name="allow_while_in_use_permission_in_fgs" msgid="4101339676785053656">"Ang sinimulan sa background na serbisyo sa foreground mula sa <xliff:g id="PACKAGENAME">%1$s</xliff:g> ay hindi magkakaroon ng pahintulot habang ginagamit sa mga R build sa hinaharap. Pakipuntahan ang go/r-bg-fgs-restriction at maghain ng bugreport."</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
- <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Pagiging Accessible?"</string>
+ <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Accessibility?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"I-on ang mga feature ng pagiging accessible?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mao-on ang mga feature ng pagiging accessible kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuwede mong baguhin ang mga napiling feature sa Mga Setting &gt; Pagiging Accessible."</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"I-on ang mga feature ng accessibility?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mao-on ang mga feature ng accessibility kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuwede mong baguhin ang mga napiling feature sa Mga Setting &gt; Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"I-on ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mao-on ang feature ng pagiging accessible na <xliff:g id="SERVICE">%1$s</xliff:g> kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting &gt; Pagiging Accessible."</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mao-on ang feature ng accessibility na <xliff:g id="SERVICE">%1$s</xliff:g> kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting &gt; Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"I-on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Huwag i-on"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"NAKA-ON"</string>
@@ -1651,12 +1651,12 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Mga volume key na pinipindot nang matagal. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Mga volume key na pinipindot nang matagal. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Pumili ng feature na gagana sa pamamagitan ng pag-tap mo sa button ng accessibility:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pumili ng feature na gagana sa pamamagitan ng galaw ng pagiging accessible (pag-swipe pataas mula sa ibaba ng screen gamit ang dalawang daliri):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Pumili ng feature na gagana sa pamamagitan ng galaw ng pagiging accessible (pag-swipe pataas mula sa ibaba ng screen gamit ang tatlong daliri):"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang dalawang daliri):"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang tatlong daliri):"</string>
<string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para magpalipat-lipat sa mga feature, pindutin nang matagal ang button ng accessibility."</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para magpalipat-lipat sa mga feature, mag-swipe pataas gamit ang dalawang daliri at i-hold ito."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para magpalipat-lipat sa mga feature, mag-swipe pataas gamit ang tatlong daliri at i-hold ito."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bb9ffc066917..f3b5a63e78fa 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1651,7 +1651,7 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string>
- <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tutun. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
+ <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tutun. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Erişilebilirlik düğmesine dokunduğunuzda kullanmak için bir özellik seçin:"</string>
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index d6542de91e08..e5492714b29f 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -129,6 +129,13 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.companiondevicesupport",
+ sub_dir: "permissions",
+ src: "com.android.car.companiondevicesupport.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.google.android.car.kitchensink",
sub_dir: "permissions",
src: "com.google.android.car.kitchensink.xml",
diff --git a/data/etc/car/com.android.car.companiondevicesupport.xml b/data/etc/car/com.android.car.companiondevicesupport.xml
new file mode 100644
index 000000000000..2067bab20d3d
--- /dev/null
+++ b/data/etc/car/com.android.car.companiondevicesupport.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.companiondevicesupport">
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+ <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+ </privapp-permissions>
+</permissions>
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index d177855e5a7d..1e5877356e8d 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -42,7 +42,7 @@ namespace renderthread {
// to the screen resolution. This is meant to be a conservative default based on
// that analysis. The 4.0f is used because the default pixel format is assumed to
// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f)
+#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
CacheManager::CacheManager()
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 50f0b76b6448..f26fe9d58533 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -203,9 +203,9 @@
<string name="vpn_settings_not_available" msgid="2894137119965668920">"VPN postavke nisu dostupne za ovog korisnika"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"Postavke za povezivanje putem mobitela nisu dostupne za ovog korisnika"</string>
<string name="apn_settings_not_available" msgid="1147111671403342300">"Postavke za ime pristupne tačke nisu dostupne za ovog korisnika"</string>
- <string name="enable_adb" msgid="8072776357237289039">"Otklanjanje grešaka putem uređaja spojenog na USB"</string>
+ <string name="enable_adb" msgid="8072776357237289039">"Otklanjanje grešaka putem USB-a"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"Način rada za uklanjanje grešaka kada je povezan USB"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"Ukini odobrenja otklanjanja grešaka putem uređaja spojenog na USB"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"Ukini odobrenja otklanjanja grešaka putem USB-a"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"Bežično otklanjanje grešaka"</string>
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Način rada otklanjanja grešaka kada je WiFi mreža povezana"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Greška"</string>
@@ -300,11 +300,11 @@
<string name="debug_view_attributes" msgid="3539609843984208216">"Omogući pregled atributa prikaza"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Prijenos podataka na mobilnoj mreži ostaje aktivan čak i kada je aktiviran WiFi (za brzo prebacivanje između mreža)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Korištenje hardverskog ubrzavanja za povezivanje putem mobitela ako je dostupno"</string>
- <string name="adb_warning_title" msgid="7708653449506485728">"Omogućiti otklanjanje grešaka putem uređaja spojenog na USB?"</string>
- <string name="adb_warning_message" msgid="8145270656419669221">"Otklanjanje grešaka putem uređaja spojenog na USB je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
+ <string name="adb_warning_title" msgid="7708653449506485728">"Omogućiti otklanjanje grešaka putem USB-a?"</string>
+ <string name="adb_warning_message" msgid="8145270656419669221">"Otklanjanje grešaka putem USB-a je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Omogućiti bežično otklanjanje grešaka?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Bežično otklanjanje grešaka je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Opozvati pristup otklanjanju grešaka putem uređaja spojenog na USB za sve računare koje ste prethodno ovlastili?"</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Opozvati pristup otklanjanju grešaka putem USB-a za sve računare koje ste prethodno ovlastili?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Dopustiti postavke za razvoj?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Ove postavke su namijenjene samo za svrhe razvoja. Mogu izazvati pogrešno ponašanje uređaja i aplikacija na njemu."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Potvrdi aplikacije putem USB-a"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index c3993e9063b2..dc9384aa7c5d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -19,42 +19,9 @@ import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
-import android.telephony.CarrierConfigManager;
-
-import androidx.annotation.VisibleForTesting;
public class TetherUtil {
-
- @VisibleForTesting
- static boolean isEntitlementCheckRequired(Context context) {
- final CarrierConfigManager configManager = (CarrierConfigManager) context
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null || configManager.getConfig() == null) {
- // return service default
- return true;
- }
- return configManager.getConfig().getBoolean(CarrierConfigManager
- .KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- }
-
- public static boolean isProvisioningNeeded(Context context) {
- // Keep in sync with other usage of config_mobile_hotspot_provision_app.
- // ConnectivityManager#enforceTetherChangePermission
- String[] provisionApp = context.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app);
- if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
- || provisionApp == null) {
- return false;
- }
- // Check carrier config for entitlement checks
- if (isEntitlementCheckRequired(context) == false) {
- return false;
- }
- return (provisionApp.length == 2);
- }
-
public static boolean isTetherAvailable(Context context) {
final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
final boolean tetherConfigDisallowed = RestrictedLockUtilsInternal
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
index 0ca779162ef2..df08809b5601 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
@@ -60,13 +60,6 @@ public class TetherUtilTest {
}
@Test
- public void isEntitlementCheckRequired_noConfigManager_returnTrue() {
- doReturn(null).when(mContext).getSystemService(Context.CARRIER_CONFIG_SERVICE);
-
- assertThat(TetherUtil.isEntitlementCheckRequired(mContext)).isTrue();
- }
-
- @Test
public void isTetherAvailable_supported_configDisallowed_hasUserRestriction_returnTrue() {
setupIsTetherAvailable(true, true, true);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 75b680dd3a88..bec8151a1351 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -219,7 +219,9 @@ public class SettingsHelper {
*/
@VisibleForTesting
public String getRealValueForSystemSetting(String setting) {
- return Settings.System.getString(mContext.getContentResolver(),
+ // The real value irrespectively of the original setting's namespace is stored in
+ // Settings.Secure.
+ return Settings.Secure.getString(mContext.getContentResolver(),
setting + SETTING_ORIGINAL_KEY_SUFFIX);
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 985269b2bb75..5b6155180e0a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -263,6 +263,9 @@
<!-- Restore settings (used by QS) even if they have been modified -->
<uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" />
+ <!-- Permission to make accessibility service access Bubbles -->
+ <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 67c4458de2f2..5f2a946a1b6d 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -129,7 +129,7 @@
</style>
<style name="TextAppearance.Keyguard.BottomArea">
- <item name="android:textSize">16sp</item>
+ <item name="android:textSize">14sp</item>
<item name="android:maxLines">1</item>
<item name="android:textColor">?attr/wallpaperTextColor</item>
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 7313d54c599a..0f561cb933e6 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -41,7 +41,8 @@
android:paddingStart="16dp"
android:paddingBottom="16dp"
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
- android:maxLines="1"
+ android:maxLines="2"
+ android:ellipsize="end"
android:text="@string/bubbles_user_education_manage_title"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
@@ -52,6 +53,8 @@
android:paddingStart="16dp"
android:paddingBottom="24dp"
android:text="@string/bubbles_user_education_manage"
+ android:maxLines="2"
+ android:ellipsize="end"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml
index a0d8ae42f584..4850e7534943 100644
--- a/packages/SystemUI/res/layout/controls_management_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_management_favorites.xml
@@ -26,6 +26,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/controls_management_list_margin"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_marginStart="@dimen/controls_management_status_side_margin"
+ android:layout_marginEnd="@dimen/controls_management_status_side_margin"
android:gravity="center_horizontal"
/>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index b90a3719b08f..04de9784812f 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -42,6 +42,17 @@
android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
android:accessibilityLiveRegion="polite"/>
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_enterprise_disclosure"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingStart="@dimen/keyguard_indication_text_padding"
+ android:paddingEnd="@dimen/keyguard_indication_text_padding"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:alpha=".54"
+ android:visibility="gone"/>
+
</LinearLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index fd9936f6b8ea..acef94315e91 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -20,111 +20,126 @@
android:orientation="vertical"
android:background="@drawable/rounded_bg_full">
- <!-- Header -->
- <LinearLayout
+ <!-- Scrollview is necessary to fit everything in landscape layout -->
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="@dimen/screenrecord_dialog_padding">
- <ImageView
- android:layout_width="@dimen/screenrecord_logo_size"
- android:layout_height="@dimen/screenrecord_logo_size"
- android:src="@drawable/ic_screenrecord"
- android:tint="@color/GM2_red_500"
- android:layout_marginBottom="@dimen/screenrecord_dialog_padding"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:text="@string/screenrecord_start_label"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/screenrecord_description"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingTop="@dimen/screenrecord_dialog_padding"
- android:paddingBottom="@dimen/screenrecord_dialog_padding"/>
+ android:layout_height="wrap_content">
- <!-- Options -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="@dimen/screenrecord_logo_size"
- android:layout_height="@dimen/screenrecord_logo_size"
- android:src="@drawable/ic_mic_26dp"
- android:tint="@color/GM2_grey_700"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
- <Spinner
- android:id="@+id/screen_recording_options"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:prompt="@string/screenrecord_audio_label"/>
- <Switch
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_weight="1"
- android:layout_gravity="end"
- android:id="@+id/screenrecord_audio_switch"/>
- </LinearLayout>
+ android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="@dimen/screenrecord_logo_size"
- android:layout_height="@dimen/screenrecord_logo_size"
- android:src="@drawable/ic_touch"
- android:tint="@color/GM2_grey_700"
- android:layout_gravity="center"
- android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
- <Switch
+ <!-- Header -->
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:id="@+id/screenrecord_taps_switch"
- android:text="@string/screenrecord_taps_label"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="@dimen/screenrecord_dialog_padding">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_logo_size"
+ android:layout_height="@dimen/screenrecord_logo_size"
+ android:src="@drawable/ic_screenrecord"
+ android:tint="@color/GM2_red_500"
+ android:layout_marginBottom="@dimen/screenrecord_dialog_padding"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/screenrecord_start_label"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/screenrecord_description"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:paddingTop="@dimen/screenrecord_dialog_padding"
+ android:paddingBottom="@dimen/screenrecord_dialog_padding"/>
- </LinearLayout>
- </LinearLayout>
+ <!-- Options -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_logo_size"
+ android:layout_height="@dimen/screenrecord_logo_size"
+ android:src="@drawable/ic_mic_26dp"
+ android:tint="@color/GM2_grey_700"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
+ <Spinner
+ android:id="@+id/screen_recording_options"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_weight="1"
+ android:prompt="@string/screenrecord_audio_label"/>
+ <Switch
+ android:layout_width="wrap_content"
+ android:minWidth="48dp"
+ android:layout_height="48dp"
+ android:layout_weight="0"
+ android:layout_gravity="end"
+ android:contentDescription="@string/screenrecord_audio_label"
+ android:id="@+id/screenrecord_audio_switch"/>
+ </LinearLayout>
- <!-- hr -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/GM2_grey_300"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageView
+ android:layout_width="@dimen/screenrecord_logo_size"
+ android:layout_height="@dimen/screenrecord_logo_size"
+ android:src="@drawable/ic_touch"
+ android:tint="@color/GM2_grey_700"
+ android:layout_gravity="center"
+ android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
+ <Switch
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:id="@+id/screenrecord_taps_switch"
+ android:text="@string/screenrecord_taps_label"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
- <!-- Buttons -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="@dimen/screenrecord_dialog_padding">
- <Button
- android:id="@+id/button_cancel"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="0"
- android:layout_gravity="start"
- android:text="@string/cancel"
- style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/>
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
- <Button
- android:id="@+id/button_start"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="0"
- android:layout_gravity="end"
- android:text="@string/screenrecord_start"
- style="@android:style/Widget.DeviceDefault.Button.Colored"/>
- </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <!-- hr -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/GM2_grey_300"/>
+
+ <!-- Buttons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="@dimen/screenrecord_dialog_padding">
+ <Button
+ android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:layout_gravity="start"
+ android:text="@string/cancel"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <Button
+ android:id="@+id/button_start"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:layout_gravity="end"
+ android:text="@string/screenrecord_start"
+ style="@android:style/Widget.DeviceDefault.Button.Colored"/>
+ </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d176fed57459..9bcfdc43fbbf 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1325,8 +1325,8 @@
<dimen name="controls_management_editing_list_margin">48dp</dimen>
<dimen name="controls_management_editing_divider_margin">24dp</dimen>
<dimen name="controls_management_apps_extra_side_margin">8dp</dimen>
- <dimen name="controls_management_apps_top_margin"></dimen>
<dimen name="controls_management_zone_top_margin">32dp</dimen>
+ <dimen name="controls_management_status_side_margin">16dp</dimen>
<dimen name="controls_management_page_indicator_height">24dp</dimen>
<dimen name="controls_management_checkbox_size">25dp</dimen>
<dimen name="controls_title_size">24sp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f4141e2f2ee8..73568eab5eac 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2741,8 +2741,10 @@
<!-- Controls management favorites screen, See other apps with changes made [CHAR LIMIT=NONE] -->
<string name="controls_favorite_toast_no_changes">Changes not saved</string>
- <!-- Controls management controls screen error on load message [CHAR LIMIT=60] -->
- <string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
+ <!-- Controls management controls screen error on load message [CHAR LIMIT=NONE] -->
+ <string name="controls_favorite_load_error">Controls could not be loaded. Check the <xliff:g id="app" example="System UI">%s</xliff:g> app to make sure that the app settings haven\u2019t changed.</string>
+ <!-- Controls management controls screen no controls found on load message [CHAR LIMIT=NONE] -->
+ <string name="controls_favorite_load_none">Compatible controls unavailable</string>
<!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
<string name="controls_favorite_other_zone_header">Other</string>
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 525e98971266..7ae3e73caa23 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -164,7 +164,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
return Long.max(mShowAndGoEndsAt - SystemClock.elapsedRealtime(), 0);
}
- boolean areHandlesShowing() {
+ public boolean areHandlesShowing() {
return mHandlesShowing;
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt
new file mode 100644
index 000000000000..e65139275685
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.internal.app.AssistUtils
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.util.FrameworkStatsLog
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.assist.AssistantInvocationEvent.Companion.deviceStateFromLegacyDeviceState
+import com.android.systemui.assist.AssistantInvocationEvent.Companion.eventFromLegacyInvocationType
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Class for reporting events related to Assistant sessions. */
+@Singleton
+open class AssistLogger @Inject constructor(
+ protected val context: Context,
+ protected val uiEventLogger: UiEventLogger,
+ private val assistUtils: AssistUtils,
+ private val phoneStateMonitor: PhoneStateMonitor,
+ private val assistHandleBehaviorController: AssistHandleBehaviorController
+) {
+
+ private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
+
+ private var currentInstanceId: InstanceId? = null
+
+ fun reportAssistantInvocationEventFromLegacy(
+ legacyInvocationType: Int,
+ isInvocationComplete: Boolean,
+ assistantComponent: ComponentName? = null,
+ legacyDeviceState: Int? = null
+ ) {
+ val deviceState = if (legacyDeviceState == null) {
+ null
+ } else {
+ deviceStateFromLegacyDeviceState(legacyDeviceState)
+ }
+ reportAssistantInvocationEvent(
+ eventFromLegacyInvocationType(legacyInvocationType, isInvocationComplete),
+ assistantComponent,
+ deviceState)
+ }
+
+ fun reportAssistantInvocationEvent(
+ invocationEvent: AssistantInvocationEvent,
+ assistantComponent: ComponentName? = null,
+ deviceState: Int? = null
+ ) {
+
+ val assistComponentFinal = assistantComponent ?: getAssistantComponentForCurrentUser()
+
+ val assistantUid = getAssistantUid(assistComponentFinal)
+
+ val deviceStateFinal = deviceState
+ ?: deviceStateFromLegacyDeviceState(phoneStateMonitor.phoneState)
+
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ASSISTANT_INVOCATION_REPORTED,
+ invocationEvent.id,
+ assistantUid,
+ assistComponentFinal.flattenToString(),
+ getOrCreateInstanceId().id,
+ deviceStateFinal,
+ assistHandleBehaviorController.areHandlesShowing())
+ reportAssistantInvocationExtraData()
+ }
+
+ fun reportAssistantSessionEvent(sessionEvent: AssistantSessionEvent) {
+ val assistantComponent = getAssistantComponentForCurrentUser()
+ val assistantUid = getAssistantUid(assistantComponent)
+ uiEventLogger.logWithInstanceId(
+ sessionEvent,
+ assistantUid,
+ assistantComponent.flattenToString(),
+ getOrCreateInstanceId())
+
+ if (SESSION_END_EVENTS.contains(sessionEvent)) {
+ clearInstanceId()
+ }
+ }
+
+ protected open fun reportAssistantInvocationExtraData() {
+ }
+
+ protected fun getOrCreateInstanceId(): InstanceId {
+ val instanceId = currentInstanceId ?: instanceIdSequence.newInstanceId()
+ currentInstanceId = instanceId
+ return instanceId
+ }
+
+ protected fun clearInstanceId() {
+ currentInstanceId = null
+ }
+
+ protected fun getAssistantComponentForCurrentUser(): ComponentName {
+ return assistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser())
+ }
+
+ protected fun getAssistantUid(assistantComponent: ComponentName): Int {
+ var assistantUid = 0
+ try {
+ assistantUid = context.packageManager.getApplicationInfo(
+ assistantComponent.packageName, /* flags = */
+ 0).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Unable to find Assistant UID", e)
+ }
+ return assistantUid
+ }
+
+ companion object {
+ protected const val TAG = "AssistLogger"
+
+ private const val INSTANCE_ID_MAX = 1 shl 20
+
+ private val SESSION_END_EVENTS =
+ setOf(
+ AssistantSessionEvent.ASSISTANT_SESSION_INVOCATION_CANCELLED,
+ AssistantSessionEvent.ASSISTANT_SESSION_CLOSE)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index a7533adc795e..6d179f27f4fb 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -40,11 +40,8 @@ import android.widget.ImageView;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
-import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.R;
@@ -124,7 +121,6 @@ public class AssistManager {
private static final long TIMEOUT_SERVICE = 2500;
private static final long TIMEOUT_ACTIVITY = 1000;
- private static final int INSTANCE_ID_MAX = 1 << 20;
protected final Context mContext;
private final WindowManager mWindowManager;
@@ -134,8 +130,7 @@ public class AssistManager {
private final AssistHandleBehaviorController mHandleController;
private final UiController mUiController;
protected final Lazy<SysUiState> mSysUiState;
- protected final InstanceIdSequence mInstanceIdSequence =
- new InstanceIdSequence(INSTANCE_ID_MAX);
+ protected final AssistLogger mAssistLogger;
private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -202,7 +197,9 @@ public class AssistManager {
PhoneStateMonitor phoneStateMonitor,
OverviewProxyService overviewProxyService,
ConfigurationController configurationController,
- Lazy<SysUiState> sysUiState) {
+ Lazy<SysUiState> sysUiState,
+ DefaultUiController defaultUiController,
+ AssistLogger assistLogger) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
@@ -211,6 +208,7 @@ public class AssistManager {
mAssistDisclosure = new AssistDisclosure(context, new Handler());
mPhoneStateMonitor = phoneStateMonitor;
mHandleController = handleController;
+ mAssistLogger = assistLogger;
configurationController.addCallback(mConfigurationListener);
@@ -221,7 +219,7 @@ public class AssistManager {
mConfigurationListener.onConfigChanged(context.getResources().getConfiguration());
mShouldEnableOrb = !ActivityManager.isLowRamDeviceStatic();
- mUiController = new DefaultUiController(mContext);
+ mUiController = defaultUiController;
mSysUiState = sysUiState;
@@ -248,6 +246,8 @@ public class AssistManager {
if (VERBOSE) {
Log.v(TAG, "Voice open");
}
+ mAssistLogger.reportAssistantSessionEvent(
+ AssistantSessionEvent.ASSISTANT_SESSION_UPDATE);
}
@Override
@@ -255,6 +255,8 @@ public class AssistManager {
if (VERBOSE) {
Log.v(TAG, "Voice closed");
}
+ mAssistLogger.reportAssistantSessionEvent(
+ AssistantSessionEvent.ASSISTANT_SESSION_CLOSE);
}
@Override
@@ -298,15 +300,19 @@ public class AssistManager {
if (args == null) {
args = new Bundle();
}
- int invocationType = args.getInt(INVOCATION_TYPE_KEY, 0);
- if (invocationType == INVOCATION_TYPE_GESTURE) {
+ int legacyInvocationType = args.getInt(INVOCATION_TYPE_KEY, 0);
+ if (legacyInvocationType == INVOCATION_TYPE_GESTURE) {
mHandleController.onAssistantGesturePerformed();
}
- int phoneState = mPhoneStateMonitor.getPhoneState();
- args.putInt(INVOCATION_PHONE_STATE_KEY, phoneState);
+ int legacyDeviceState = mPhoneStateMonitor.getPhoneState();
+ args.putInt(INVOCATION_PHONE_STATE_KEY, legacyDeviceState);
args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime());
- logStartAssist(/* instanceId = */ null, invocationType, phoneState);
- logStartAssistLegacy(invocationType, phoneState);
+ mAssistLogger.reportAssistantInvocationEventFromLegacy(
+ legacyInvocationType,
+ /* isInvocationComplete = */ true,
+ assistComponent,
+ legacyDeviceState);
+ logStartAssistLegacy(legacyInvocationType, legacyDeviceState);
startAssistInternal(args, assistComponent, isService);
}
@@ -506,34 +512,6 @@ public class AssistManager {
return toLoggingSubType(invocationType, mPhoneStateMonitor.getPhoneState());
}
- protected void logStartAssist(
- @Nullable InstanceId instanceId, int invocationType, int deviceState) {
- InstanceId currentInstanceId =
- instanceId == null ? mInstanceIdSequence.newInstanceId() : instanceId;
- ComponentName assistantComponent =
- mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
- int assistantUid = 0;
- try {
- assistantUid =
- mContext.getPackageManager()
- .getApplicationInfo(
- assistantComponent.getPackageName(),
- /* flags = */ 0)
- .uid;
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to find Assistant UID", e);
- }
-
- FrameworkStatsLog.write(
- FrameworkStatsLog.ASSISTANT_INVOCATION_REPORTED,
- AssistantInvocationEvent.Companion.eventIdFromLegacyInvocationType(invocationType),
- assistantUid,
- assistantComponent.flattenToString(),
- currentInstanceId.getId(),
- AssistantInvocationEvent.Companion.deviceStateFromLegacyDeviceState(deviceState),
- mHandleController.areHandlesShowing());
- }
-
protected void logStartAssistLegacy(int invocationType, int phoneState) {
MetricsLogger.action(
new LogMaker(MetricsEvent.ASSISTANT)
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt
index 1de7b8423617..fb5f1d1f725f 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt
@@ -50,33 +50,55 @@ enum class AssistantInvocationEvent(private val id: Int) : UiEventLogger.UiEvent
ASSISTANT_INVOCATION_HOME_LONG_PRESS(447),
@UiEvent(doc = "Assistant invoked by physical gesture")
- ASSISTANT_INVOCATION_PHYSICAL_GESTURE(448);
+ ASSISTANT_INVOCATION_PHYSICAL_GESTURE(448),
+
+ @UiEvent(doc = "Assistant invocation started by unknown method")
+ ASSISTANT_INVOCATION_START_UNKNOWN(530),
+
+ @UiEvent(doc = "Assistant invocation started by touch gesture")
+ ASSISTANT_INVOCATION_START_TOUCH_GESTURE(531),
+
+ @UiEvent(doc = "Assistant invocation started by physical gesture")
+ ASSISTANT_INVOCATION_START_PHYSICAL_GESTURE(532);
override fun getId(): Int {
return id
}
companion object {
- fun eventIdFromLegacyInvocationType(legacyInvocationType: Int): Int {
- return when (legacyInvocationType) {
- AssistManager.INVOCATION_TYPE_GESTURE ->
- ASSISTANT_INVOCATION_TOUCH_GESTURE
-
- AssistManager.INVOCATION_TYPE_OTHER ->
- ASSISTANT_INVOCATION_PHYSICAL_GESTURE
-
- AssistManager.INVOCATION_TYPE_VOICE ->
- ASSISTANT_INVOCATION_HOTWORD
-
- AssistManager.INVOCATION_TYPE_QUICK_SEARCH_BAR ->
- ASSISTANT_INVOCATION_QUICK_SEARCH_BAR
-
- AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS ->
- ASSISTANT_INVOCATION_HOME_LONG_PRESS
-
- else ->
- ASSISTANT_INVOCATION_UNKNOWN
- }.id
+ fun eventFromLegacyInvocationType(legacyInvocationType: Int, isInvocationComplete: Boolean)
+ : AssistantInvocationEvent {
+ return if (isInvocationComplete) {
+ when (legacyInvocationType) {
+ AssistManager.INVOCATION_TYPE_GESTURE ->
+ ASSISTANT_INVOCATION_TOUCH_GESTURE
+
+ AssistManager.INVOCATION_TYPE_OTHER ->
+ ASSISTANT_INVOCATION_PHYSICAL_GESTURE
+
+ AssistManager.INVOCATION_TYPE_VOICE ->
+ ASSISTANT_INVOCATION_HOTWORD
+
+ AssistManager.INVOCATION_TYPE_QUICK_SEARCH_BAR ->
+ ASSISTANT_INVOCATION_QUICK_SEARCH_BAR
+
+ AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS ->
+ ASSISTANT_INVOCATION_HOME_LONG_PRESS
+
+ else ->
+ ASSISTANT_INVOCATION_UNKNOWN
+ }
+ } else {
+ when (legacyInvocationType) {
+ AssistManager.INVOCATION_TYPE_GESTURE ->
+ ASSISTANT_INVOCATION_START_TOUCH_GESTURE
+
+ AssistManager.INVOCATION_TYPE_OTHER ->
+ ASSISTANT_INVOCATION_START_PHYSICAL_GESTURE
+
+ else -> ASSISTANT_INVOCATION_START_UNKNOWN
+ }
+ }
}
fun deviceStateFromLegacyDeviceState(legacyDeviceState: Int): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
new file mode 100644
index 000000000000..8b953fa46441
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class AssistantSessionEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Unknown assistant session event")
+ ASSISTANT_SESSION_UNKNOWN(523),
+
+ @UiEvent(doc = "Assistant session dismissed due to timeout")
+ ASSISTANT_SESSION_TIMEOUT_DISMISS(524),
+
+ @UiEvent(doc = "User began a gesture for invoking the Assistant")
+ ASSISTANT_SESSION_INVOCATION_START(525),
+
+ @UiEvent(doc =
+ "User stopped a gesture for invoking the Assistant before the gesture was completed")
+ ASSISTANT_SESSION_INVOCATION_CANCELLED(526),
+
+ @UiEvent(doc = "User manually dismissed the Assistant session")
+ ASSISTANT_SESSION_USER_DISMISS(527),
+
+ @UiEvent(doc = "The Assistant session has changed modes")
+ ASSISTANT_SESSION_UPDATE(528),
+
+ @UiEvent(doc = "The Assistant session completed")
+ ASSISTANT_SESSION_CLOSE(529);
+
+ override fun getId(): Int {
+ return id
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 652ce6f04db0..257ad50eff61 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -104,7 +104,7 @@ public final class PhoneStateMonitor {
});
}
- int getPhoneState() {
+ public int getPhoneState() {
int phoneState;
if (isShadeFullscreen()) {
phoneState = getPhoneLockscreenState();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 68242f0a0ac2..05f3617dd1d6 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -38,15 +38,21 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.assist.AssistHandleViewController;
+import com.android.systemui.assist.AssistLogger;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.assist.AssistantSessionEvent;
import com.android.systemui.statusbar.NavigationBarController;
import java.util.Locale;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Default UiController implementation. Shows white edge lights along the bottom of the phone,
* expanding from the corners to meet in the center.
*/
+@Singleton
public class DefaultUiController implements AssistManager.UiController {
private static final String TAG = "DefaultUiController";
@@ -58,6 +64,7 @@ public class DefaultUiController implements AssistManager.UiController {
protected final FrameLayout mRoot;
protected InvocationLightsView mInvocationLightsView;
+ protected final AssistLogger mAssistLogger;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mLayoutParams;
@@ -69,7 +76,9 @@ public class DefaultUiController implements AssistManager.UiController {
private ValueAnimator mInvocationAnimator = new ValueAnimator();
- public DefaultUiController(Context context) {
+ @Inject
+ public DefaultUiController(Context context, AssistLogger assistLogger) {
+ mAssistLogger = assistLogger;
mRoot = new FrameLayout(context);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -142,6 +151,11 @@ public class DefaultUiController implements AssistManager.UiController {
if (VERBOSE) {
Log.v(TAG, "Invocation started: type=" + type);
}
+ mAssistLogger.reportAssistantInvocationEventFromLegacy(
+ type,
+ /* isInvocationComplete = */ false,
+ /* assistantComponent = */ null,
+ /* legacyDeviceState = */ null);
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
@@ -152,6 +166,8 @@ public class DefaultUiController implements AssistManager.UiController {
if (VERBOSE) {
Log.v(TAG, "Invocation cancelled: type=" + type);
}
+ mAssistLogger.reportAssistantSessionEvent(
+ AssistantSessionEvent.ASSISTANT_SESSION_INVOCATION_CANCELLED);
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(DISMISS_REASON_INVOCATION_CANCELLED));
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index 55be77ca3be5..a86a46960bcb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -100,6 +100,7 @@ public class BadgedImageView extends ImageView {
mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE);
setFocusable(true);
+ setClickable(true);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 6da7bc8a2ade..6377b4f0a9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -348,9 +348,10 @@ class Bubble implements BubbleViewProvider {
mAppUid = entry.getSbn().getUid();
mInstanceId = entry.getSbn().getInstanceId();
mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
+ mShortcutInfo = (entry.getBubbleMetadata() != null
+ && entry.getBubbleMetadata().getShortcutId() != null
+ && entry.getRanking() != null) ? entry.getRanking().getShortcutInfo() : null;
if (entry.getRanking() != null) {
- mShortcutInfo = entry.getRanking().getShortcutInfo() != null
- ? entry.getRanking().getShortcutInfo() : mShortcutInfo;
mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
}
if (entry.getBubbleMetadata() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index b2c5402c7cd3..c42920965ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -67,6 +67,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseSetArray;
import android.view.Display;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -394,6 +395,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
: statusBarService;
mBubbleScrim = new ScrimView(mContext);
+ mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mCurrentUserId = mNotifUserManager.getCurrentUserId();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 2f7ffde50fd4..959130bbdd0f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -35,6 +35,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityView;
@@ -126,6 +127,7 @@ public class BubbleExpandedView extends LinearLayout {
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
private WindowManager mWindowManager;
+ private ActivityManager mActivityManager;
private BubbleStackView mStackView;
private View mVirtualImeView;
@@ -191,6 +193,10 @@ public class BubbleExpandedView extends LinearLayout {
}
});
mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
+ break;
+ case ACTIVITY_STARTED:
+ post(() -> mActivityManager.moveTaskToFront(mTaskId, 0));
+ break;
}
}
@@ -252,6 +258,7 @@ public class BubbleExpandedView extends LinearLayout {
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
updateDimensions();
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
void updateDimensions() {
@@ -661,7 +668,7 @@ public class BubbleExpandedView extends LinearLayout {
desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
}
float height = Math.min(desiredHeight, getMaxExpandedHeight());
- height = Math.max(height, mIsOverflow? mOverflowHeight : mMinHeight);
+ height = Math.max(height, mMinHeight);
ViewGroup.LayoutParams lp = mActivityView.getLayoutParams();
mNeedsNewHeight = lp.height != height;
if (!mKeyboardVisible) {
@@ -746,11 +753,7 @@ public class BubbleExpandedView extends LinearLayout {
if (mActivityView == null) {
return;
}
- switch (mActivityViewStatus) {
- case INITIALIZED:
- case ACTIVITY_STARTED:
- mActivityView.release();
- }
+ mActivityView.release();
if (mTaskId != -1) {
try {
ActivityTaskManager.getService().removeTask(mTaskId);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 0b25c444a8b8..b9437078a330 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -27,7 +27,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 41dc08124565..297d92e3135f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -144,6 +144,12 @@ public class BubbleStackView extends FrameLayout
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
+ /**
+ * How long to wait to animate the stack temporarily invisible after a drag/flyout hide
+ * animation ends, if we are in fact temporarily invisible.
+ */
+ private static final int ANIMATE_TEMPORARILY_INVISIBLE_DELAY = 1000;
+
private static final PhysicsAnimator.SpringConfig FLYOUT_IME_ANIMATION_SPRING_CONFIG =
new PhysicsAnimator.SpringConfig(
StackAnimationController.IME_ANIMATION_STIFFNESS,
@@ -280,6 +286,9 @@ public class BubbleStackView extends FrameLayout
/** Whether or not the stack is temporarily invisible off the side of the screen. */
private boolean mTemporarilyInvisible = false;
+ /** Whether we're in the middle of dragging the stack around by touch. */
+ private boolean mIsDraggingStack = false;
+
/** Description of current animation controller state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
@@ -477,6 +486,8 @@ public class BubbleStackView extends FrameLayout
private OnClickListener mBubbleClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
+ mIsDraggingStack = false; // If the touch ended in a click, we're no longer dragging.
+
// Bubble clicks either trigger expansion/collapse or a bubble switch, both of which we
// shouldn't interrupt. These are quick transitions, so it's not worth trying to adjust
// the animations inflight.
@@ -562,6 +573,12 @@ public class BubbleStackView extends FrameLayout
// Also, save the magnetized stack so we can dispatch touch events to it.
mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
mMagnetizedObject.setMagnetListener(mStackMagnetListener);
+
+ mIsDraggingStack = true;
+
+ // Cancel animations to make the stack temporarily invisible, since we're now
+ // dragging it.
+ updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
}
passEventToMagnetizedObject(ev);
@@ -623,6 +640,11 @@ public class BubbleStackView extends FrameLayout
hideDismissTarget();
}
+
+ mIsDraggingStack = false;
+
+ // Hide the stack after a delay, if needed.
+ updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
}
};
@@ -867,14 +889,7 @@ public class BubbleStackView extends FrameLayout
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
mStackAnimationController.updateResources(mOrientation);
-
- // Reposition & adjust the height for new orientation
- if (mIsExpanded) {
- mExpandedViewContainer.setTranslationY(getExpandedViewY());
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().updateView(getLocationOnScreen());
- }
- }
+ mBubbleOverflow.updateDimensions();
// Need to update the padding around the view
WindowInsets insets = getRootWindowInsets();
@@ -898,9 +913,15 @@ public class BubbleStackView extends FrameLayout
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
+ beforeExpandedViewAnimation();
+ updateOverflowVisibility();
+ updatePointerPosition();
mExpandedAnimationController.expandFromStack(() -> {
- updatePointerPosition();
+ afterExpandedViewAnimation();
} /* after */);
+ mExpandedViewContainer.setTranslationX(0);
+ mExpandedViewContainer.setTranslationY(getExpandedViewY());
+ mExpandedViewContainer.setAlpha(1f);
}
if (mVerticalPosPercentBeforeRotation >= 0) {
mStackAnimationController.moveStackToSimilarPositionAfterRotation(
@@ -966,14 +987,35 @@ public class BubbleStackView extends FrameLayout
*/
public void setTemporarilyInvisible(boolean invisible) {
mTemporarilyInvisible = invisible;
- animateTemporarilyInvisible();
+
+ // If we are animating out, hide immediately if possible so we animate out with the status
+ // bar.
+ updateTemporarilyInvisibleAnimation(invisible /* hideImmediately */);
}
/**
- * Animates onto or off the screen depending on whether we're temporarily invisible, and whether
- * a flyout is visible.
+ * Animates the stack to be temporarily invisible, if needed.
+ *
+ * If we're currently dragging the stack, or a flyout is visible, the stack will remain visible.
+ * regardless of the value of {@link #mTemporarilyInvisible}. This method is called on ACTION_UP
+ * as well as whenever a flyout hides, so we will animate invisible at that point if needed.
*/
- private void animateTemporarilyInvisible() {
+ private void updateTemporarilyInvisibleAnimation(boolean hideImmediately) {
+ removeCallbacks(mAnimateTemporarilyInvisibleImmediate);
+
+ if (mIsDraggingStack) {
+ // If we're dragging the stack, don't animate it invisible.
+ return;
+ }
+
+ final boolean shouldHide =
+ mTemporarilyInvisible && mFlyout.getVisibility() != View.VISIBLE;
+
+ postDelayed(mAnimateTemporarilyInvisibleImmediate,
+ shouldHide && !hideImmediately ? ANIMATE_TEMPORARILY_INVISIBLE_DELAY : 0);
+ }
+
+ private final Runnable mAnimateTemporarilyInvisibleImmediate = () -> {
if (mTemporarilyInvisible && mFlyout.getVisibility() != View.VISIBLE) {
if (mStackAnimationController.isStackOnLeftSide()) {
animate().translationX(-mBubbleSize).start();
@@ -983,7 +1025,7 @@ public class BubbleStackView extends FrameLayout
} else {
animate().translationX(0).start();
}
- }
+ };
private void setUpManageMenu() {
if (mManageMenu != null) {
@@ -2299,7 +2341,9 @@ public class BubbleStackView extends FrameLayout
BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
mFlyout.setVisibility(INVISIBLE);
- animateTemporarilyInvisible();
+
+ // Hide the stack after a delay, if needed.
+ updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
};
mFlyout.setVisibility(INVISIBLE);
@@ -2317,7 +2361,7 @@ public class BubbleStackView extends FrameLayout
final Runnable expandFlyoutAfterDelay = () -> {
mAnimateInFlyout = () -> {
mFlyout.setVisibility(VISIBLE);
- animateTemporarilyInvisible();
+ updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
mFlyoutDragDeltaX =
mStackAnimationController.isStackOnLeftSide()
? -mFlyout.getWidth()
@@ -2466,6 +2510,10 @@ public class BubbleStackView extends FrameLayout
.spring(DynamicAnimation.SCALE_Y, 1f)
.spring(DynamicAnimation.TRANSLATION_X, targetX)
.spring(DynamicAnimation.TRANSLATION_Y, targetY)
+ .withEndActions(() -> {
+ View child = mManageMenu.getChildAt(0);
+ child.requestAccessibilityFocus();
+ })
.start();
mManageMenu.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index cb8995a72dc3..8e232520a196 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -203,12 +203,22 @@ public class ExpandedAnimationController
public void updateResources(int orientation, Point displaySize) {
mScreenOrientation = orientation;
mDisplaySize = displaySize;
- if (mLayout != null) {
- Resources res = mLayout.getContext().getResources();
- mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
- mStatusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
+ if (mLayout == null) {
+ return;
}
+ Resources res = mLayout.getContext().getResources();
+ mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
+ mStatusBarHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
+ mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
+ mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
+
+ // Includes overflow button.
+ float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+ - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+ mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
}
/**
@@ -464,18 +474,7 @@ public class ExpandedAnimationController
@Override
void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
- final Resources res = layout.getResources();
- mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
- mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
- mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
- mStatusBarHeight =
- res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
-
- // Includes overflow button.
- float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
- - (mBubblesMaxRendered + 1) * mBubbleSizePx;
- mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
+ updateResources(mScreenOrientation, mDisplaySize);
// Ensure that all child views are at 1x scale, and visible, in case they were animating
// in.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 496b21b612fe..4884781c64de 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -79,6 +79,7 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var structurePager: ViewPager2
private lateinit var statusText: TextView
private lateinit var titleView: TextView
+ private lateinit var subtitleView: TextView
private lateinit var pageIndicator: ManagementPageIndicator
private var mTooltipManager: TooltipManager? = null
private lateinit var doneButton: View
@@ -165,7 +166,12 @@ class ControlsFavoritingActivity @Inject constructor(
structurePager.adapter = StructureAdapter(listOfStructures)
structurePager.setCurrentItem(structureIndex)
if (error) {
- statusText.text = resources.getText(R.string.controls_favorite_load_error)
+ statusText.text = resources.getString(R.string.controls_favorite_load_error,
+ appName ?: "")
+ subtitleView.visibility = View.GONE
+ } else if (listOfStructures.isEmpty()) {
+ statusText.text = resources.getString(R.string.controls_favorite_load_none)
+ subtitleView.visibility = View.GONE
} else {
statusText.visibility = View.GONE
}
@@ -266,8 +272,9 @@ class ControlsFavoritingActivity @Inject constructor(
titleView = requireViewById<TextView>(R.id.title).apply {
text = title
}
- requireViewById<TextView>(R.id.subtitle).text =
- resources.getText(R.string.controls_favorite_subtitle)
+ subtitleView = requireViewById<TextView>(R.id.subtitle).apply {
+ text = resources.getText(R.string.controls_favorite_subtitle)
+ }
structurePager = requireViewById<ViewPager2>(R.id.structure_pager)
structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 0f5aef7eeec1..1eb7e2168a6a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -103,6 +103,22 @@ class ControlsUiControllerImpl @Inject constructor (
private lateinit var dismissGlobalActions: Runnable
private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow)
+ private val collator = Collator.getInstance(context.resources.configuration.locales[0])
+ private val localeComparator = compareBy<SelectionItem, CharSequence>(collator) {
+ it.getTitle()
+ }
+
+ private val onSeedingComplete = Consumer<Boolean> {
+ accepted ->
+ if (accepted) {
+ selectedStructure = controlsController.get().getFavorites().maxBy {
+ it.controls.size
+ } ?: EMPTY_STRUCTURE
+ updatePreferences(selectedStructure)
+ }
+ reload(parent)
+ }
+
override val available: Boolean
get() = controlsController.get().available
@@ -113,22 +129,13 @@ class ControlsUiControllerImpl @Inject constructor (
): ControlsListingController.ControlsListingCallback {
return object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
- bgExecutor.execute {
- val collator = Collator.getInstance(context.resources.configuration.locales[0])
- val localeComparator = compareBy<ControlsServiceInfo, CharSequence>(collator) {
- it.loadLabel()
- }
-
- val mList = serviceInfos.toMutableList()
- mList.sortWith(localeComparator)
- lastItems = mList.map {
- SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
- }
- uiExecutor.execute {
- parent.removeAllViews()
- if (lastItems.size > 0) {
- onResult(lastItems)
- }
+ val lastItems = serviceInfos.map {
+ SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
+ }
+ uiExecutor.execute {
+ parent.removeAllViews()
+ if (lastItems.size > 0) {
+ onResult(lastItems)
}
}
}
@@ -144,8 +151,7 @@ class ControlsUiControllerImpl @Inject constructor (
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
- val cb = Consumer<Boolean> { _ -> reload(parent) }
- if (controlsController.get().addSeedingFavoritesCallback(cb)) {
+ if (controlsController.get().addSeedingFavoritesCallback(onSeedingComplete)) {
listingCallback = createCallback(::showSeedingView)
} else if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
// only show initial view if there are really no favorites across any structure
@@ -309,9 +315,12 @@ class ControlsUiControllerImpl @Inject constructor (
}
val itemsByComponent = items.associateBy { it.componentName }
- val itemsWithStructure = allStructures.mapNotNull {
+ val itemsWithStructure = mutableListOf<SelectionItem>()
+ allStructures.mapNotNullTo(itemsWithStructure) {
itemsByComponent.get(it.componentName)?.copy(structure = it.structure)
}
+ itemsWithStructure.sortWith(localeComparator)
+
val selectionItem = findSelectionItem(selectedStructure, itemsWithStructure) ?: items[0]
var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply {
@@ -441,6 +450,7 @@ class ControlsUiControllerImpl @Inject constructor (
}
private fun updatePreferences(si: StructureInfo) {
+ if (si == EMPTY_STRUCTURE) return
sharedPreferences.edit()
.putString(PREF_COMPONENT, si.componentName.flattenToString())
.putString(PREF_STRUCTURE, si.structure.toString())
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d5a28378c993..2df0507a8864 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -96,13 +96,13 @@ public class MediaControlPanel {
*/
@Inject
public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
- ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager,
+ ActivityStarter activityStarter, MediaViewController mediaViewController,
SeekBarViewModel seekBarViewModel) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
mSeekBarViewModel = seekBarViewModel;
- mMediaViewController = new MediaViewController(context, mediaHostStatesManager);
+ mMediaViewController = mediaViewController;
loadDimens();
}
@@ -366,14 +366,6 @@ public class MediaControlPanel {
}
/**
- * Return the token for the current media session
- * @return the token
- */
- public MediaSession.Token getMediaSessionToken() {
- return mToken;
- }
-
- /**
* Get the current media controller
* @return the controller
*/
@@ -382,25 +374,6 @@ public class MediaControlPanel {
}
/**
- * Get the name of the package associated with the current media controller
- * @return the package name, or null if no controller
- */
- public String getMediaPlayerPackage() {
- if (mController == null) {
- return null;
- }
- return mController.getPackageName();
- }
-
- /**
- * Check whether this player has an attached media session.
- * @return whether there is a controller with a current media session.
- */
- public boolean hasMediaSession() {
- return mController != null && mController.getPlaybackState() != null;
- }
-
- /**
* Check whether the media controlled by this player is currently playing
* @return whether it is playing, or false if no controller information
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 5d28178a3b1b..0b0ffcede3af 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -25,19 +25,65 @@ import android.media.session.MediaSession
data class MediaData(
val initialized: Boolean = false,
val backgroundColor: Int,
+ /**
+ * App name that will be displayed on the player.
+ */
val app: String?,
+ /**
+ * Icon shown on player, close to app name.
+ */
val appIcon: Drawable?,
+ /**
+ * Artist name.
+ */
val artist: CharSequence?,
+ /**
+ * Song name.
+ */
val song: CharSequence?,
+ /**
+ * Album artwork.
+ */
val artwork: Icon?,
+ /**
+ * List of actions that can be performed on the player: prev, next, play, pause, etc.
+ */
val actions: List<MediaAction>,
+ /**
+ * Same as above, but shown on smaller versions of the player, like in QQS or keyguard.
+ */
val actionsToShowInCompact: List<Int>,
+ /**
+ * Package name of the app that's posting the media.
+ */
val packageName: String,
+ /**
+ * Unique media session identifier.
+ */
val token: MediaSession.Token?,
+ /**
+ * Action to perform when the player is tapped.
+ * This is unrelated to {@link #actions}.
+ */
val clickIntent: PendingIntent?,
+ /**
+ * Where the media is playing: phone, headphones, ear buds, remote session.
+ */
val device: MediaDeviceData?,
+ /**
+ * When active, a player will be displayed on keyguard and quick-quick settings.
+ * This is unrelated to the stream being playing or not, a player will not be active if
+ * timed out, or in resumption mode.
+ */
+ var active: Boolean,
+ /**
+ * Action that should be performed to restart a non active session.
+ */
var resumeAction: Runnable?,
- val notificationKey: String = "INVALID",
+ /**
+ * Notification key for cancelling a media player after a timeout (when not using resumption.)
+ */
+ val notificationKey: String? = null,
var hasCheckedForResume: Boolean = false
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5fe39958fd67..5300795189de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -67,7 +67,7 @@ private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
private val LOADING = MediaData(false, 0, null, null, null, null, null,
- emptyList(), emptyList(), "INVALID", null, null, null, null)
+ emptyList(), emptyList(), "INVALID", null, null, null, true, null)
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
if (!sbn.notification.hasMediaSession()) {
@@ -88,12 +88,12 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean {
class MediaDataManager @Inject constructor(
private val context: Context,
private val mediaControllerFactory: MediaControllerFactory,
- private val mediaTimeoutListener: MediaTimeoutListener,
private val notificationEntryManager: NotificationEntryManager,
- private val mediaResumeListener: MediaResumeListener,
@Background private val backgroundExecutor: Executor,
@Main private val foregroundExecutor: Executor,
- private val broadcastDispatcher: BroadcastDispatcher
+ broadcastDispatcher: BroadcastDispatcher,
+ mediaTimeoutListener: MediaTimeoutListener,
+ mediaResumeListener: MediaResumeListener
) {
private val listeners: MutableSet<Listener> = mutableSetOf()
@@ -131,7 +131,6 @@ class MediaDataManager @Inject constructor(
mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
setTimedOut(token, timedOut) }
addListener(mediaTimeoutListener)
-
if (useMediaResumption) {
mediaResumeListener.addTrackToResumeCallback = { desc: MediaDescription,
resumeAction: Runnable, token: MediaSession.Token, appName: String,
@@ -215,7 +214,7 @@ class MediaDataManager @Inject constructor(
mediaEntries.put(packageName, resumeData)
}
backgroundExecutor.execute {
- loadMediaDataInBg(desc, action, token, appName, appIntent, packageName)
+ loadMediaDataInBgForResumption(desc, action, token, appName, appIntent, packageName)
}
}
@@ -255,16 +254,21 @@ class MediaDataManager @Inject constructor(
fun removeListener(listener: Listener) = listeners.remove(listener)
private fun setTimedOut(token: String, timedOut: Boolean) {
- if (!timedOut) {
- return
- }
mediaEntries[token]?.let {
- notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */,
- UNDEFINED_DISMISS_REASON)
+ if (Utils.useMediaResumption(context)) {
+ if (it.active == !timedOut) {
+ return
+ }
+ it.active = !timedOut
+ onMediaDataLoaded(token, token, it)
+ } else if (timedOut) {
+ notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */,
+ UNDEFINED_DISMISS_REASON)
+ }
}
}
- private fun loadMediaDataInBg(
+ private fun loadMediaDataInBgForResumption(
desc: MediaDescription,
resumeAction: Runnable,
token: MediaSession.Token,
@@ -272,11 +276,6 @@ class MediaDataManager @Inject constructor(
appIntent: PendingIntent,
packageName: String
) {
- if (resumeAction == null) {
- Log.e(TAG, "Resume action cannot be null")
- return
- }
-
if (TextUtils.isEmpty(desc.title)) {
Log.e(TAG, "Description incomplete")
return
@@ -298,8 +297,9 @@ class MediaDataManager @Inject constructor(
val mediaAction = getResumeMediaAction(resumeAction)
foregroundExecutor.execute {
onMediaDataLoaded(packageName, null, MediaData(true, Color.DKGRAY, appName,
- null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
- packageName, token, appIntent, null, resumeAction, packageName))
+ null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
+ packageName, token, appIntent, device = null, active = false,
+ resumeAction = resumeAction))
}
}
@@ -430,7 +430,8 @@ class MediaDataManager @Inject constructor(
foregroundExecutor.execute {
onMediaDataLoaded(key, oldKey, MediaData(true, bgColor, app, smallIconDrawable, artist,
song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
- notif.contentIntent, null, resumeAction, key))
+ notif.contentIntent, null, active = true, resumeAction = resumeAction,
+ notificationKey = key))
}
}
@@ -528,13 +529,13 @@ class MediaDataManager @Inject constructor(
/**
* Are there any media notifications active?
*/
- fun hasActiveMedia() = mediaEntries.any({ isActive(it.value) })
+ fun hasActiveMedia() = mediaEntries.any { it.value.active }
- fun isActive(data: MediaData): Boolean {
- if (data.token == null) {
+ fun isActive(token: MediaSession.Token?): Boolean {
+ if (token == null) {
return false
}
- val controller = mediaControllerFactory.create(data.token)
+ val controller = mediaControllerFactory.create(token)
val state = controller?.playbackState?.state
return state != null && NotificationMediaManager.isActiveState(state)
}
@@ -542,7 +543,7 @@ class MediaDataManager @Inject constructor(
/**
* Are there any media entries, including resume controls?
*/
- fun hasAnyMedia() = mediaEntries.isNotEmpty()
+ fun hasAnyMedia() = if (useMediaResumption) mediaEntries.isNotEmpty() else hasActiveMedia()
interface Listener {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index de0af16bc2fa..7ae2dc5c0941 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -22,6 +22,10 @@ import android.media.session.MediaController
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import java.io.FileDescriptor
+import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@@ -35,13 +39,15 @@ class MediaDeviceManager @Inject constructor(
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor,
- private val mediaDataManager: MediaDataManager
-) : MediaDataManager.Listener {
+ private val mediaDataManager: MediaDataManager,
+ private val dumpManager: DumpManager
+) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
private val entries: MutableMap<String, Token> = mutableMapOf()
init {
mediaDataManager.addListener(this)
+ dumpManager.registerDumpable(javaClass.name, this)
}
/**
@@ -81,6 +87,17 @@ class MediaDeviceManager @Inject constructor(
}
}
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+ with(pw) {
+ println("MediaDeviceManager state:")
+ entries.forEach {
+ key, entry ->
+ println(" key=$key")
+ entry.dump(fd, pw, args)
+ }
+ }
+ }
+
private fun processDevice(key: String, device: MediaDevice?) {
val enabled = device != null
val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
@@ -122,6 +139,17 @@ class MediaDeviceManager @Inject constructor(
localMediaManager.stopScan()
localMediaManager.unregisterCallback(this)
}
+ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+ val route = controller?.let {
+ mr2manager.getRoutingSessionForMediaController(it)
+ }
+ with(pw) {
+ println(" current device is ${current?.name}")
+ val type = controller?.playbackInfo?.playbackType
+ println(" PlaybackType=$type (1 for local, 2 for remote)")
+ println(" route=$route")
+ }
+ }
override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute {
updateCurrent()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 2bd8c0cbeab2..7c5f0d1c2a16 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.graphics.PointF
import android.graphics.Rect
import android.view.View
import android.view.View.OnAttachStateChangeListener
@@ -20,8 +21,6 @@ class MediaHost @Inject constructor(
var location: Int = -1
private set
var visibleChangedListener: ((Boolean) -> Unit)? = null
- var visible: Boolean = false
- private set
private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0)
@@ -109,16 +108,17 @@ class MediaHost @Inject constructor(
}
private fun updateViewVisibility() {
- if (showsOnlyActiveMedia) {
- visible = mediaDataManager.hasActiveMedia()
+ visible = if (showsOnlyActiveMedia) {
+ mediaDataManager.hasActiveMedia()
} else {
- visible = mediaDataManager.hasAnyMedia()
+ mediaDataManager.hasAnyMedia()
}
hostView.visibility = if (visible) View.VISIBLE else View.GONE
visibleChangedListener?.invoke(visible)
}
class MediaHostStateHolder @Inject constructor() : MediaHostState {
+ private var gonePivot: PointF = PointF()
override var measurementInput: MeasurementInput? = null
set(value) {
@@ -144,6 +144,25 @@ class MediaHost @Inject constructor(
}
}
+ override var visible: Boolean = true
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ changedListener?.invoke()
+ }
+
+ override fun getPivotX(): Float = gonePivot.x
+ override fun getPivotY(): Float = gonePivot.y
+ override fun setGonePivot(x: Float, y: Float) {
+ if (gonePivot.equals(x, y)) {
+ return
+ }
+ gonePivot.set(x, y)
+ changedListener?.invoke()
+ }
+
/**
* A listener for all changes. This won't be copied over when invoking [copy]
*/
@@ -157,6 +176,8 @@ class MediaHost @Inject constructor(
mediaHostState.expansion = expansion
mediaHostState.showsOnlyActiveMedia = showsOnlyActiveMedia
mediaHostState.measurementInput = measurementInput?.copy()
+ mediaHostState.visible = visible
+ mediaHostState.gonePivot.set(gonePivot)
return mediaHostState
}
@@ -173,6 +194,12 @@ class MediaHost @Inject constructor(
if (showsOnlyActiveMedia != other.showsOnlyActiveMedia) {
return false
}
+ if (visible != other.visible) {
+ return false
+ }
+ if (!gonePivot.equals(other.getPivotX(), other.getPivotY())) {
+ return false
+ }
return true
}
@@ -180,6 +207,8 @@ class MediaHost @Inject constructor(
var result = measurementInput?.hashCode() ?: 0
result = 31 * result + expansion.hashCode()
result = 31 * result + showsOnlyActiveMedia.hashCode()
+ result = 31 * result + if (visible) 1 else 2
+ result = 31 * result + gonePivot.hashCode()
return result
}
}
@@ -194,7 +223,8 @@ interface MediaHostState {
var measurementInput: MeasurementInput?
/**
- * The expansion of the player, 0 for fully collapsed, 1 for fully expanded
+ * The expansion of the player, 0 for fully collapsed (up to 3 actions), 1 for fully expanded
+ * (up to 5 actions.)
*/
var expansion: Float
@@ -204,6 +234,30 @@ interface MediaHostState {
var showsOnlyActiveMedia: Boolean
/**
+ * If the view should be VISIBLE or GONE.
+ */
+ var visible: Boolean
+
+ /**
+ * Sets the pivot point when clipping the height or width.
+ * Clipping happens when animating visibility when we're visible in QS but not on QQS,
+ * for example.
+ */
+ fun setGonePivot(x: Float, y: Float)
+
+ /**
+ * x position of pivot, from 0 to 1
+ * @see [setGonePivot]
+ */
+ fun getPivotX(): Float
+
+ /**
+ * y position of pivot, from 0 to 1
+ * @see [setGonePivot]
+ */
+ fun getPivotY(): Float
+
+ /**
* Get a copy of this view state, deepcopying all appropriate members
*/
fun copy(): MediaHostState
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 3c3f4a977ee7..cf8a636a2b67 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -43,6 +43,11 @@ class MediaTimeoutListener @Inject constructor(
private val mediaListeners: MutableMap<String, PlaybackStateListener> = mutableMapOf()
+ /**
+ * Callback representing that a media object is now expired:
+ * @param token Media session unique identifier
+ * @param pauseTimeuot True when expired for {@code PAUSED_MEDIA_TIMEOUT}
+ */
lateinit var timeoutCallback: (String, Boolean) -> Unit
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
@@ -77,6 +82,7 @@ class MediaTimeoutListener @Inject constructor(
init {
mediaController?.registerCallback(this)
+ onPlaybackStateChanged(mediaController?.playbackState)
}
fun destroy() {
@@ -112,11 +118,11 @@ class MediaTimeoutListener @Inject constructor(
}
}
- private fun expireMediaTimeout(mediaNotificationKey: String, reason: String) {
+ private fun expireMediaTimeout(mediaKey: String, reason: String) {
cancellation?.apply {
if (DEBUG) {
Log.v(TAG,
- "media timeout cancelled for $mediaNotificationKey, reason: $reason")
+ "media timeout cancelled for $mediaKey, reason: $reason")
}
run()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index e82bb402407e..90ccfc6ca725 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -17,20 +17,22 @@
package com.android.systemui.media
import android.content.Context
+import android.graphics.PointF
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
+import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionLayoutController
import com.android.systemui.util.animation.TransitionViewState
-import com.android.systemui.util.animation.MeasurementOutput
+import javax.inject.Inject
/**
* A class responsible for controlling a single instance of a media player handling interactions
* with the view instance and keeping the media view states up to date.
*/
-class MediaViewController(
+class MediaViewController @Inject constructor(
context: Context,
- val mediaHostStatesManager: MediaHostStatesManager
+ private val mediaHostStatesManager: MediaHostStatesManager
) {
private var firstRefresh: Boolean = true
@@ -44,7 +46,7 @@ class MediaViewController(
/**
* A map containing all viewStates for all locations of this mediaState
*/
- private val mViewStates: MutableMap<MediaHostState, TransitionViewState?> = mutableMapOf()
+ private val viewStates: MutableMap<MediaHostState, TransitionViewState?> = mutableMapOf()
/**
* The ending location of the view where it ends when all animations and transitions have
@@ -69,6 +71,11 @@ class MediaViewController(
private val tmpState = TransitionViewState()
/**
+ * Temporary variable to avoid unnecessary allocations.
+ */
+ private val tmpPoint = PointF()
+
+ /**
* A callback for media state changes
*/
val stateCallback = object : MediaHostStatesManager.Callback {
@@ -125,7 +132,7 @@ class MediaViewController(
* it's not available, it will recreate one by measuring, which may be expensive.
*/
private fun obtainViewState(state: MediaHostState): TransitionViewState? {
- val viewState = mViewStates[state]
+ val viewState = viewStates[state]
if (viewState != null) {
// we already have cached this measurement, let's continue
return viewState
@@ -143,7 +150,7 @@ class MediaViewController(
// We don't want to cache interpolated or null states as this could quickly fill up
// our cache. We only cache the start and the end states since the interpolation
// is cheap
- mViewStates[state.copy()] = result
+ viewStates[state.copy()] = result
} else {
// This is an interpolated state
val startState = state.copy().also { it.expansion = 0.0f }
@@ -153,11 +160,13 @@ class MediaViewController(
val startViewState = obtainViewState(startState) as TransitionViewState
val endState = state.copy().also { it.expansion = 1.0f }
val endViewState = obtainViewState(endState) as TransitionViewState
+ tmpPoint.set(startState.getPivotX(), startState.getPivotY())
result = TransitionViewState()
layoutController.getInterpolatedState(
startViewState,
endViewState,
state.expansion,
+ tmpPoint,
result)
}
} else {
@@ -213,11 +222,35 @@ class MediaViewController(
val shouldAnimate = animateNextStateChange && !applyImmediately
+ var startHostState = mediaHostStatesManager.mediaHostStates[startLocation]
+ var endHostState = mediaHostStatesManager.mediaHostStates[endLocation]
+ var swappedStartState = false
+ var swappedEndState = false
+
+ // if we're going from or to a non visible state, let's grab the visible one and animate
+ // the view being clipped instead.
+ if (endHostState?.visible != true) {
+ endHostState = startHostState
+ swappedEndState = true
+ }
+ if (startHostState?.visible != true) {
+ startHostState = endHostState
+ swappedStartState = true
+ }
+ if (startHostState == null || endHostState == null) {
+ return
+ }
+
+ var endViewState = obtainViewState(endHostState) ?: return
+ if (swappedEndState) {
+ endViewState = endViewState.copy()
+ endViewState.height = 0
+ }
+
// Obtain the view state that we'd want to be at the end
// The view might not be bound yet or has never been measured and in that case will be
// reset once the state is fully available
- val endState = obtainViewStateForLocation(endLocation) ?: return
- layoutController.setMeasureState(endState)
+ layoutController.setMeasureState(endViewState)
// If the view isn't bound, we can drop the animation, otherwise we'll executute it
animateNextStateChange = false
@@ -225,24 +258,43 @@ class MediaViewController(
return
}
- val startState = obtainViewStateForLocation(startLocation)
+ var startViewState = obtainViewState(startHostState)
+ if (swappedStartState) {
+ startViewState = startViewState?.copy()
+ startViewState?.height = 0
+ }
+
val result: TransitionViewState?
- if (transitionProgress == 1.0f || startState == null) {
- result = endState
+ result = if (transitionProgress == 1.0f || startViewState == null) {
+ endViewState
} else if (transitionProgress == 0.0f) {
- result = startState
+ startViewState
} else {
- layoutController.getInterpolatedState(startState, endState, transitionProgress,
- tmpState)
- result = tmpState
+ if (swappedEndState || swappedStartState) {
+ tmpPoint.set(startHostState.getPivotX(), startHostState.getPivotY())
+ } else {
+ tmpPoint.set(0.0f, 0.0f)
+ }
+ layoutController.getInterpolatedState(startViewState, endViewState, transitionProgress,
+ tmpPoint, tmpState)
+ tmpState
}
layoutController.setState(result, applyImmediately, shouldAnimate, animationDuration,
animationDelay)
}
- private fun obtainViewStateForLocation(location: Int): TransitionViewState? {
- val mediaState = mediaHostStatesManager.mediaHostStates[location] ?: return null
- return obtainViewState(mediaState)
+ /**
+ * Retrieves the [TransitionViewState] and [MediaHostState] of a [@MediaLocation].
+ * In the event of [location] not being visible, [locationWhenHidden] will be used instead.
+ *
+ * @param location Target
+ * @param locationWhenHidden Location that will be used when the target is not
+ * [MediaHost.visible]
+ * @return State require for executing a transition, and also the respective [MediaHost].
+ */
+ private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? {
+ val mediaHostState = mediaHostStatesManager.mediaHostStates[location] ?: return null
+ return obtainViewState(mediaHostState)
}
/**
@@ -250,8 +302,7 @@ class MediaViewController(
* This updates the width the view will me measured with.
*/
fun onLocationPreChange(@MediaLocation newLocation: Int) {
- val viewState = obtainViewStateForLocation(newLocation)
- viewState?.let {
+ obtainViewStateForLocation(newLocation)?.let {
layoutController.setMeasureState(it)
}
}
@@ -271,7 +322,7 @@ class MediaViewController(
fun refreshState() {
if (!firstRefresh) {
// Let's clear all of our measurements and recreate them!
- mViewStates.clear()
+ viewStates.clear()
setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress,
applyImmediately = false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
index 9b9a6b4b13ab..bccc3abd8a27 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
@@ -13,6 +13,7 @@ import androidx.core.view.GestureDetectorCompat
import com.android.systemui.R
import com.android.systemui.qs.PageIndicator
import com.android.systemui.statusbar.notification.VisualStabilityManager
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.animation.requiresRemeasuring
import javax.inject.Inject
@@ -31,7 +32,8 @@ class MediaViewManager @Inject constructor(
private val mediaControlPanelFactory: Provider<MediaControlPanel>,
private val visualStabilityManager: VisualStabilityManager,
private val mediaHostStatesManager: MediaHostStatesManager,
- mediaManager: MediaDataCombineLatest
+ mediaManager: MediaDataCombineLatest,
+ configurationController: ConfigurationController
) {
/**
@@ -74,6 +76,7 @@ class MediaViewManager @Inject constructor(
private val mediaCarousel: HorizontalScrollView
val mediaFrame: ViewGroup
val mediaPlayers: MutableMap<String, MediaControlPanel> = mutableMapOf()
+ private val mediaData: MutableMap<String, MediaData> = mutableMapOf()
private val mediaContent: ViewGroup
private val pageIndicator: PageIndicator
private val gestureDetector: GestureDetectorCompat
@@ -120,6 +123,11 @@ class MediaViewManager @Inject constructor(
return this@MediaViewManager.onTouch(view, motionEvent)
}
}
+ private val configListener = object : ConfigurationController.ConfigurationListener {
+ override fun onDensityOrFontScaleChanged() {
+ recreatePlayers()
+ }
+ }
init {
gestureDetector = GestureDetectorCompat(context, gestureListener)
@@ -130,6 +138,7 @@ class MediaViewManager @Inject constructor(
mediaCarousel.setOnTouchListener(touchListener)
mediaCarousel.setOverScrollMode(View.OVER_SCROLL_NEVER)
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
+ configurationController.addCallback(configListener)
visualStabilityCallback = VisualStabilityManager.Callback {
if (needsReordering) {
needsReordering = false
@@ -142,29 +151,14 @@ class MediaViewManager @Inject constructor(
true /* persistent */)
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- updateView(key, oldKey, data)
- updatePlayerVisibilities()
- mediaCarousel.requiresRemeasuring = true
+ oldKey?.let { mediaData.remove(it) }
+ mediaData.put(key, data)
+ addOrUpdatePlayer(key, oldKey, data)
}
override fun onMediaDataRemoved(key: String) {
- val removed = mediaPlayers.remove(key)
- removed?.apply {
- val beforeActive = mediaContent.indexOfChild(removed.view?.player) <=
- activeMediaIndex
- mediaContent.removeView(removed.view?.player)
- removed.onDestroy()
- updateMediaPaddings()
- if (beforeActive) {
- // also update the index here since the scroll below might not always lead
- // to a scrolling changed
- activeMediaIndex = Math.max(0, activeMediaIndex - 1)
- mediaCarousel.scrollX = Math.max(mediaCarousel.scrollX -
- playerWidthPlusPadding, 0)
- }
- updatePlayerVisibilities()
- updatePageIndicator()
- }
+ mediaData.remove(key)
+ removePlayer(key)
}
})
mediaHostStatesManager.addCallback(object : MediaHostStatesManager.Callback {
@@ -253,7 +247,7 @@ class MediaViewManager @Inject constructor(
}
}
- private fun updateView(key: String, oldKey: String?, data: MediaData) {
+ private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
// If the key was changed, update entry
val oldData = mediaPlayers[oldKey]
if (oldData != null) {
@@ -288,6 +282,39 @@ class MediaViewManager @Inject constructor(
existingPlayer?.bind(data)
updateMediaPaddings()
updatePageIndicator()
+ updatePlayerVisibilities()
+ mediaCarousel.requiresRemeasuring = true
+ }
+
+ private fun removePlayer(key: String) {
+ val removed = mediaPlayers.remove(key)
+ removed?.apply {
+ val beforeActive = mediaContent.indexOfChild(removed.view?.player) <=
+ activeMediaIndex
+ mediaContent.removeView(removed.view?.player)
+ removed.onDestroy()
+ updateMediaPaddings()
+ if (beforeActive) {
+ // also update the index here since the scroll below might not always lead
+ // to a scrolling changed
+ activeMediaIndex = Math.max(0, activeMediaIndex - 1)
+ mediaCarousel.scrollX = Math.max(mediaCarousel.scrollX -
+ playerWidthPlusPadding, 0)
+ }
+ updatePlayerVisibilities()
+ updatePageIndicator()
+ }
+ }
+
+ private fun recreatePlayers() {
+ // Note that this will scramble the order of players. Actively playing sessions will, at
+ // least, still be put in the front. If we want to maintain order, then more work is
+ // needed.
+ mediaData.forEach {
+ key, data ->
+ removePlayer(key)
+ addOrUpdatePlayer(key = key, oldKey = null, data = data)
+ }
}
private fun updateMediaPaddings() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index b93e07e65c73..9daa876038d5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -708,6 +708,12 @@ public class PipTaskOrganizer extends TaskOrganizer implements
Log.w(TAG, "Abort animation, invalid leash");
return;
}
+
+ if (startBounds.isEmpty() || destinationBounds.isEmpty()) {
+ Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting.");
+ return;
+ }
+
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
tx.apply();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 856c19290af6..06c98d00cca7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
import android.util.Log;
@@ -38,6 +39,9 @@ import com.android.systemui.util.magnetictarget.MagnetizedObject;
import java.io.PrintWriter;
import java.util.function.Consumer;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function0;
+
/**
* A helper to animate and manipulate the PiP.
*/
@@ -74,9 +78,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
new SfVsyncFrameCallbackProvider();
/**
- * Bounds that are animated using the physics animator.
+ * Temporary bounds used when PIP is being dragged or animated. These bounds are applied to PIP
+ * using {@link PipTaskOrganizer#scheduleUserResizePip}, so that we can animate shrinking into
+ * and expanding out of the magnetic dismiss target.
+ *
+ * Once PIP is done being dragged or animated, we set {@link #mBounds} equal to these temporary
+ * bounds, and call {@link PipTaskOrganizer#scheduleFinishResizePip} to 'officially' move PIP to
+ * its new bounds.
*/
- private final Rect mAnimatedBounds = new Rect();
+ private final Rect mTemporaryBounds = new Rect();
/** The destination bounds to which PIP is animating. */
private final Rect mAnimatingToBounds = new Rect();
@@ -85,20 +95,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private FloatingContentCoordinator mFloatingContentCoordinator;
/** Callback that re-sizes PIP to the animated bounds. */
- private final Choreographer.FrameCallback mResizePipVsyncCallback =
- l -> resizePipUnchecked(mAnimatedBounds);
+ private final Choreographer.FrameCallback mResizePipVsyncCallback;
/**
- * PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
+ * PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations.
*/
- private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
- mAnimatedBounds);
+ private PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
+ mTemporaryBounds);
+
+ private MagnetizedObject<Rect> mMagnetizedPip;
/**
- * Update listener that resizes the PIP to {@link #mAnimatedBounds}.
+ * Update listener that resizes the PIP to {@link #mTemporaryBounds}.
*/
- final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
- (target, values) -> mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
+ private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener;
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
private PhysicsAnimator.FlingConfig mFlingConfigX;
@@ -124,6 +134,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private boolean mSpringingToTouch = false;
/**
+ * Whether PIP was released in the dismiss target, and will be animated out and dismissed
+ * shortly.
+ */
+ private boolean mDismissalPending = false;
+
+ /**
* Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
* used to show menu activity when the expand animation is completed.
*/
@@ -155,6 +171,16 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
+
+ mResizePipVsyncCallback = l -> {
+ if (!mTemporaryBounds.isEmpty()) {
+ mPipTaskOrganizer.scheduleUserResizePip(
+ mBounds, mTemporaryBounds, null);
+ }
+ };
+
+ mResizePipUpdateListener = (target, values) ->
+ mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
}
@NonNull
@@ -186,19 +212,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
}
- /**
- * Synchronizes the current bounds with either the pinned stack, or the ongoing animation. This
- * is done to prepare for a touch gesture.
- */
- void synchronizePinnedStackBoundsForTouchGesture() {
- if (mAnimatingToBounds.isEmpty()) {
- // If we're not animating anywhere, sync normally.
- synchronizePinnedStackBounds();
- } else {
- // If we're animating, set the current bounds to the animated bounds. That way, the
- // touch gesture will begin at the most recent animated location of the bounds.
- mBounds.set(mAnimatedBounds);
- }
+ boolean isAnimating() {
+ return mTemporaryBoundsPhysicsAnimator.isRunning();
}
/**
@@ -224,32 +239,54 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
// If we are moving PIP directly to the touch event locations, cancel any animations and
// move PIP to the given bounds.
cancelAnimations();
- resizePipUnchecked(toBounds);
- mBounds.set(toBounds);
+
+ if (!isDragging) {
+ resizePipUnchecked(toBounds);
+ mBounds.set(toBounds);
+ } else {
+ mTemporaryBounds.set(toBounds);
+ mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, null);
+ }
} else {
// If PIP is 'catching up' after being stuck in the dismiss target, update the animation
// to spring towards the new touch location.
- mAnimatedBoundsPhysicsAnimator
+ mTemporaryBoundsPhysicsAnimator
+ .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
+ .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
- .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig)
- .withEndActions(() -> mSpringingToTouch = false);
+ .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */,
false /* dismiss */);
}
}
- /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
- void setSpringingToTouch(boolean springingToTouch) {
- if (springingToTouch) {
- mAnimatedBounds.set(mBounds);
- }
+ /** Animates the PIP into the dismiss target, scaling it down. */
+ void animateIntoDismissTarget(
+ MagnetizedObject.MagneticTarget target,
+ float velX, float velY,
+ boolean flung, Function0<Unit> after) {
+ final PointF targetCenter = target.getCenterOnScreen();
- mSpringingToTouch = springingToTouch;
+ final float desiredWidth = mBounds.width() / 2;
+ final float desiredHeight = mBounds.height() / 2;
+
+ final float destinationX = targetCenter.x - (desiredWidth / 2f);
+ final float destinationY = targetCenter.y - (desiredHeight / 2f);
+
+ mTemporaryBoundsPhysicsAnimator
+ .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
+ .spring(FloatProperties.RECT_Y, destinationY, velY, mSpringConfig)
+ .spring(FloatProperties.RECT_WIDTH, desiredWidth, mSpringConfig)
+ .spring(FloatProperties.RECT_HEIGHT, desiredHeight, mSpringConfig)
+ .withEndActions(after);
+
+ startBoundsAnimator(destinationX, destinationY, false);
}
- void prepareForAnimation() {
- mAnimatedBounds.set(mBounds);
+ /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
+ void setSpringingToTouch(boolean springingToTouch) {
+ mSpringingToTouch = springingToTouch;
}
/**
@@ -309,13 +346,22 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
/**
+ * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
+ * otherwise.
+ */
+ Rect getPossiblyAnimatingBounds() {
+ return mTemporaryBounds.isEmpty() ? mBounds : mTemporaryBounds;
+ }
+
+ /**
* Flings the PiP to the closest snap target.
*/
void flingToSnapTarget(
float velocityX, float velocityY,
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
- mAnimatedBounds.set(mBounds);
- mAnimatedBoundsPhysicsAnimator
+ mTemporaryBoundsPhysicsAnimator
+ .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
+ .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
.flingThenSpring(
FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
true /* flingMustReachMinOrMax */)
@@ -324,13 +370,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
.withEndActions(endAction);
if (updateAction != null) {
- mAnimatedBoundsPhysicsAnimator.addUpdateListener(
+ mTemporaryBoundsPhysicsAnimator.addUpdateListener(
(target, values) -> updateAction.run());
}
final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
final float estimatedFlingYEndValue =
- PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
+ PhysicsAnimator.estimateFlingEndValue(
+ mTemporaryBounds.top, velocityY, mFlingConfigY);
startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */,
false /* dismiss */);
@@ -341,8 +388,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* configuration
*/
void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
- mAnimatedBounds.set(mBounds);
- mAnimatedBoundsPhysicsAnimator
+ if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
+ // Animate from the current bounds if we're not already animating.
+ mTemporaryBounds.set(mBounds);
+ }
+
+ mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_X, bounds.left, springConfig)
.spring(FloatProperties.RECT_Y, bounds.top, springConfig);
startBoundsAnimator(bounds.left /* toX */, bounds.top /* toY */,
@@ -353,18 +404,19 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* Animates the dismissal of the PiP off the edge of the screen.
*/
void animateDismiss() {
- mAnimatedBounds.set(mBounds);
-
// Animate off the bottom of the screen, then dismiss PIP.
- mAnimatedBoundsPhysicsAnimator
+ mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_Y,
- mBounds.bottom + mBounds.height(),
+ mMovementBounds.bottom + mBounds.height() * 2,
0,
mSpringConfig)
.withEndActions(this::dismissPip);
- startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */,
+ startBoundsAnimator(
+ mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */,
true /* dismiss */);
+
+ mDismissalPending = false;
}
/**
@@ -415,7 +467,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* Cancels all existing animations.
*/
private void cancelAnimations() {
- mAnimatedBoundsPhysicsAnimator.cancel();
+ mTemporaryBoundsPhysicsAnimator.cancel();
mAnimatingToBounds.setEmpty();
mSpringingToTouch = false;
}
@@ -449,15 +501,36 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
(int) toY + mBounds.height());
setAnimatingToBounds(mAnimatingToBounds);
- mAnimatedBoundsPhysicsAnimator
- .withEndActions(() -> {
- if (!dismiss) {
- mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds);
- }
- mAnimatingToBounds.setEmpty();
- })
- .addUpdateListener(mResizePipUpdateListener)
- .start();
+ if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
+ mTemporaryBoundsPhysicsAnimator
+ .addUpdateListener(mResizePipUpdateListener)
+ .withEndActions(this::onBoundsAnimationEnd);
+ }
+
+ mTemporaryBoundsPhysicsAnimator.start();
+ }
+
+ /**
+ * Notify that PIP was released in the dismiss target and will be animated out and dismissed
+ * shortly.
+ */
+ void notifyDismissalPending() {
+ mDismissalPending = true;
+ }
+
+ private void onBoundsAnimationEnd() {
+ if (!mDismissalPending
+ && !mSpringingToTouch
+ && !mMagnetizedPip.getObjectStuckToTarget()) {
+ mBounds.set(mTemporaryBounds);
+ mPipTaskOrganizer.scheduleFinishResizePip(mBounds);
+
+ mTemporaryBounds.setEmpty();
+ }
+
+ mAnimatingToBounds.setEmpty();
+ mSpringingToTouch = false;
+ mDismissalPending = false;
}
/**
@@ -503,25 +576,29 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* magnetic dismiss target so it can calculate PIP's size and position.
*/
MagnetizedObject<Rect> getMagnetizedPip() {
- return new MagnetizedObject<Rect>(
- mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
- @Override
- public float getWidth(@NonNull Rect animatedPipBounds) {
- return animatedPipBounds.width();
- }
-
- @Override
- public float getHeight(@NonNull Rect animatedPipBounds) {
- return animatedPipBounds.height();
- }
+ if (mMagnetizedPip == null) {
+ mMagnetizedPip = new MagnetizedObject<Rect>(
+ mContext, mTemporaryBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
+ @Override
+ public float getWidth(@NonNull Rect animatedPipBounds) {
+ return animatedPipBounds.width();
+ }
+
+ @Override
+ public float getHeight(@NonNull Rect animatedPipBounds) {
+ return animatedPipBounds.height();
+ }
+
+ @Override
+ public void getLocationOnScreen(
+ @NonNull Rect animatedPipBounds, @NonNull int[] loc) {
+ loc[0] = animatedPipBounds.left;
+ loc[1] = animatedPipBounds.top;
+ }
+ };
+ }
- @Override
- public void getLocationOnScreen(
- @NonNull Rect animatedPipBounds, @NonNull int[] loc) {
- loc[0] = animatedPipBounds.left;
- loc[1] = animatedPipBounds.top;
- }
- };
+ return mMagnetizedPip;
}
public void dump(PrintWriter pw, String prefix) {
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 3cc9127068bf..2f9b29d13744 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -70,6 +70,8 @@ import com.android.systemui.util.magnetictarget.MagnetizedObject;
import java.io.PrintWriter;
+import kotlin.Unit;
+
/**
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
* the PIP.
@@ -262,12 +264,14 @@ public class PipTouchHandler {
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
updateMagneticTargetSize();
- mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener);
+ mMagnetizedPip.setAnimateStuckToTarget(
+ (target, velX, velY, flung, after) -> {
+ mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+ return Unit.INSTANCE;
+ });
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
@Override
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mMotionHelper.prepareForAnimation();
-
// Show the dismiss target, in case the initial touch event occurred within the
// magnetic field radius.
showDismissTargetMaybe();
@@ -286,12 +290,13 @@ public class PipTouchHandler {
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ mMotionHelper.notifyDismissalPending();
+
mHandler.post(() -> {
mMotionHelper.animateDismiss();
hideDismissTarget();
});
-
MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
PipUtils.getTopPipActivity(mContext, mActivityManager));
}
@@ -617,11 +622,16 @@ public class PipTouchHandler {
}
MotionEvent ev = (MotionEvent) inputEvent;
-
- if (mPipResizeGestureHandler.isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+ if (!mTouchState.isDragging()
+ && !mMagnetizedPip.getObjectStuckToTarget()
+ && !mMotionHelper.isAnimating()
+ && mPipResizeGestureHandler.isWithinTouchRegion(
+ (int) ev.getRawX(), (int) ev.getRawY())) {
return true;
}
- if (mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
+
+ if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
+ && mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
// to the touch state. Touch state needs a DOWN event in order to later process MOVE
// events it'll receive if the object is dragged out of the magnetic field.
@@ -643,7 +653,6 @@ public class PipTouchHandler {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
- mMotionHelper.synchronizePinnedStackBoundsForTouchGesture();
mGesture.onDown(mTouchState);
break;
}
@@ -688,11 +697,11 @@ public class PipTouchHandler {
break;
}
case MotionEvent.ACTION_HOVER_EXIT: {
- mHideMenuAfterShown = true;
// If Touch Exploration is enabled, some a11y services (e.g. Talkback) is probably
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ mHideMenuAfterShown = true;
mMenuController.hideMenu();
}
if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
@@ -872,7 +881,7 @@ public class PipTouchHandler {
return;
}
- Rect bounds = mMotionHelper.getBounds();
+ Rect bounds = mMotionHelper.getPossiblyAnimatingBounds();
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
@@ -914,7 +923,7 @@ public class PipTouchHandler {
mDelta.x += left - lastX;
mDelta.y += top - lastY;
- mTmpBounds.set(mMotionHelper.getBounds());
+ mTmpBounds.set(mMotionHelper.getPossiblyAnimatingBounds());
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0fc3829fab66..5021e0019d57 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -235,6 +235,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
protected void initMediaHostState() {
mMediaHost.setExpansion(1.0f);
mMediaHost.setShowsOnlyActiveMedia(false);
+ // Reveal player with some parallax (1.0f would also work)
+ mMediaHost.setGonePivot(0.0f, 0.8f);
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index f2e8599536ca..87597263168a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -311,7 +311,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
this,
REQUEST_CODE,
viewIntent,
- Intent.FLAG_GRANT_READ_URI_PERMISSION))
+ PendingIntent.FLAG_IMMUTABLE))
.addAction(shareAction)
.addAction(deleteAction)
.setAutoCancel(true)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index ca3753286bf9..8c1e1dd0cac7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -69,6 +69,7 @@ import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.view.Display;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -374,6 +375,20 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
// Inflate the screenshot layout
mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
+ mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ dismissScreenshot("back pressed", true);
+ return true;
+ }
+ return false;
+ }
+ });
+ // Get focus so that the key events go to the layout.
+ mScreenshotLayout.setFocusableInTouchMode(true);
+ mScreenshotLayout.requestFocus();
+
mScreenshotAnimatedView =
mScreenshotLayout.findViewById(R.id.global_screenshot_animated_view);
mScreenshotAnimatedView.setClipToOutline(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 43b47232d27d..8c7e071e0d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,9 +16,15 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
@@ -43,6 +49,7 @@ import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -78,15 +85,19 @@ public class KeyguardIndicationController implements StateListener,
private static final float BOUNCE_ANIMATION_FINAL_Y = 0f;
private final Context mContext;
+ private final BroadcastDispatcher mBroadcastDispatcher;
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTextView;
+ private KeyguardIndicationTextView mDisclosure;
private final IBatteryStats mBatteryInfo;
private final SettableWakeLock mWakeLock;
private final DockManager mDockManager;
+ private final DevicePolicyManager mDevicePolicyManager;
+ private BroadcastReceiver mBroadcastReceiver;
private LockscreenLockIconController mLockIconController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -105,6 +116,7 @@ public class KeyguardIndicationController implements StateListener,
private int mChargingWattage;
private int mBatteryLevel;
private long mChargingTimeRemaining;
+ private float mDisclosureMaxAlpha;
private String mMessageToShowOnScreenOn;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -128,8 +140,12 @@ public class KeyguardIndicationController implements StateListener,
StatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DockManager dockManager,
+ BroadcastDispatcher broadcastDispatcher,
+ DevicePolicyManager devicePolicyManager,
IBatteryStats iBatteryStats) {
mContext = context;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mDevicePolicyManager = devicePolicyManager;
mKeyguardStateController = keyguardStateController;
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -151,7 +167,22 @@ public class KeyguardIndicationController implements StateListener,
mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
mInitialTextColorState = mTextView != null ?
mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
+ mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
+ mDisclosureMaxAlpha = mDisclosure.getAlpha();
updateIndication(false /* animate */);
+ updateDisclosure();
+
+ if (mBroadcastReceiver == null) {
+ // Update the disclosure proactively to avoid IPC on the critical path.
+ mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateDisclosure();
+ }
+ };
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, new IntentFilter(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+ }
}
public void setLockIconController(LockscreenLockIconController lockIconController) {
@@ -190,6 +221,23 @@ public class KeyguardIndicationController implements StateListener,
return mUpdateMonitorCallback;
}
+ private void updateDisclosure() {
+ // NOTE: Because this uses IPC, avoid calling updateDisclosure() on a critical path.
+ if (whitelistIpcs(mDevicePolicyManager::isDeviceManaged)) {
+ final CharSequence organizationName =
+ mDevicePolicyManager.getDeviceOwnerOrganizationName();
+ if (organizationName != null) {
+ mDisclosure.switchIndication(mContext.getResources().getString(
+ R.string.do_disclosure_with_name, organizationName));
+ } else {
+ mDisclosure.switchIndication(R.string.do_disclosure_generic);
+ }
+ mDisclosure.setVisibility(View.VISIBLE);
+ } else {
+ mDisclosure.setVisibility(View.GONE);
+ }
+ }
+
public void setVisible(boolean visible) {
mVisible = visible;
mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -574,6 +622,11 @@ public class KeyguardIndicationController implements StateListener,
}
@Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ mDisclosure.setAlpha((1 - linear) * mDisclosureMaxAlpha);
+ }
+
+ @Override
public void onUnlockedChanged() {
updateIndication(!mDozing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 5ee4693a32bf..e0532c3e2b28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -17,9 +17,11 @@
package com.android.systemui.statusbar.notification;
import android.content.res.Resources;
+import android.text.Layout;
import android.util.Pools;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.internal.widget.IMessagingLayout;
import com.android.internal.widget.MessagingGroup;
@@ -229,6 +231,15 @@ public class MessagingLayoutTransformState extends TransformState {
return result;
}
+ private boolean hasEllipses(TextView textView) {
+ Layout layout = textView.getLayout();
+ return layout != null && layout.getEllipsisCount(layout.getLineCount() - 1) > 0;
+ }
+
+ private boolean needsReflow(TextView own, TextView other) {
+ return hasEllipses(own) != hasEllipses(other);
+ }
+
/**
* Transform two groups towards each other.
*
@@ -238,13 +249,20 @@ public class MessagingLayoutTransformState extends TransformState {
float transformationAmount, boolean to) {
boolean useLinearTransformation =
otherGroup.getIsolatedMessage() == null && !mTransformInfo.isAnimating();
- transformView(transformationAmount, to, ownGroup.getSenderView(), otherGroup.getSenderView(),
- true /* sameAsAny */, useLinearTransformation);
+ TextView ownSenderView = ownGroup.getSenderView();
+ TextView otherSenderView = otherGroup.getSenderView();
+ transformView(transformationAmount, to, ownSenderView, otherSenderView,
+ // Normally this would be handled by the TextViewMessageState#sameAs check, but in
+ // this case it doesn't work because our text won't match, due to the appended colon
+ // in the collapsed view.
+ !needsReflow(ownSenderView, otherSenderView),
+ useLinearTransformation);
int totalAvatarTranslation = transformView(transformationAmount, to, ownGroup.getAvatar(),
otherGroup.getAvatar(), true /* sameAsAny */, useLinearTransformation);
List<MessagingMessage> ownMessages = ownGroup.getMessages();
List<MessagingMessage> otherMessages = otherGroup.getMessages();
float previousTranslation = 0;
+ boolean isLastView = true;
for (int i = 0; i < ownMessages.size(); i++) {
View child = ownMessages.get(ownMessages.size() - 1 - i).getView();
if (isGone(child)) {
@@ -278,6 +296,9 @@ public class MessagingLayoutTransformState extends TransformState {
mMessagingLayout.setMessagingClippingDisabled(true);
}
if (otherChild == null) {
+ if (isLastView) {
+ previousTranslation = ownSenderView.getTranslationY();
+ }
child.setTranslationY(previousTranslation);
setClippingDeactivated(child, true);
} else if (ownGroup.getIsolatedMessage() == child || otherIsIsolated) {
@@ -287,6 +308,7 @@ public class MessagingLayoutTransformState extends TransformState {
} else {
previousTranslation = child.getTranslationY();
}
+ isLastView = false;
}
ownGroup.updateClipRect();
return totalAvatarTranslation;
@@ -382,6 +404,9 @@ public class MessagingLayoutTransformState extends TransformState {
if (view.getParent() == null) {
return true;
}
+ if (view.getWidth() == 0) {
+ return true;
+ }
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof MessagingLinearLayout.LayoutParams
&& ((MessagingLinearLayout.LayoutParams) lp).hide) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index ae7867d68af4..b47c59acb82d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -125,6 +125,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
private ViewGroup mIndicationArea;
+ private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
private ViewGroup mPreviewContainer;
private ViewGroup mOverlayContainer;
@@ -238,6 +239,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
+ mEnterpriseDisclosure = findViewById(
+ R.id.keyguard_indication_enterprise_disclosure);
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom);
@@ -315,6 +318,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
// Respect font size setting.
+ mEnterpriseDisclosure.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.text_size_small_material));
mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(
com.android.internal.R.dimen.text_size_small_material));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 83d398d3e7ae..0d6597f1b11b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -39,7 +39,7 @@ import javax.inject.Singleton;
public class LockscreenGestureLogger {
/**
- * Contains Lockscreen related Westworld UiEvent enums.
+ * Contains Lockscreen related statsd UiEvent enums.
*/
public enum LockscreenUiEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "Lockscreen > Pull shade open")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index bc94cdeba37f..81d0699a29e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -460,6 +460,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
@Override
+ public void onStartedWakingUp() {
+ mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ .setAnimationsDisabled(false);
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ .setAnimationsDisabled(true);
+ }
+
+ @Override
public void onFinishedGoingToSleep() {
mBouncer.onScreenTurnedOff();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index c929243085d6..2d8784dc41bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -28,7 +28,7 @@ import android.graphics.PixelFormat;
import android.graphics.RecordingCanvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.util.Log;
+import android.os.Trace;
import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewConfiguration;
@@ -74,6 +74,11 @@ public class KeyButtonRipple extends Drawable {
private final HashSet<Animator> mRunningAnimations = new HashSet<>();
private final ArrayList<Animator> mTmpArray = new ArrayList<>();
+ private final TraceAnimatorListener mExitHwTraceAnimator =
+ new TraceAnimatorListener("exitHardware");
+ private final TraceAnimatorListener mEnterHwTraceAnimator =
+ new TraceAnimatorListener("enterHardware");
+
public enum Type {
OVAL,
ROUNDED_RECT
@@ -221,7 +226,7 @@ public class KeyButtonRipple extends Drawable {
@Override
public void jumpToCurrentState() {
- cancelAnimations("jumpToCurrentState");
+ endAnimations("jumpToCurrentState", false /* cancel */);
}
@Override
@@ -235,7 +240,6 @@ public class KeyButtonRipple extends Drawable {
}
public void setPressed(boolean pressed) {
- Log.d("b/63783866", "KeyButtonRipple.setPressed: pressed=" + pressed);
if (mDark != mLastDark && pressed) {
mRipplePaint = null;
mLastDark = mDark;
@@ -255,14 +259,19 @@ public class KeyButtonRipple extends Drawable {
mHandler.removeCallbacksAndMessages(null);
}
- private void cancelAnimations(String reason) {
- Log.d("b/63783866", "KeyButtonRipple.cancelAnimations: reason=" + reason);
+ private void endAnimations(String reason, boolean cancel) {
+ Trace.beginSection("KeyButtonRipple.endAnim: reason=" + reason + " cancel=" + cancel);
+ Trace.endSection();
mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
for (int i = 0; i < size; i++) {
Animator a = mTmpArray.get(i);
- a.cancel();
+ if (cancel) {
+ a.cancel();
+ } else {
+ a.end();
+ }
}
mTmpArray.clear();
mRunningAnimations.clear();
@@ -287,7 +296,7 @@ public class KeyButtonRipple extends Drawable {
}
private void enterSoftware() {
- cancelAnimations("enterSoftware");
+ endAnimations("enterSoftware", true /* cancel */);
mVisible = true;
mGlowAlpha = getMaxGlowAlpha();
ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
@@ -373,8 +382,7 @@ public class KeyButtonRipple extends Drawable {
}
private void enterHardware() {
- Log.d("b/63783866", "enterHardware");
- cancelAnimations("enterHardware");
+ endAnimations("enterHardware", true /* cancel */);
mVisible = true;
mDrawingHardwareGlow = true;
setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
@@ -391,6 +399,7 @@ public class KeyButtonRipple extends Drawable {
endAnim.setDuration(ANIMATION_DURATION_SCALE);
endAnim.setInterpolator(mInterpolator);
endAnim.addListener(mAnimatorListener);
+ endAnim.addListener(mEnterHwTraceAnimator);
endAnim.setTarget(mTargetView);
if (isHorizontal()) {
@@ -426,13 +435,13 @@ public class KeyButtonRipple extends Drawable {
}
private void exitHardware() {
- Log.d("b/63783866", "exitHardware");
mPaintProp = CanvasProperty.createPaint(getRipplePaint());
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
RenderNodeAnimator.PAINT_ALPHA, 0);
opacityAnim.setDuration(ANIMATION_DURATION_FADE);
opacityAnim.setInterpolator(Interpolators.ALPHA_OUT);
opacityAnim.addListener(mAnimatorListener);
+ opacityAnim.addListener(mExitHwTraceAnimator);
opacityAnim.setTarget(mTargetView);
opacityAnim.start();
@@ -443,16 +452,41 @@ public class KeyButtonRipple extends Drawable {
private final AnimatorListenerAdapter mAnimatorListener =
new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningAnimations.remove(animation);
+ if (mRunningAnimations.isEmpty() && !mPressed) {
+ mVisible = false;
+ mDrawingHardwareGlow = false;
+ invalidateSelf();
+ }
+ }
+ };
+
+ private static final class TraceAnimatorListener extends AnimatorListenerAdapter {
+ private final String mName;
+ TraceAnimatorListener(String name) {
+ mName = name;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ Trace.beginSection("KeyButtonRipple.start." + mName);
+ Trace.endSection();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ Trace.beginSection("KeyButtonRipple.cancel." + mName);
+ Trace.endSection();
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
- mRunningAnimations.remove(animation);
- if (mRunningAnimations.isEmpty() && !mPressed) {
- mVisible = false;
- mDrawingHardwareGlow = false;
- invalidateSelf();
- }
+ Trace.beginSection("KeyButtonRipple.end." + mName);
+ Trace.endSection();
}
- };
+ }
/**
* Interpolator with a smooth log deceleration
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt b/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt
index ecd3afd687b3..a284a747da21 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/FloatProperties.kt
@@ -67,6 +67,40 @@ class FloatProperties {
}
/**
+ * Represents the width of a [Rect]. Typically used to animate resizing a Rect horizontally.
+ *
+ * This property's getter returns [Rect.width], and its setter changes the value of
+ * [Rect.right] by adding the animated width value to [Rect.left].
+ */
+ @JvmField
+ val RECT_WIDTH = object : FloatPropertyCompat<Rect>("RectWidth") {
+ override fun getValue(rect: Rect): Float {
+ return rect.width().toFloat()
+ }
+
+ override fun setValue(rect: Rect, value: Float) {
+ rect.right = rect.left + value.toInt()
+ }
+ }
+
+ /**
+ * Represents the height of a [Rect]. Typically used to animate resizing a Rect vertically.
+ *
+ * This property's getter returns [Rect.height], and its setter changes the value of
+ * [Rect.bottom] by adding the animated height value to [Rect.top].
+ */
+ @JvmField
+ val RECT_HEIGHT = object : FloatPropertyCompat<Rect>("RectHeight") {
+ override fun getValue(rect: Rect): Float {
+ return rect.height().toFloat()
+ }
+
+ override fun setValue(rect: Rect, value: Float) {
+ rect.bottom = rect.top + value.toInt()
+ }
+ }
+
+ /**
* Represents the x-coordinate of a [RectF]. Typically used to animate moving a RectF
* horizontally.
*
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 806d9d8e158a..e5b126d7ff7f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -17,6 +17,8 @@
package com.android.systemui.util.animation
import android.content.Context
+import android.graphics.Canvas
+import android.graphics.PointF
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
@@ -36,6 +38,7 @@ class TransitionLayout @JvmOverloads constructor(
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
+ private val boundsRect = Rect()
private val originalGoneChildrenSet: MutableSet<Int> = mutableSetOf()
private val originalViewAlphas: MutableMap<Int, Float> = mutableMapOf()
private var measureAsConstraint: Boolean = false
@@ -146,11 +149,26 @@ class TransitionLayout @JvmOverloads constructor(
}
}
+ override fun dispatchDraw(canvas: Canvas?) {
+ val clip = !boundsRect.isEmpty
+ if (clip) {
+ canvas?.save()
+ canvas?.clipRect(boundsRect)
+ }
+ super.dispatchDraw(canvas)
+ if (clip) {
+ canvas?.restore()
+ }
+ }
+
private fun updateBounds() {
val layoutLeft = left
val layoutTop = top
setLeftTopRightBottom(layoutLeft, layoutTop, layoutLeft + currentState.width,
layoutTop + currentState.height)
+ translationX = currentState.translation.x
+ translationY = currentState.translation.y
+ boundsRect.set(0, 0, (width + translationX).toInt(), (height + translationY).toInt())
}
/**
@@ -234,11 +252,13 @@ class TransitionViewState {
var widgetStates: MutableMap<Int, WidgetState> = mutableMapOf()
var width: Int = 0
var height: Int = 0
+ val translation = PointF()
fun copy(reusedState: TransitionViewState? = null): TransitionViewState {
// we need a deep copy of this, so we can't use a data class
val copy = reusedState ?: TransitionViewState()
copy.width = width
copy.height = height
+ copy.translation.set(translation.x, translation.y)
for (entry in widgetStates) {
copy.widgetStates[entry.key] = entry.value.copy()
}
@@ -256,6 +276,7 @@ class TransitionViewState {
}
width = transitionLayout.measuredWidth
height = transitionLayout.measuredHeight
+ translation.set(0.0f, 0.0f)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
index 9ee141053861..b73aeb30009c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.util.animation
import android.animation.ValueAnimator
+import android.graphics.PointF
import android.util.MathUtils
import com.android.systemui.Interpolators
@@ -43,6 +44,7 @@ open class TransitionLayoutController {
private var currentState = TransitionViewState()
private var animationStartState: TransitionViewState? = null
private var state = TransitionViewState()
+ private var pivot = PointF()
private var animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
init {
@@ -63,6 +65,7 @@ open class TransitionLayoutController {
startState = animationStartState!!,
endState = state,
progress = animator.animatedFraction,
+ pivot = pivot,
resultState = currentState)
view.setState(currentState)
}
@@ -75,8 +78,10 @@ open class TransitionLayoutController {
startState: TransitionViewState,
endState: TransitionViewState,
progress: Float,
+ pivot: PointF,
resultState: TransitionViewState
) {
+ this.pivot.set(pivot)
val view = transitionLayout ?: return
val childCount = view.childCount
for (i in 0 until childCount) {
@@ -178,6 +183,8 @@ open class TransitionLayoutController {
progress).toInt()
height = MathUtils.lerp(startState.height.toFloat(), endState.height.toFloat(),
progress).toInt()
+ translation.x = (endState.width - width) * pivot.x
+ translation.y = (endState.height - height) * pivot.y
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index 5a2b064c5389..47b607fc6285 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -178,6 +178,18 @@ abstract class MagnetizedObject<T : Any>(
var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
/**
+ * Method that is called when the object should be animated stuck to the target. The default
+ * implementation uses the object's x and y properties to animate the object centered inside the
+ * target. You can override this if you need custom animation.
+ *
+ * The method is invoked with the MagneticTarget that the object is sticking to, the X and Y
+ * velocities of the gesture that brought the object into the magnetic radius, whether or not it
+ * was flung, and a callback you must call after your animation completes.
+ */
+ var animateStuckToTarget: (MagneticTarget, Float, Float, Boolean, (() -> Unit)?) -> Unit =
+ ::animateStuckToTargetInternal
+
+ /**
* Sets whether forcefully flinging the object vertically towards a target causes it to be
* attracted to the target and then released immediately, despite never being dragged within the
* magnetic field.
@@ -373,7 +385,7 @@ abstract class MagnetizedObject<T : Any>(
targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
cancelAnimations()
magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
- animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false)
+ animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false, null)
vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
} else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
@@ -430,8 +442,8 @@ abstract class MagnetizedObject<T : Any>(
targetObjectIsStuckTo = flungToTarget
animateStuckToTarget(flungToTarget, velX, velY, true) {
- targetObjectIsStuckTo = null
magnetListener.onReleasedInTarget(flungToTarget)
+ targetObjectIsStuckTo = null
vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
}
@@ -465,7 +477,7 @@ abstract class MagnetizedObject<T : Any>(
}
/** Animates sticking the object to the provided target with the given start velocities. */
- private fun animateStuckToTarget(
+ private fun animateStuckToTargetInternal(
target: MagneticTarget,
velX: Float,
velY: Float,
@@ -581,10 +593,10 @@ abstract class MagnetizedObject<T : Any>(
* multiple objects.
*/
class MagneticTarget(
- internal val targetView: View,
+ val targetView: View,
var magneticFieldRadiusPx: Int
) {
- internal val centerOnScreen = PointF()
+ val centerOnScreen = PointF()
private val tempLoc = IntArray(2)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 6131e3b504af..369552fc814d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -340,7 +340,7 @@ public class Events {
}
/**
- * Logs an event to the event log and UiEvent (Westworld) logging. Compare writeEvent, which
+ * Logs an event to the event log and UiEvent (statsd) logging. Compare writeEvent, which
* adds more log destinations.
* @param tag One of the EVENT_* codes above.
* @param list Any additional event-specific arguments, documented above.
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 32e3a7fc032e..7b114525adcd 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -19,11 +19,13 @@ package com.android.systemui.wm;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
@@ -36,6 +38,7 @@ import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.internal.view.IInputMethodManager;
import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
@@ -352,6 +355,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
t.hide(mImeSourceControl.getLeash());
+ final IInputMethodManager imms = getImms();
+ if (imms != null) {
+ try {
+ // Remove the IME surface to make the insets invisible for
+ // non-client controlled insets.
+ imms.removeImeSurface();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to remove IME surface.", e);
+ }
+ }
}
t.apply();
mTransactionPool.release(t);
@@ -382,9 +395,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
* Called when the IME position is starting to animate.
*
* @param hiddenTop The y position of the top of the IME surface when it is hidden.
- * @param shownTop The y position of the top of the IME surface when it is shown.
- * @param showing {@code true} when we are animating from hidden to shown, {@code false}
- * when animating from shown to hidden.
+ * @param shownTop The y position of the top of the IME surface when it is shown.
+ * @param showing {@code true} when we are animating from hidden to shown, {@code false}
+ * when animating from shown to hidden.
*/
default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean showing, SurfaceControl.Transaction t) {}
@@ -406,4 +419,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
default void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {}
}
+
+ public IInputMethodManager getImms() {
+ return IInputMethodManager.Stub.asInterface(
+ ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 737ced63eed0..e6a41fbac3b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -31,6 +31,7 @@ import android.widget.ImageButton
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -75,9 +76,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var holder: PlayerViewHolder
@Mock private lateinit var view: TransitionLayout
- @Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
@Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
+ @Mock private lateinit var mediaViewController: MediaViewController
private lateinit var appIcon: ImageView
private lateinit var appName: TextView
private lateinit var albumView: ImageView
@@ -104,8 +105,10 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Before
fun setUp() {
bgExecutor = FakeExecutor(FakeSystemClock())
+ whenever(mediaViewController.expandedLayout).thenReturn(mock(ConstraintSet::class.java))
+ whenever(mediaViewController.collapsedLayout).thenReturn(mock(ConstraintSet::class.java))
- player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager,
+ player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
seekBarViewModel)
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
@@ -172,7 +175,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindWhenUnattached() {
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, null, null, device, null)
+ emptyList(), PACKAGE, null, null, device, true, null)
player.bind(state)
assertThat(player.isPlaying()).isFalse()
}
@@ -181,7 +184,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindText() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device, null)
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bind(state)
assertThat(appName.getText()).isEqualTo(APP)
assertThat(titleText.getText()).isEqualTo(TITLE)
@@ -192,7 +195,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindBackgroundColor() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device, null)
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bind(state)
val list = ArgumentCaptor.forClass(ColorStateList::class.java)
verify(view).setBackgroundTintList(list.capture())
@@ -203,7 +206,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindDevice() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device, null)
+ emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bind(state)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.isEnabled()).isTrue()
@@ -213,7 +216,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindDisabledDevice() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, null)
+ emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null)
player.bind(state)
assertThat(seamless.isEnabled()).isFalse()
assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
@@ -224,7 +227,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
fun bindNullDevice() {
player.attach(holder)
val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null, null)
+ emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
player.bind(state)
assertThat(seamless.isEnabled()).isTrue()
assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index bed5c9eb6df5..618ee892b2b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -79,7 +79,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mManager.addListener(mListener);
mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, null, KEY, false);
+ new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, KEY,
+ false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 6fcf6e37572b..6c7f2e8d7925 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -71,6 +72,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
private lateinit var fakeExecutor: FakeExecutor
+ @Mock private lateinit var dumpster: DumpManager
@Mock private lateinit var listener: MediaDeviceManager.Listener
@Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable
@@ -85,7 +87,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Before
fun setUp() {
fakeExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager)
+ manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager,
+ dumpster)
manager.addListener(listener)
// Configure mocks.
@@ -116,7 +119,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken()))
}
mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null,
- emptyList(), emptyList(), PACKAGE, session.sessionToken, null, null, null)
+ emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
+ device = null, active = true, resumeAction = null)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 7d44327b0d38..916fd0fe11b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -38,6 +38,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -93,11 +94,16 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
}
session.setActive(true)
mediaData = MediaData(true, 0, PACKAGE, null, null, SESSION_TITLE, null,
- emptyList(), emptyList(), PACKAGE, session.sessionToken, null, null, null)
+ emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
+ device = null, active = true, resumeAction = null)
}
@Test
fun testOnMediaDataLoaded_registersPlaybackListener() {
+ val playingState = mock(android.media.session.PlaybackState::class.java)
+ `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
+
+ `when`(mediaController.playbackState).thenReturn(playingState)
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
@@ -108,6 +114,13 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
}
@Test
+ fun testOnMediaDataLoaded_registersTimeout_whenPaused() {
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
+ verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ }
+
+ @Test
fun testOnMediaDataRemoved_unregistersPlaybackListener() {
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
mediaTimeoutListener.onMediaDataRemoved(KEY)
@@ -164,4 +177,4 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
assertThat(mediaTimeoutListener.isTimedOut(KEY)).isFalse()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 23099d783586..22f50d0fb591 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -25,17 +25,21 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
@@ -44,6 +48,7 @@ import android.os.BatteryManager;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserManager;
+import android.view.View;
import android.view.ViewGroup;
import androidx.test.InstrumentationRegistry;
@@ -52,13 +57,16 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -75,6 +83,10 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class KeyguardIndicationControllerTest extends SysuiTestCase {
+ private static final String ORGANIZATION_NAME = "organization";
+
+ private String mDisclosureWithOrganization;
+
@Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
@@ -82,6 +94,12 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
+ private KeyguardIndicationTextView mDisclosure;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private LockIcon mLockIcon;
+ @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -112,11 +130,17 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mContext.addMockSystemService(UserManager.class, mUserManager);
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
+ mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
+ ORGANIZATION_NAME);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
+
+ when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
+ .thenReturn(mDisclosure);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
+ when(mDisclosure.getAlpha()).thenReturn(1f);
mWakeLock = new WakeLockFake();
mWakeLockBuilder = new WakeLockFake.Builder(mContext);
@@ -130,10 +154,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
- mDockManager, mIBatteryStats);
+ mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats);
mController.setIndicationArea(mIndicationArea);
mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
clearInvocations(mIBatteryStats);
+ verify(mDisclosure).getAlpha();
}
@Test
@@ -215,6 +240,106 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void disclosure_unmanaged() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
+ createController();
+
+ verify(mDisclosure).setVisibility(View.GONE);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
+ public void disclosure_managedNoOwnerName() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+ createController();
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
+ public void disclosure_hiddenWhenDozing() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+ createController();
+
+ mController.setVisible(true);
+ mController.onDozeAmountChanged(1, 1);
+ mController.setDozing(true);
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).setAlpha(0f);
+ verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
+ public void disclosure_visibleWhenDozing() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+ createController();
+
+ mController.setVisible(true);
+ mController.onDozeAmountChanged(0, 0);
+ mController.setDozing(false);
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).setAlpha(1f);
+ verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
+ public void disclosure_managedOwnerName() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
+ createController();
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
+ public void disclosure_updateOnTheFly() {
+ ArgumentCaptor<BroadcastReceiver> receiver = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ doNothing().when(mBroadcastDispatcher).registerReceiver(receiver.capture(), any());
+
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
+ createController();
+
+ final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback();
+ reset(mDisclosure);
+
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+ receiver.getValue().onReceive(mContext, new Intent());
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
+ verifyNoMoreInteractions(mDisclosure);
+ reset(mDisclosure);
+
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
+ receiver.getValue().onReceive(mContext, new Intent());
+
+ verify(mDisclosure).setVisibility(View.VISIBLE);
+ verify(mDisclosure).switchIndication(mDisclosureWithOrganization);
+ verifyNoMoreInteractions(mDisclosure);
+ reset(mDisclosure);
+
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
+ receiver.getValue().onReceive(mContext, new Intent());
+
+ verify(mDisclosure).setVisibility(View.GONE);
+ verifyNoMoreInteractions(mDisclosure);
+ }
+
+ @Test
public void transientIndication_holdsWakeLock_whenDozing() {
createController();
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index fd6f171487a9..f14def6a3a02 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -37,8 +37,8 @@ public final class TetheringConstants {
private TetheringConstants() { }
/**
- * Extra used for communicating with the TetherService. Includes the type of tethering to
- * enable if any.
+ * Extra used for communicating with the TetherService and TetherProvisioningActivity.
+ * Includes the type of tethering to enable if any.
*/
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
/**
@@ -56,8 +56,38 @@ public final class TetheringConstants {
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
/**
- * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
- * which will receive provisioning results. Can be left empty.
+ * Extra used for communicating with the TetherService and TetherProvisioningActivity.
+ * Contains the {@link ResultReceiver} which will receive provisioning results.
+ * Can not be empty.
*/
public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
+ /**
+ * Extra used for communicating with the TetherService and TetherProvisioningActivity.
+ * Contains the subId of current active cellular upstream.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
+
+ /**
+ * Extra used for telling TetherProvisioningActivity the entitlement package name and class
+ * name to start UI entitlement check.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME =
+ "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME";
+
+ /**
+ * Extra used for telling TetherService the intent action to start silent entitlement check.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION =
+ "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION";
+
+ /**
+ * Extra used for TetherService to receive the response of provisioning check.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_PROVISIONING_RESPONSE =
+ "android.net.extra.TETHER_PROVISIONING_RESPONSE";
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 3c6e8d88ed13..9dace709d734 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -19,6 +19,10 @@ package com.android.networkstack.tethering;
import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
+import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
+import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
+import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
+import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_INVALID;
@@ -69,7 +73,6 @@ public class EntitlementManager {
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
private static final String ACTION_PROVISIONING_ALARM =
"com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
- private static final String EXTRA_SUBID = "subId";
private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
@@ -197,9 +200,9 @@ public class EntitlementManager {
// till upstream change to cellular.
if (mUsingCellularAsUpstream) {
if (showProvisioningUi) {
- runUiTetherProvisioning(downstreamType, config.activeDataSubId);
+ runUiTetherProvisioning(downstreamType, config);
} else {
- runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
+ runSilentTetherProvisioning(downstreamType, config);
}
mNeedReRunProvisioningUi = false;
} else {
@@ -262,9 +265,9 @@ public class EntitlementManager {
if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
if (mNeedReRunProvisioningUi) {
mNeedReRunProvisioningUi = false;
- runUiTetherProvisioning(downstream, config.activeDataSubId);
+ runUiTetherProvisioning(downstream, config);
} else {
- runSilentTetherProvisioning(downstream, config.activeDataSubId);
+ runSilentTetherProvisioning(downstream, config);
}
}
}
@@ -361,7 +364,7 @@ public class EntitlementManager {
* @param subId default data subscription ID.
*/
@VisibleForTesting
- protected void runSilentTetherProvisioning(int type, int subId) {
+ protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) {
if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
// For silent provisioning, settings would stop tethering when entitlement fail.
ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null);
@@ -369,17 +372,20 @@ public class EntitlementManager {
Intent intent = new Intent();
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi);
+ intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_SUBID, subId);
+ intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
intent.setComponent(mSilentProvisioningService);
// Only admin user can change tethering and SilentTetherProvisioning don't need to
// show UI, it is fine to always start setting's background service as system user.
mContext.startService(intent);
+ return intent;
}
- private void runUiTetherProvisioning(int type, int subId) {
+ private void runUiTetherProvisioning(int type, final TetheringConfiguration config) {
ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null);
- runUiTetherProvisioning(type, subId, receiver);
+ runUiTetherProvisioning(type, config, receiver);
}
/**
@@ -389,17 +395,20 @@ public class EntitlementManager {
* @param receiver to receive entitlement check result.
*/
@VisibleForTesting
- protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
+ protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config,
+ ResultReceiver receiver) {
if (DBG) mLog.i("runUiTetherProvisioning: " + type);
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_SUBID, subId);
+ intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Only launch entitlement UI for system user. Entitlement UI should not appear for other
// user because only admin user is allowed to change tethering.
mContext.startActivity(intent);
+ return intent;
}
// Not needed to check if this don't run on the handler thread because it's private.
@@ -631,7 +640,7 @@ public class EntitlementManager {
receiver.send(cacheValue, null);
} else {
ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
- runUiTetherProvisioning(downstream, config.activeDataSubId, proxy);
+ runUiTetherProvisioning(downstream, config, proxy);
}
}
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index df6745855067..2f01186b117d 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -18,6 +18,7 @@ package com.android.networkstack.tethering;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
+import static android.content.pm.PackageManager.GET_ACTIVITIES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
@@ -70,6 +71,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
@@ -109,7 +111,6 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceSpecificException;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -782,11 +783,30 @@ public class Tethering {
}
}
+ private boolean isProvisioningNeededButUnavailable() {
+ return isTetherProvisioningRequired() && !doesEntitlementPackageExist();
+ }
+
boolean isTetherProvisioningRequired() {
final TetheringConfiguration cfg = mConfig;
return mEntitlementMgr.isTetherProvisioningRequired(cfg);
}
+ private boolean doesEntitlementPackageExist() {
+ // provisioningApp must contain package and class name.
+ if (mConfig.provisioningApp.length != 2) {
+ return false;
+ }
+
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(mConfig.provisioningApp[0], GET_ACTIVITIES);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
// TODO: Figure out how to update for local hotspot mode interfaces.
private void sendTetherStateChangedBroadcast() {
if (!isTetheringSupported()) return;
@@ -2145,14 +2165,14 @@ public class Tethering {
// gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off.
boolean isTetheringSupported() {
- final int defaultVal =
- SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
+ final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1;
final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
final boolean tetherEnabledInSettings = tetherSupported
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
- return tetherEnabledInSettings && hasTetherableConfiguration();
+ return tetherEnabledInSettings && hasTetherableConfiguration()
+ && !isProvisioningNeededButUnavailable();
}
void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 48a600dfe6e1..1d45f129b51b 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -107,6 +107,7 @@ public class TetheringConfiguration {
public final String[] provisioningApp;
public final String provisioningAppNoUi;
public final int provisioningCheckPeriod;
+ public final String provisioningResponse;
public final int activeDataSubId;
@@ -141,10 +142,13 @@ public class TetheringConfiguration {
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
- provisioningAppNoUi = getProvisioningAppNoUi(res);
+ provisioningAppNoUi = getResourceString(res,
+ R.string.config_mobile_hotspot_provision_app_no_ui);
provisioningCheckPeriod = getResourceInteger(res,
R.integer.config_mobile_hotspot_provision_check_period,
0 /* No periodic re-check */);
+ provisioningResponse = getResourceString(res,
+ R.string.config_mobile_hotspot_provision_response);
mOffloadPollInterval = getResourceInteger(res,
R.integer.config_tether_offload_poll_interval,
@@ -337,9 +341,9 @@ public class TetheringConfiguration {
return copy(LEGACY_DHCP_DEFAULT_RANGE);
}
- private static String getProvisioningAppNoUi(Resources res) {
+ private static String getResourceString(Resources res, final int resId) {
try {
- return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui);
+ return res.getString(resId);
} catch (Resources.NotFoundException e) {
return "";
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index d637c8646b4a..31f747d3c4b5 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -26,6 +26,8 @@ import android.net.util.SharedLog;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.SystemProperties;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -150,4 +152,11 @@ public abstract class TetheringDependencies {
* Get a reference to BluetoothAdapter to be used by tethering.
*/
public abstract BluetoothAdapter getBluetoothAdapter();
+
+ /**
+ * Get SystemProperties which indicate whether tethering is denied.
+ */
+ public boolean isTetheringDenied() {
+ return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true");
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 72fa916b9e42..354e75356e9f 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -16,8 +16,16 @@
package com.android.networkstack.tethering;
+import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
+import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
+import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
+import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
+import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
+import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
+import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringManager.TETHERING_INVALID;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -44,6 +52,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Bundle;
@@ -53,6 +62,7 @@ import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import androidx.test.filters.SmallTest;
@@ -76,6 +86,7 @@ public final class EntitlementManagerTest {
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
+ private static final String PROVISIONING_APP_RESPONSE = "app_response";
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
@@ -122,15 +133,51 @@ public final class EntitlementManagerTest {
}
@Override
- protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
+ protected Intent runUiTetherProvisioning(int type,
+ final TetheringConfiguration config, final ResultReceiver receiver) {
+ Intent intent = super.runUiTetherProvisioning(type, config, receiver);
+ assertUiTetherProvisioningIntent(type, config, receiver, intent);
uiProvisionCount++;
receiver.send(fakeEntitlementResult, null);
+ return intent;
+ }
+
+ private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config,
+ final ResultReceiver receiver, final Intent intent) {
+ assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction());
+ assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
+ final String[] appName = intent.getStringArrayExtra(
+ EXTRA_TETHER_UI_PROVISIONING_APP_NAME);
+ assertEquals(PROVISIONING_APP_NAME.length, appName.length);
+ for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) {
+ assertEquals(PROVISIONING_APP_NAME[i], appName[i]);
+ }
+ assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK));
+ assertEquals(config.activeDataSubId,
+ intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
@Override
- protected void runSilentTetherProvisioning(int type, int subId) {
+ protected Intent runSilentTetherProvisioning(int type,
+ final TetheringConfiguration config) {
+ Intent intent = super.runSilentTetherProvisioning(type, config);
+ assertSilentTetherProvisioning(type, config, intent);
silentProvisionCount++;
addDownstreamMapping(type, fakeEntitlementResult);
+ return intent;
+ }
+
+ private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config,
+ final Intent intent) {
+ assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
+ assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false));
+ assertEquals(PROVISIONING_NO_UI_APP_NAME,
+ intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION));
+ assertEquals(PROVISIONING_APP_RESPONSE,
+ intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE));
+ assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK));
+ assertEquals(config.activeDataSubId,
+ intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
}
@@ -187,6 +234,8 @@ public final class EntitlementManagerTest {
.thenReturn(PROVISIONING_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
+ when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn(
+ PROVISIONING_APP_RESPONSE);
// Act like the CarrierConfigManager is present and ready unless told otherwise.
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(mCarrierConfigManager);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 1999ad786ed4..312186391d5f 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -61,6 +61,8 @@ public class TetheringConfigurationTest {
private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+ private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
+ private static final String PROVISIONING_APP_RESPONSE = "app_response";
@Mock private Context mContext;
@Mock private TelephonyManager mTelephonyManager;
@Mock private Resources mResources;
@@ -388,6 +390,8 @@ public class TetheringConfigurationTest {
new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
+ assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME);
+ assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE);
}
private void setUpResourceForSubId() {
@@ -403,6 +407,10 @@ public class TetheringConfigurationTest {
new int[0]);
when(mResourcesForSubId.getStringArray(
R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
+ when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
+ .thenReturn(PROVISIONING_NO_UI_APP_NAME);
+ when(mResourcesForSubId.getString(
+ R.string.config_mobile_hotspot_provision_response)).thenReturn(
+ PROVISIONING_APP_RESPONSE);
}
-
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 8146a58dddcb..f53c42b7e760 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -16,6 +16,7 @@
package com.android.networkstack.tethering;
+import static android.content.pm.PackageManager.GET_ACTIVITIES;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM;
@@ -81,6 +82,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
@@ -204,6 +206,7 @@ public class TetheringTest {
@Mock private EthernetManager mEm;
@Mock private TetheringNotificationUpdater mNotificationUpdater;
@Mock private BpfCoordinator mBpfCoordinator;
+ @Mock private PackageManager mPackageManager;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -264,6 +267,11 @@ public class TetheringTest {
}
@Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
public String getSystemServiceName(Class<?> serviceClass) {
if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
return super.getSystemServiceName(serviceClass);
@@ -425,6 +433,11 @@ public class TetheringTest {
public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
return mNotificationUpdater;
}
+
+ @Override
+ public boolean isTetheringDenied() {
+ return false;
+ }
}
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -1951,6 +1964,23 @@ public class TetheringTest {
assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME));
}
+ @Test
+ public void testProvisioningNeededButUnavailable() throws Exception {
+ assertTrue(mTethering.isTetheringSupported());
+ verify(mPackageManager, never()).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
+
+ setupForRequiredProvisioning();
+ assertTrue(mTethering.isTetheringSupported());
+ verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
+ reset(mPackageManager);
+
+ doThrow(PackageManager.NameNotFoundException.class).when(mPackageManager).getPackageInfo(
+ PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
+ setupForRequiredProvisioning();
+ assertFalse(mTethering.isTetheringSupported());
+ verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES);
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index 0ec8654f2a20..398ece4c1836 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -77,8 +77,8 @@ final class AutofillInlineSessionController {
if (mSession != null) {
// Destroy the existing session.
mSession.destroySessionLocked();
- mInlineFillUi = null;
}
+ mInlineFillUi = null;
// TODO(b/151123764): consider reusing the same AutofillInlineSession object for the
// same field.
mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId,
@@ -87,6 +87,22 @@ final class AutofillInlineSessionController {
}
/**
+ * Destroys the current session. May send an empty response to IME to clear the suggestions if
+ * the focus didn't change to a different field.
+ *
+ * @param autofillId the currently focused view from the autofill session
+ */
+ @GuardedBy("mLock")
+ void destroyLocked(@NonNull AutofillId autofillId) {
+ if (mSession != null) {
+ mSession.onInlineSuggestionsResponseLocked(InlineFillUi.emptyUi(autofillId));
+ mSession.destroySessionLocked();
+ mSession = null;
+ }
+ mInlineFillUi = null;
+ }
+
+ /**
* Returns the {@link InlineSuggestionsRequest} provided by IME for the last request.
*
* <p> The caller is responsible for making sure Autofill hears back from IME before calling
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 96b593d9682f..57ffe0498a88 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -815,6 +815,19 @@ final class AutofillManagerServiceImpl
}
}
+ void logAugmentedAutofillAuthenticationSelected(int sessionId, @Nullable String selectedDataset,
+ @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillEventHistory == null
+ || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+ return;
+ }
+ mAugmentedAutofillEventHistory.addEvent(
+ new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+ clientState, null, null, null, null, null, null, null, null));
+ }
+ }
+
void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
@Nullable Bundle clientState) {
synchronized (mLock) {
@@ -1199,6 +1212,14 @@ final class AutofillManagerServiceImpl
}
@Override
+ public void logAugmentedAutofillAuthenticationSelected(int sessionId,
+ String suggestionId, Bundle clientState) {
+ AutofillManagerServiceImpl.this
+ .logAugmentedAutofillAuthenticationSelected(
+ sessionId, suggestionId, clientState);
+ }
+
+ @Override
public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
Slog.w(TAG, "remote augmented autofill service died");
final RemoteAugmentedAutofillService remoteService =
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index a7d0061cc043..11f901538868 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -167,14 +167,16 @@ final class RemoteAugmentedAutofillService
new IFillCallback.Stub() {
@Override
public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ @Nullable Bundle clientState, boolean showingFillWindow) {
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsRequest, inlineSuggestionsData,
clientState, focusedId, focusedValue,
inlineSuggestionsCallback,
client, onErrorCallback, remoteRenderService);
- requestAutofill.complete(null);
+ if (!showingFillWindow) {
+ requestAutofill.complete(null);
+ }
}
@Override
@@ -263,7 +265,28 @@ final class RemoteAugmentedAutofillService
request, inlineSuggestionsData, focusedId, filterText,
new InlineFillUi.InlineSuggestionUiCallback() {
@Override
- public void autofill(Dataset dataset) {
+ public void autofill(Dataset dataset, int datasetIndex) {
+ if (dataset.getAuthentication() != null) {
+ mCallbacks.logAugmentedAutofillAuthenticationSelected(sessionId,
+ dataset.getId(), clientState);
+ final IntentSender action = dataset.getAuthentication();
+ final int authenticationId =
+ AutofillManager.makeAuthenticationId(
+ Session.AUGMENTED_AUTOFILL_REQUEST_ID,
+ datasetIndex);
+ final Intent fillInIntent = new Intent();
+ fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE,
+ clientState);
+ try {
+ client.authenticate(sessionId, authenticationId, action,
+ fillInIntent, false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error starting auth flow");
+ inlineSuggestionsCallback.apply(
+ InlineFillUi.emptyUi(focusedId));
+ }
+ return;
+ }
mCallbacks.logAugmentedAutofillSelected(sessionId,
dataset.getId(), clientState);
try {
@@ -319,5 +342,8 @@ final class RemoteAugmentedAutofillService
void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
@Nullable Bundle clientState);
+
+ void logAugmentedAutofillAuthenticationSelected(int sessionId,
+ @Nullable String suggestionId, @Nullable Bundle clientState);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a9a0ab69f633..712b413c6e1a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -144,7 +144,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final MetricsLogger mMetricsLogger = new MetricsLogger();
- private static AtomicInteger sIdCounter = new AtomicInteger();
+ static final int AUGMENTED_AUTOFILL_REQUEST_ID = 1;
+
+ private static AtomicInteger sIdCounter = new AtomicInteger(2);
/**
* ID of the session.
@@ -313,18 +315,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
void onSwitchInputMethodLocked() {
+ // One caveat is that for the case where the focus is on a field for which regular autofill
+ // returns null, and augmented autofill is triggered, and then the user switches the input
+ // method. Tapping on the field again will not trigger a new augmented autofill request.
+ // This may be fixed by adding more checks such as whether mCurrentViewId is null.
if (mExpiredResponse) {
return;
}
-
- if (shouldExpireResponseOnInputMethodSwitch()) {
+ if (shouldResetSessionStateOnInputMethodSwitch()) {
// Set the old response expired, so the next action (ACTION_VIEW_ENTERED) can trigger
// a new fill request.
mExpiredResponse = true;
+ // Clear the augmented autofillable ids so augmented autofill will trigger again.
+ mAugmentedAutofillableIds = null;
+ // In case the field is augmented autofill only, we clear the current view id, so that
+ // we won't skip view entered due to same view entered, for the augmented autofill.
+ if (mForAugmentedAutofillOnly) {
+ mCurrentViewId = null;
+ }
}
}
- private boolean shouldExpireResponseOnInputMethodSwitch() {
+ private boolean shouldResetSessionStateOnInputMethodSwitch() {
// One of below cases will need a new fill request to update the inline spec for the new
// input method.
// 1. The autofill provider supports inline suggestion and the render service is available.
@@ -726,7 +738,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
viewState.setState(newState);
int requestId;
-
+ // TODO(b/158623971): Update this to prevent possible overflow
do {
requestId = sIdCounter.getAndIncrement();
} while (requestId == INVALID_REQUEST_ID);
@@ -1334,6 +1346,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ id + " destroyed");
return;
}
+ final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
+ if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
+ setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
+ return;
+ }
if (mResponses == null) {
// Typically happens when app explicitly called cancel() while the service was showing
// the auth UI.
@@ -1341,7 +1358,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
removeSelf();
return;
}
- final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
final FillResponse authenticatedResponse = mResponses.get(requestId);
if (authenticatedResponse == null || data == null) {
Slog.w(TAG, "no authenticated response");
@@ -1401,6 +1417,58 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
+ void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) {
+ final Dataset dataset = (data == null) ? null :
+ data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
+ if (sDebug) {
+ Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id
+ + ", authId=" + authId + ", dataset=" + dataset);
+ }
+ if (dataset == null
+ || dataset.getFieldIds().size() != 1
+ || dataset.getFieldIds().get(0) == null
+ || dataset.getFieldValues().size() != 1
+ || dataset.getFieldValues().get(0) == null) {
+ if (sDebug) {
+ Slog.d(TAG, "Rejecting empty/invalid auth result");
+ }
+ mService.resetLastAugmentedAutofillResponse();
+ removeSelfLocked();
+ return;
+ }
+ final List<AutofillId> fieldIds = dataset.getFieldIds();
+ final List<AutofillValue> autofillValues = dataset.getFieldValues();
+ final AutofillId fieldId = fieldIds.get(0);
+ final AutofillValue value = autofillValues.get(0);
+
+ // Update state to ensure that after filling the field here we don't end up firing another
+ // autofill request that will end up showing the same suggestions to the user again. When
+ // the auth activity came up, the field for which the suggestions were shown lost focus and
+ // mCurrentViewId was cleared. We need to set mCurrentViewId back to the id of the field
+ // that we are filling.
+ fieldId.setSessionId(id);
+ mCurrentViewId = fieldId;
+
+ // Notify the Augmented Autofill provider of the dataset that was selected.
+ final Bundle clientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
+ mService.logAugmentedAutofillSelected(id, dataset.getId(), clientState);
+
+ // Fill the value into the field.
+ if (sDebug) {
+ Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value);
+ }
+ try {
+ mClient.autofill(id, fieldIds, autofillValues, true);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value
+ + ", error=" + e);
+ }
+
+ // Clear the suggestions since the user already accepted one of them.
+ mInlineSessionController.setInlineFillUiLocked(InlineFillUi.emptyUi(fieldId));
+ }
+
+ @GuardedBy("mLock")
void setHasCallbackLocked(boolean hasIt) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
@@ -2496,6 +2564,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ actionAsString(action) + ", flags=" + flags);
}
ViewState viewState = mViewStates.get(id);
+ if (sVerbose) {
+ Slog.v(TAG, "updateLocked(" + this.id + "): mCurrentViewId=" + mCurrentViewId
+ + ", mExpiredResponse=" + mExpiredResponse + ", viewState=" + viewState);
+ }
if (viewState == null) {
if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
@@ -2588,15 +2660,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
id)) {
// Regular autofill handled the view and returned null response, but it
// triggered augmented autofill
- if (!isSameViewEntered || mExpiredResponse) {
+ if (!isSameViewEntered) {
if (sDebug) Slog.d(TAG, "trigger augmented autofill.");
triggerAugmentedAutofillLocked(flags);
} else {
if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
}
return;
- } else if (mForAugmentedAutofillOnly && isSameViewEntered
- && !mExpiredResponse) {
+ } else if (mForAugmentedAutofillOnly && isSameViewEntered) {
// Regular autofill is disabled.
if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
return;
@@ -3208,16 +3279,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
};
// When the inline suggestion render service is available and the view is focused, there
- // are 2 cases when augmented autofill should ask IME for inline suggestion request,
+ // are 3 cases when augmented autofill should ask IME for inline suggestion request,
// because standard autofill flow didn't:
// 1. the field is augmented autofill only (when standard autofill provider is None or
// when it returns null response)
// 2. standard autofill provider doesn't support inline suggestion
+ // 3. we re-entered the autofill session and standard autofill was not re-triggered, this is
+ // recognized by seeing mExpiredResponse == true
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService != null
&& (mForAugmentedAutofillOnly
- || !isInlineSuggestionsEnabledByAutofillProviderLocked())
+ || !isInlineSuggestionsEnabledByAutofillProviderLocked()
+ || mExpiredResponse)
&& isViewFocusedLocked(flags)) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback(
@@ -3702,6 +3776,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
unlinkClientVultureLocked();
mUi.destroyAll(mPendingSaveUi, this, true);
mUi.clearCallback(this);
+ if (mCurrentViewId != null) {
+ mInlineSessionController.destroyLocked(mCurrentViewId);
+ }
mDestroyed = true;
// Log metrics
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index a3d0fb955da4..1c430b3543ac 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -290,7 +290,7 @@ public final class InlineFillUi {
/**
* Callback to autofill a dataset to the client app.
*/
- void autofill(@NonNull Dataset dataset);
+ void autofill(@NonNull Dataset dataset, int datasetIndex);
/**
* Callback to start Intent in client app.
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index c8485b7c2b38..462ffd668e2e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -109,7 +109,7 @@ final class InlineSuggestionFactory {
return createInlineSuggestionsInternal(/* isAugmented= */ true, request,
datasets, autofillId, onErrorCallback,
(dataset, datasetIndex) ->
- inlineSuggestionUiCallback.autofill(dataset),
+ inlineSuggestionUiCallback.autofill(dataset, datasetIndex),
(intentSender) ->
inlineSuggestionUiCallback.startIntentSender(intentSender, new Intent()),
remoteRenderService);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 13e5ab451cfc..4a4b7dd7cc29 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -26,6 +26,15 @@ import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECU
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ACCEPT_DATA_SHARE_REQUEST;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_EMPTY_DATA;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_IOEXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_SERVICE_PIPE_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST;
import static com.android.internal.util.SyncResultReceiver.bundleFor;
import android.annotation.NonNull;
@@ -95,6 +104,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* A service used to observe the contents of the screen.
@@ -114,6 +124,14 @@ public final class ContentCaptureManagerService extends
private static final int MAX_CONCURRENT_FILE_SHARING_REQUESTS = 10;
private static final int DATA_SHARE_BYTE_BUFFER_LENGTH = 1_024;
+ // Needed to pass checkstyle_hook as names are too long for one line.
+ private static final int EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST =
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST;
+ private static final int EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED =
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED;
+ private static final int EVENT__DATA_SHARE_WRITE_FINISHED =
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED;
+
private final LocalService mLocalService = new LocalService();
@Nullable
@@ -657,6 +675,10 @@ public final class ContentCaptureManagerService extends
if (mPackagesWithShareRequests.size() >= MAX_CONCURRENT_FILE_SHARING_REQUESTS
|| mPackagesWithShareRequests.contains(request.getPackageName())) {
try {
+ String serviceName = mServiceNameResolver.getServiceName(userId);
+ ContentCaptureMetricsLogger.writeServiceEvent(
+ EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST,
+ serviceName, request.getPackageName());
clientAdapter.error(
ContentCaptureManager.DATA_SHARE_ERROR_CONCURRENT_REQUEST);
} catch (RemoteException e) {
@@ -920,6 +942,7 @@ public final class ContentCaptureManagerService extends
@NonNull private final DataShareRequest mDataShareRequest;
@NonNull private final IDataShareWriteAdapter mClientAdapter;
@NonNull private final ContentCaptureManagerService mParentService;
+ @NonNull private final AtomicBoolean mLoggedWriteFinish = new AtomicBoolean(false);
DataShareCallbackDelegate(@NonNull DataShareRequest dataShareRequest,
@NonNull IDataShareWriteAdapter clientAdapter,
@@ -932,9 +955,12 @@ public final class ContentCaptureManagerService extends
@Override
public void accept(@NonNull IDataShareReadAdapter serviceAdapter) throws RemoteException {
Slog.i(TAG, "Data share request accepted by Content Capture service");
+ logServiceEvent(CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ACCEPT_DATA_SHARE_REQUEST);
Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
if (clientPipe == null) {
+ logServiceEvent(
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL);
mClientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
serviceAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
return;
@@ -945,6 +971,8 @@ public final class ContentCaptureManagerService extends
Pair<ParcelFileDescriptor, ParcelFileDescriptor> servicePipe = createPipe();
if (servicePipe == null) {
+ logServiceEvent(
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_SERVICE_PIPE_FAIL);
bestEffortCloseFileDescriptors(sourceIn, sinkIn);
mClientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
@@ -987,6 +1015,8 @@ public final class ContentCaptureManagerService extends
}
} catch (IOException e) {
Slog.e(TAG, "Failed to pipe client and service streams", e);
+ logServiceEvent(
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_IOEXCEPTION);
sendErrorSignal(mClientAdapter, serviceAdapter,
ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
@@ -996,6 +1026,10 @@ public final class ContentCaptureManagerService extends
.remove(mDataShareRequest.getPackageName());
}
if (receivedData) {
+ if (!mLoggedWriteFinish.get()) {
+ logServiceEvent(EVENT__DATA_SHARE_WRITE_FINISHED);
+ mLoggedWriteFinish.set(true);
+ }
try {
mClientAdapter.finish();
} catch (RemoteException e) {
@@ -1008,6 +1042,8 @@ public final class ContentCaptureManagerService extends
}
} else {
// Client or service may have crashed before sending.
+ logServiceEvent(
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_EMPTY_DATA);
sendErrorSignal(mClientAdapter, serviceAdapter,
ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
}
@@ -1027,6 +1063,7 @@ public final class ContentCaptureManagerService extends
@Override
public void reject() throws RemoteException {
Slog.i(TAG, "Data share request rejected by Content Capture service");
+ logServiceEvent(CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST);
mClientAdapter.rejected();
}
@@ -1048,11 +1085,16 @@ public final class ContentCaptureManagerService extends
&& !sourceOut.getFileDescriptor().valid();
if (finishedSuccessfully) {
+ if (!mLoggedWriteFinish.get()) {
+ logServiceEvent(EVENT__DATA_SHARE_WRITE_FINISHED);
+ mLoggedWriteFinish.set(true);
+ }
Slog.i(TAG, "Content capture data sharing session terminated "
+ "successfully for package '"
+ mDataShareRequest.getPackageName()
+ "'");
} else {
+ logServiceEvent(EVENT__DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED);
Slog.i(TAG, "Reached the timeout of Content Capture data sharing session "
+ "for package '"
+ mDataShareRequest.getPackageName()
@@ -1123,5 +1165,12 @@ public final class ContentCaptureManagerService extends
Slog.e(TAG, "Failed to call error() the service operation", e);
}
}
+
+ private void logServiceEvent(int eventType) {
+ int userId = UserHandle.getCallingUserId();
+ String serviceName = mParentService.mServiceNameResolver.getServiceName(userId);
+ ContentCaptureMetricsLogger.writeServiceEvent(eventType, serviceName,
+ mDataShareRequest.getPackageName());
+ }
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 0b9bf3962562..08e6a0550f69 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -156,6 +156,9 @@ final class RemoteContentCaptureService
public void onDataShareRequest(@NonNull DataShareRequest request,
@NonNull IDataShareCallback.Stub dataShareCallback) {
scheduleAsyncRequest((s) -> s.onDataShared(request, dataShareCallback));
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DATA_SHARE_REQUEST,
+ mComponentName, request.getPackageName());
}
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2958fd2ae63a..36ba610085e1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1698,6 +1698,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
return newNc;
}
+ // Allow VPNs to see ownership of their own VPN networks - not location sensitive.
+ if (nc.hasTransport(TRANSPORT_VPN)) {
+ // Owner UIDs already checked above. No need to re-check.
+ return newNc;
+ }
+
Binder.withCleanCallingIdentity(
() -> {
if (!mLocationPermissionChecker.checkLocationPermission(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 079512275797..133ea8394ba6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,8 +24,8 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
-import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -16911,8 +16911,9 @@ public class ActivityManagerService extends IActivityManager.Stub
"disable hidden API checks");
}
+ // TODO(b/158750470): remove
final boolean mountExtStorageFull = isCallerShell()
- && (flags & INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL) != 0;
+ && (flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0;
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
@@ -16934,6 +16935,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!mActiveInstrumentation.contains(activeInstr)) {
mActiveInstrumentation.add(activeInstr);
}
+
+ if ((flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0) {
+ // Allow OP_NO_ISOLATED_STORAGE app op for the package running instrumentation with
+ // --no-isolated-storage flag.
+ mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, ai.uid,
+ ii.packageName, AppOpsManager.MODE_ALLOWED);
+ }
Binder.restoreCallingIdentity(origId);
}
@@ -17024,6 +17032,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// Can't call out of the system process with a lock held, so post a message.
if (instr.mUiAutomationConnection != null) {
+ // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
+ mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
+ app.info.packageName, AppOpsManager.MODE_ERRORED);
mAppOpsService.setAppOpsServiceDelegate(null);
getPermissionManagerInternalLocked().setCheckPermissionDelegate(null);
mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index a0349493edbc..a168af5ad842 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -1269,12 +1269,12 @@ public final class ProcessStatsService extends IProcessStats.Stub {
* Dump proto for the statsd, mainly for testing.
*/
private void dumpProtoForStatsd(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ final ProtoOutputStream[] protos = {new ProtoOutputStream(fd)};
ProcessStats procStats = new ProcessStats(false);
getCommittedStatsMerged(0, 0, true, null, procStats);
- procStats.dumpAggregatedProtoForStatsd(proto);
+ procStats.dumpAggregatedProtoForStatsd(protos, 999999 /* max bytes per shard */);
- proto.flush();
+ protos[0].flush();
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e654af706fca..1f85d1046523 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1106,7 +1106,8 @@ public class Vpn {
NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
- mNetworkCapabilities.setOwnerUid(Binder.getCallingUid());
+ mNetworkCapabilities.setOwnerUid(mOwnerUID);
+ mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID});
mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
mConfig.allowedApplications, mConfig.disallowedApplications));
long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 18adc0ba27ee..d4377e4870a5 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -116,12 +116,20 @@ final class DisplayDeviceInfo {
/**
* Flag: This flag identifies secondary displays that should show system decorations, such as
* status bar, navigation bar, home activity or IME.
+ * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
* @hide
*/
// TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 12;
/**
+ * Flag: The display is trusted to show system decorations and receive inputs without users'
+ * touch.
+ * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ */
+ public static final int FLAG_TRUSTED = 1 << 13;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index dee6cd02917f..1058000e0b68 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -25,6 +26,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_C
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
@@ -2189,16 +2191,25 @@ public final class DisplayManagerService extends SystemService {
}
}
+ if (callingUid == Process.SYSTEM_UID
+ || checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
+ flags |= VIRTUAL_DISPLAY_FLAG_TRUSTED;
+ } else {
+ flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+ }
+
// Sometimes users can have sensitive information in system decoration windows. An app
// could create a virtual display with system decorations support and read the user info
// from the surface.
// We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
- // to virtual displays that are owned by the system.
- if (callingUid != Process.SYSTEM_UID
- && (flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
- if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
+ // to trusted virtual displays.
+ final int trustedDisplayWithSysDecorFlag =
+ (VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ | VIRTUAL_DISPLAY_FLAG_TRUSTED);
+ if ((flags & trustedDisplayWithSysDecorFlag)
+ == VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+ && !checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) {
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
- }
}
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 2a65b33461cf..2c08420af42d 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -577,6 +577,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_hdmi_display_name);
}
+ // The display is trusted since it is created by system.
+ mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
}
return mInfo;
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0261f388f7cb..8556f084a072 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -269,6 +269,9 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_TRUSTED) != 0) {
+ mBaseDisplayInfo.flags |= Display.FLAG_TRUSTED;
+ }
Rect maskingInsets = getMaskingInsets(deviceInfo);
int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 8fb384070e25..69943e3904ed 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
+
import android.annotation.Nullable;
import android.content.Context;
import android.database.ContentObserver;
@@ -356,6 +358,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
mInfo.type = Display.TYPE_OVERLAY;
mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
mInfo.state = mState;
+ // The display is trusted since it is created by system.
+ mInfo.flags |= FLAG_TRUSTED;
}
return mInfo;
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index ccd88483593a..210d2979c807 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -25,6 +25,9 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTAT
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+
+import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
@@ -412,6 +415,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
}
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+ mInfo.flags |= FLAG_TRUSTED;
+ }
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 5584dcf69f50..57323170b327 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -651,6 +651,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
mInfo.address = mAddress;
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
+ // The display is trusted since it is created by system.
+ mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
}
return mInfo;
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 769956d797b0..e3eeb6c41d9f 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -47,6 +47,10 @@ import android.service.dreams.IDreamManager;
import android.util.Slog;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -77,6 +81,8 @@ public final class DreamManagerService extends SystemService {
private final PowerManagerInternal mPowerManagerInternal;
private final PowerManager.WakeLock mDozeWakeLock;
private final ActivityTaskManagerInternal mAtmInternal;
+ private final UiEventLogger mUiEventLogger;
+ private final ComponentName mAmbientDisplayComponent;
private Binder mCurrentDreamToken;
private ComponentName mCurrentDreamName;
@@ -91,6 +97,26 @@ public final class DreamManagerService extends SystemService {
private AmbientDisplayConfiguration mDozeConfig;
+ @VisibleForTesting
+ public enum DreamManagerEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The screensaver has started.")
+ DREAM_START(577),
+
+ @UiEvent(doc = "The screensaver has stopped.")
+ DREAM_STOP(578);
+
+ private final int mId;
+
+ DreamManagerEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
public DreamManagerService(Context context) {
super(context);
mContext = context;
@@ -102,6 +128,9 @@ public final class DreamManagerService extends SystemService {
mAtmInternal = getLocalService(ActivityTaskManagerInternal.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
+ mUiEventLogger = new UiEventLoggerImpl();
+ AmbientDisplayConfiguration adc = new AmbientDisplayConfiguration(mContext);
+ mAmbientDisplayComponent = ComponentName.unflattenFromString(adc.ambientDisplayComponent());
}
@Override
@@ -388,6 +417,9 @@ public final class DreamManagerService extends SystemService {
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
mHandler.post(wakeLock.wrap(() -> {
mAtmInternal.notifyDreamStateChanged(true);
+ if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
+ mUiEventLogger.log(DreamManagerEvent.DREAM_START);
+ }
mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock);
}));
}
@@ -415,6 +447,9 @@ public final class DreamManagerService extends SystemService {
}
private void cleanupDreamLocked() {
+ if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
+ mUiEventLogger.log(DreamManagerEvent.DREAM_STOP);
+ }
mCurrentDreamToken = null;
mCurrentDreamName = null;
mCurrentDreamIsTest = false;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b9669c74a6df..87a908c10721 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -3142,7 +3142,7 @@ public class HdmiControlService extends SystemService {
return;
}
- setHdmiCecVolumeControlEnabled(false);
+ mHdmiCecVolumeControlEnabled = false;
// Call the vendor handler before the service is disabled.
invokeVendorCommandListenersOnControlStateChanged(false,
HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 65a13016c9b6..b647a1ab9873 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -207,6 +207,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
static final int MSG_INITIALIZE_IME = 1040;
static final int MSG_CREATE_SESSION = 1050;
+ static final int MSG_REMOVE_IME_SURFACE = 1060;
static final int MSG_START_INPUT = 2000;
@@ -3946,6 +3947,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
+ public void removeImeSurface() {
+ mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -4216,6 +4223,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
args.recycle();
return true;
}
+ case MSG_REMOVE_IME_SURFACE: {
+ try {
+ if (mEnabledSession != null && mEnabledSession.session != null) {
+ mEnabledSession.session.removeImeSurface();
+ }
+ } catch (RemoteException e) {
+ }
+ return true;
+ }
// ---------------------------------------------------------
case MSG_START_INPUT: {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6c415ca326c6..33c78e403ce3 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1463,6 +1463,12 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
+ public void removeImeSurface() {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
public boolean showSoftInput(
IInputMethodClient client, IBinder token, int flags,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 176ec3f003ea..5933723f01b6 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -71,6 +71,9 @@ public abstract class MediaKeyDispatcher {
mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_STOP, 0);
mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_NEXT, 0);
mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_DOWN, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_UP, 0);
+ mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_MUTE, 0);
}
// TODO: Move this method into SessionPolicyProvider.java for better readability.
@@ -126,6 +129,9 @@ public abstract class MediaKeyDispatcher {
* <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
* <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
+ * <li> {@link KeyEvent#KEYCODE_VOLUME_UP}
+ * <li> {@link KeyEvent#KEYCODE_VOLUME_DOWN}
+ * <li> {@link KeyEvent#KEYCODE_VOLUME_MUTE}
* </ul>
* @see {@link KeyEvent#isMediaSessionKey(int)}
*/
@@ -164,6 +170,9 @@ public abstract class MediaKeyDispatcher {
* <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
* <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
* <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
+ * <li> {@link KeyEvent#KEYCODE_VOLUME_DOWN}
+ * <li> {@link KeyEvent#KEYCODE_VOLUME_UP}
+ * <li> {@link KeyEvent#KEYCODE_VOLUME_MUTE}
* </ul>
* @see {@link KeyEvent#isMediaSessionKey(int)}
* @param keyCode
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index afae20dcf525..96250411f984 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -830,9 +830,6 @@ public class MediaSessionService extends SystemService implements Monitor {
private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
private int mOnVolumeKeyLongPressListenerUid;
- private KeyEvent mInitialDownVolumeKeyEvent;
- private int mInitialDownVolumeStream;
- private boolean mInitialDownMusicOnly;
private IOnMediaKeyListener mOnMediaKeyListener;
private int mOnMediaKeyListenerUid;
@@ -1104,12 +1101,10 @@ public class MediaSessionService extends SystemService implements Monitor {
"android.media.AudioService.WAKELOCK_ACQUIRED";
private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
- private KeyEvent mTrackingFirstDownKeyEvent = null;
- private boolean mIsLongPressing = false;
- private Runnable mLongPressTimeoutRunnable = null;
- private int mMultiTapCount = 0;
- private int mMultiTapKeyCode = 0;
- private Runnable mMultiTapTimeoutRunnable = null;
+ private KeyEventHandler mMediaKeyEventHandler =
+ new KeyEventHandler(KeyEventHandler.KEY_TYPE_MEDIA);
+ private KeyEventHandler mVolumeKeyEventHandler =
+ new KeyEventHandler(KeyEventHandler.KEY_TYPE_VOLUME);
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
@@ -1387,8 +1382,8 @@ public class MediaSessionService extends SystemService implements Monitor {
dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
keyEvent, needWakeLock);
} else {
- handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
- needWakeLock);
+ mMediaKeyEventHandler.handleMediaKeyEventLocked(packageName, pid, uid,
+ asSystemService, keyEvent, needWakeLock);
}
}
} finally {
@@ -1710,53 +1705,14 @@ public class MediaSessionService extends SystemService implements Monitor {
try {
synchronized (mLock) {
- if (isGlobalPriorityActiveLocked()
- || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+ if (isGlobalPriorityActiveLocked()) {
dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
asSystemService, keyEvent, stream, musicOnly);
} else {
// TODO: Consider the case when both volume up and down keys are pressed
// at the same time.
- if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
- if (keyEvent.getRepeatCount() == 0) {
- // Keeps the copy of the KeyEvent because it can be reused.
- mCurrentFullUserRecord.mInitialDownVolumeKeyEvent =
- KeyEvent.obtain(keyEvent);
- mCurrentFullUserRecord.mInitialDownVolumeStream = stream;
- mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly;
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(
- MessageHandler.MSG_VOLUME_INITIAL_DOWN,
- mCurrentFullUserRecord.mFullUserId, 0),
- LONG_PRESS_TIMEOUT);
- }
- if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
- mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
- if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) {
- dispatchVolumeKeyLongPressLocked(
- mCurrentFullUserRecord.mInitialDownVolumeKeyEvent);
- // Mark that the key is already handled.
- mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null;
- }
- dispatchVolumeKeyLongPressLocked(keyEvent);
- }
- } else { // if up
- mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN);
- if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null
- && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent
- .getDownTime() == keyEvent.getDownTime()) {
- // Short-press. Should change volume.
- dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
- asSystemService,
- mCurrentFullUserRecord.mInitialDownVolumeKeyEvent,
- mCurrentFullUserRecord.mInitialDownVolumeStream,
- mCurrentFullUserRecord.mInitialDownMusicOnly);
- dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
- asSystemService, keyEvent, stream, musicOnly);
- } else {
- dispatchVolumeKeyLongPressLocked(keyEvent);
- }
- }
+ mVolumeKeyEventHandler.handleVolumeKeyEventLocked(packageName, pid, uid,
+ asSystemService, keyEvent, opPackageName, stream, musicOnly);
}
}
} finally {
@@ -2136,266 +2092,6 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- // A long press is determined by:
- // 1) A KeyEvent.ACTION_DOWN KeyEvent and repeat count of 0, followed by
- // 2) A KeyEvent.ACTION_DOWN KeyEvent with the same key code, a repeat count of 1, and
- // FLAG_LONG_PRESS received within ViewConfiguration.getLongPressTimeout().
- // A tap is determined by:
- // 1) A KeyEvent.ACTION_DOWN KeyEvent followed by
- // 2) A KeyEvent.ACTION_UP KeyEvent with the same key code.
- private void handleKeyEventLocked(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
- if (keyEvent.isCanceled()) {
- return;
- }
-
- int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0
- : mCustomMediaKeyDispatcher.getOverriddenKeyEvents().get(keyEvent.getKeyCode());
- cancelTrackingIfNeeded(packageName, pid, uid, asSystemService, keyEvent, needWakeLock,
- overriddenKeyEvents);
- if (!needTracking(keyEvent, overriddenKeyEvents)) {
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
- needWakeLock);
- return;
- }
-
- if (isFirstDownKeyEvent(keyEvent)) {
- mTrackingFirstDownKeyEvent = keyEvent;
- mIsLongPressing = false;
- return;
- }
-
- // Long press is always overridden here, otherwise the key event would have been already
- // handled
- if (isFirstLongPressKeyEvent(keyEvent)) {
- mIsLongPressing = true;
- }
- if (mIsLongPressing) {
- handleLongPressLocked(keyEvent, needWakeLock, overriddenKeyEvents);
- return;
- }
-
- if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
- mTrackingFirstDownKeyEvent = null;
- if (shouldTrackForMultipleTapsLocked(overriddenKeyEvents)) {
- if (mMultiTapCount == 0) {
- mMultiTapTimeoutRunnable = createSingleTapRunnable(packageName, pid, uid,
- asSystemService, keyEvent, needWakeLock,
- isSingleTapOverridden(overriddenKeyEvents));
- if (isSingleTapOverridden(overriddenKeyEvents)
- && !isDoubleTapOverridden(overriddenKeyEvents)
- && !isTripleTapOverridden(overriddenKeyEvents)) {
- mMultiTapTimeoutRunnable.run();
- } else {
- mHandler.postDelayed(mMultiTapTimeoutRunnable,
- MULTI_TAP_TIMEOUT);
- mMultiTapCount = 1;
- mMultiTapKeyCode = keyEvent.getKeyCode();
- }
- } else if (mMultiTapCount == 1) {
- mHandler.removeCallbacks(mMultiTapTimeoutRunnable);
- mMultiTapTimeoutRunnable = createDoubleTapRunnable(packageName, pid, uid,
- asSystemService, keyEvent, needWakeLock,
- isSingleTapOverridden(overriddenKeyEvents),
- isDoubleTapOverridden(overriddenKeyEvents));
- if (isTripleTapOverridden(overriddenKeyEvents)) {
- mHandler.postDelayed(mMultiTapTimeoutRunnable, MULTI_TAP_TIMEOUT);
- mMultiTapCount = 2;
- } else {
- mMultiTapTimeoutRunnable.run();
- }
- } else if (mMultiTapCount == 2) {
- mHandler.removeCallbacks(mMultiTapTimeoutRunnable);
- onTripleTap(keyEvent);
- }
- } else {
- dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- }
- }
- }
-
- private boolean shouldTrackForMultipleTapsLocked(int overriddenKeyEvents) {
- return isSingleTapOverridden(overriddenKeyEvents)
- || isDoubleTapOverridden(overriddenKeyEvents)
- || isTripleTapOverridden(overriddenKeyEvents);
- }
-
- private void cancelTrackingIfNeeded(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
- int overriddenKeyEvents) {
- if (mTrackingFirstDownKeyEvent == null && mMultiTapTimeoutRunnable == null) {
- return;
- }
-
- if (isFirstDownKeyEvent(keyEvent)) {
- if (mLongPressTimeoutRunnable != null) {
- mHandler.removeCallbacks(mLongPressTimeoutRunnable);
- mLongPressTimeoutRunnable.run();
- }
- if (mMultiTapTimeoutRunnable != null && keyEvent.getKeyCode() != mMultiTapKeyCode) {
- runExistingMultiTapRunnableLocked();
- }
- resetLongPressTracking();
- return;
- }
-
- if (mTrackingFirstDownKeyEvent != null
- && mTrackingFirstDownKeyEvent.getDownTime() == keyEvent.getDownTime()
- && mTrackingFirstDownKeyEvent.getKeyCode() == keyEvent.getKeyCode()
- && keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
- if (isFirstLongPressKeyEvent(keyEvent)) {
- if (mMultiTapTimeoutRunnable != null) {
- runExistingMultiTapRunnableLocked();
- }
- if ((overriddenKeyEvents & KEY_EVENT_LONG_PRESS) == 0
- && !isVoiceKey(keyEvent.getKeyCode())) {
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- mTrackingFirstDownKeyEvent, needWakeLock);
- mTrackingFirstDownKeyEvent = null;
- }
- } else if (keyEvent.getRepeatCount() > 1 && !mIsLongPressing) {
- resetLongPressTracking();
- }
- }
- }
-
- private boolean needTracking(KeyEvent keyEvent, int overriddenKeyEvents) {
- if (!isFirstDownKeyEvent(keyEvent)) {
- if (mTrackingFirstDownKeyEvent == null) {
- return false;
- } else if (mTrackingFirstDownKeyEvent.getDownTime() != keyEvent.getDownTime()
- || mTrackingFirstDownKeyEvent.getKeyCode() != keyEvent.getKeyCode()) {
- return false;
- }
- }
- if (overriddenKeyEvents == 0 && !isVoiceKey(keyEvent.getKeyCode())) {
- return false;
- }
- return true;
- }
-
- private void runExistingMultiTapRunnableLocked() {
- mHandler.removeCallbacks(mMultiTapTimeoutRunnable);
- mMultiTapTimeoutRunnable.run();
- }
-
- private void resetMultiTapTrackingLocked() {
- mMultiTapCount = 0;
- mMultiTapTimeoutRunnable = null;
- mMultiTapKeyCode = 0;
- }
-
- private void handleLongPressLocked(KeyEvent keyEvent, boolean needWakeLock,
- int overriddenKeyEvents) {
- if (mCustomMediaKeyDispatcher != null
- && isLongPressOverridden(overriddenKeyEvents)) {
- mCustomMediaKeyDispatcher.onLongPress(keyEvent);
-
- if (mLongPressTimeoutRunnable != null) {
- mHandler.removeCallbacks(mLongPressTimeoutRunnable);
- }
- if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
- if (mLongPressTimeoutRunnable == null) {
- mLongPressTimeoutRunnable = createLongPressTimeoutRunnable(keyEvent);
- }
- mHandler.postDelayed(mLongPressTimeoutRunnable, LONG_PRESS_TIMEOUT);
- } else {
- resetLongPressTracking();
- }
- } else if (isFirstLongPressKeyEvent(keyEvent) && isVoiceKey(keyEvent.getKeyCode())) {
- // Default implementation
- startVoiceInput(needWakeLock);
- resetLongPressTracking();
- }
- }
-
- private Runnable createLongPressTimeoutRunnable(KeyEvent keyEvent) {
- return new Runnable() {
- @Override
- public void run() {
- if (mCustomMediaKeyDispatcher != null) {
- mCustomMediaKeyDispatcher.onLongPress(createCanceledKeyEvent(keyEvent));
- }
- resetLongPressTracking();
- }
- };
- }
-
- private void resetLongPressTracking() {
- mTrackingFirstDownKeyEvent = null;
- mIsLongPressing = false;
- mLongPressTimeoutRunnable = null;
- }
-
- private KeyEvent createCanceledKeyEvent(KeyEvent keyEvent) {
- KeyEvent upEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP);
- return KeyEvent.changeTimeRepeat(upEvent, System.currentTimeMillis(), 0,
- KeyEvent.FLAG_CANCELED);
- }
-
- private boolean isFirstLongPressKeyEvent(KeyEvent keyEvent) {
- return ((keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0)
- && keyEvent.getRepeatCount() == 1;
- }
-
- private boolean isFirstDownKeyEvent(KeyEvent keyEvent) {
- return keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getRepeatCount() == 0;
- }
-
- private void dispatchDownAndUpKeyEventsLocked(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
- KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- downEvent, needWakeLock);
- dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- }
-
- Runnable createSingleTapRunnable(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
- boolean overridden) {
- return new Runnable() {
- @Override
- public void run() {
- resetMultiTapTrackingLocked();
- if (overridden) {
- mCustomMediaKeyDispatcher.onSingleTap(keyEvent);
- } else {
- dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- }
- }
- };
- };
-
- Runnable createDoubleTapRunnable(String packageName, int pid, int uid,
- boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
- boolean singleTapOverridden, boolean doubleTapOverridden) {
- return new Runnable() {
- @Override
- public void run() {
- resetMultiTapTrackingLocked();
- if (doubleTapOverridden) {
- mCustomMediaKeyDispatcher.onDoubleTap(keyEvent);
- } else if (singleTapOverridden) {
- mCustomMediaKeyDispatcher.onSingleTap(keyEvent);
- mCustomMediaKeyDispatcher.onSingleTap(keyEvent);
- } else {
- dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
- keyEvent, needWakeLock);
- }
- }
- };
- };
-
- private void onTripleTap(KeyEvent keyEvent) {
- resetMultiTapTrackingLocked();
- mCustomMediaKeyDispatcher.onTripleTap(keyEvent);
- }
-
private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid,
boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
if (mCurrentFullUserRecord.getMediaButtonSessionLocked()
@@ -2579,8 +2275,8 @@ public class MediaSessionService extends SystemService implements Monitor {
dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
mKeyEvent, mNeedWakeLock);
} else {
- handleKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService,
- mKeyEvent, mNeedWakeLock);
+ mMediaKeyEventHandler.handleMediaKeyEventLocked(mPackageName, mPid, mUid,
+ mAsSystemService, mKeyEvent, mNeedWakeLock);
}
}
}
@@ -2655,12 +2351,338 @@ public class MediaSessionService extends SystemService implements Monitor {
onReceiveResult(resultCode, null);
}
};
+
+ // A long press is determined by:
+ // 1) A KeyEvent.ACTION_DOWN KeyEvent and repeat count of 0, followed by
+ // 2) A KeyEvent.ACTION_DOWN KeyEvent with the same key code, a repeat count of 1, and
+ // FLAG_LONG_PRESS received within ViewConfiguration.getLongPressTimeout().
+ // A tap is determined by:
+ // 1) A KeyEvent.ACTION_DOWN KeyEvent followed by
+ // 2) A KeyEvent.ACTION_UP KeyEvent with the same key code.
+ class KeyEventHandler {
+ private static final int KEY_TYPE_MEDIA = 0;
+ private static final int KEY_TYPE_VOLUME = 1;
+
+ private KeyEvent mTrackingFirstDownKeyEvent;
+ private boolean mIsLongPressing;
+ private Runnable mLongPressTimeoutRunnable;
+ private int mMultiTapCount;
+ private Runnable mMultiTapTimeoutRunnable;
+ private int mMultiTapKeyCode;
+ private int mKeyType;
+
+ KeyEventHandler(int keyType) {
+ mKeyType = keyType;
+ }
+
+ void handleMediaKeyEventLocked(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
+ handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, needWakeLock,
+ null, 0, false);
+ }
+
+ void handleVolumeKeyEventLocked(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, String opPackageName, int stream,
+ boolean musicOnly) {
+ handleKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, false,
+ opPackageName, stream, musicOnly);
+ }
+
+ void handleKeyEventLocked(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
+ String opPackageName, int stream, boolean musicOnly) {
+ if (keyEvent.isCanceled()) {
+ return;
+ }
+
+ int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0
+ : mCustomMediaKeyDispatcher.getOverriddenKeyEvents()
+ .get(keyEvent.getKeyCode());
+ cancelTrackingIfNeeded(packageName, pid, uid, asSystemService, keyEvent,
+ needWakeLock, opPackageName, stream, musicOnly, overriddenKeyEvents);
+ if (!needTracking(keyEvent, overriddenKeyEvents)) {
+ if (mKeyType == KEY_TYPE_VOLUME) {
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+ asSystemService, keyEvent, stream, musicOnly);
+ } else {
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock);
+ }
+ return;
+ }
+
+ if (isFirstDownKeyEvent(keyEvent)) {
+ mTrackingFirstDownKeyEvent = keyEvent;
+ mIsLongPressing = false;
+ return;
+ }
+
+ // Long press is always overridden here, otherwise the key event would have been
+ // already handled
+ if (isFirstLongPressKeyEvent(keyEvent)) {
+ mIsLongPressing = true;
+ }
+ if (mIsLongPressing) {
+ handleLongPressLocked(keyEvent, needWakeLock, overriddenKeyEvents);
+ return;
+ }
+
+ if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+ mTrackingFirstDownKeyEvent = null;
+ if (shouldTrackForMultipleTapsLocked(overriddenKeyEvents)) {
+ if (mMultiTapCount == 0) {
+ mMultiTapTimeoutRunnable = createSingleTapRunnable(packageName, pid,
+ uid, asSystemService, keyEvent, needWakeLock,
+ opPackageName, stream, musicOnly,
+ isSingleTapOverridden(overriddenKeyEvents));
+ if (isSingleTapOverridden(overriddenKeyEvents)
+ && !isDoubleTapOverridden(overriddenKeyEvents)
+ && !isTripleTapOverridden(overriddenKeyEvents)) {
+ mMultiTapTimeoutRunnable.run();
+ } else {
+ mHandler.postDelayed(mMultiTapTimeoutRunnable,
+ MULTI_TAP_TIMEOUT);
+ mMultiTapCount = 1;
+ mMultiTapKeyCode = keyEvent.getKeyCode();
+ }
+ } else if (mMultiTapCount == 1) {
+ mHandler.removeCallbacks(mMultiTapTimeoutRunnable);
+ mMultiTapTimeoutRunnable = createDoubleTapRunnable(packageName, pid,
+ uid, asSystemService, keyEvent, needWakeLock, opPackageName,
+ stream, musicOnly, isSingleTapOverridden(overriddenKeyEvents),
+ isDoubleTapOverridden(overriddenKeyEvents));
+ if (isTripleTapOverridden(overriddenKeyEvents)) {
+ mHandler.postDelayed(mMultiTapTimeoutRunnable, MULTI_TAP_TIMEOUT);
+ mMultiTapCount = 2;
+ } else {
+ mMultiTapTimeoutRunnable.run();
+ }
+ } else if (mMultiTapCount == 2) {
+ mHandler.removeCallbacks(mMultiTapTimeoutRunnable);
+ onTripleTap(keyEvent);
+ }
+ } else {
+ dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock, opPackageName, stream, musicOnly);
+ }
+ }
+ }
+
+ private boolean shouldTrackForMultipleTapsLocked(int overriddenKeyEvents) {
+ return isSingleTapOverridden(overriddenKeyEvents)
+ || isDoubleTapOverridden(overriddenKeyEvents)
+ || isTripleTapOverridden(overriddenKeyEvents);
+ }
+
+ private void cancelTrackingIfNeeded(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
+ String opPackageName, int stream, boolean musicOnly, int overriddenKeyEvents) {
+ if (mTrackingFirstDownKeyEvent == null && mMultiTapTimeoutRunnable == null) {
+ return;
+ }
+
+ if (isFirstDownKeyEvent(keyEvent)) {
+ if (mLongPressTimeoutRunnable != null) {
+ mHandler.removeCallbacks(mLongPressTimeoutRunnable);
+ mLongPressTimeoutRunnable.run();
+ }
+ if (mMultiTapTimeoutRunnable != null
+ && keyEvent.getKeyCode() != mMultiTapKeyCode) {
+ runExistingMultiTapRunnableLocked();
+ }
+ resetLongPressTracking();
+ return;
+ }
+
+ if (mTrackingFirstDownKeyEvent != null
+ && mTrackingFirstDownKeyEvent.getDownTime() == keyEvent.getDownTime()
+ && mTrackingFirstDownKeyEvent.getKeyCode() == keyEvent.getKeyCode()
+ && keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ if (isFirstLongPressKeyEvent(keyEvent)) {
+ if (mMultiTapTimeoutRunnable != null) {
+ runExistingMultiTapRunnableLocked();
+ }
+ if ((overriddenKeyEvents & KEY_EVENT_LONG_PRESS) == 0) {
+ if (mKeyType == KEY_TYPE_VOLUME) {
+ if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid,
+ uid, asSystemService, keyEvent, stream, musicOnly);
+ mTrackingFirstDownKeyEvent = null;
+ }
+ } else if (!isVoiceKey(keyEvent.getKeyCode())) {
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock);
+ mTrackingFirstDownKeyEvent = null;
+ }
+ }
+ } else if (keyEvent.getRepeatCount() > 1 && !mIsLongPressing) {
+ resetLongPressTracking();
+ }
+ }
+ }
+
+ private boolean needTracking(KeyEvent keyEvent, int overriddenKeyEvents) {
+ if (!isFirstDownKeyEvent(keyEvent)) {
+ if (mTrackingFirstDownKeyEvent == null) {
+ return false;
+ } else if (mTrackingFirstDownKeyEvent.getDownTime() != keyEvent.getDownTime()
+ || mTrackingFirstDownKeyEvent.getKeyCode() != keyEvent.getKeyCode()) {
+ return false;
+ }
+ }
+ if (overriddenKeyEvents == 0) {
+ if (mKeyType == KEY_TYPE_VOLUME) {
+ if (mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) {
+ return false;
+ }
+ } else if (!isVoiceKey(keyEvent.getKeyCode())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void runExistingMultiTapRunnableLocked() {
+ mHandler.removeCallbacks(mMultiTapTimeoutRunnable);
+ mMultiTapTimeoutRunnable.run();
+ }
+
+ private void resetMultiTapTrackingLocked() {
+ mMultiTapCount = 0;
+ mMultiTapTimeoutRunnable = null;
+ mMultiTapKeyCode = 0;
+ }
+
+ private void handleLongPressLocked(KeyEvent keyEvent, boolean needWakeLock,
+ int overriddenKeyEvents) {
+ if (mCustomMediaKeyDispatcher != null
+ && isLongPressOverridden(overriddenKeyEvents)) {
+ mCustomMediaKeyDispatcher.onLongPress(keyEvent);
+
+ if (mLongPressTimeoutRunnable != null) {
+ mHandler.removeCallbacks(mLongPressTimeoutRunnable);
+ }
+ if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ if (mLongPressTimeoutRunnable == null) {
+ mLongPressTimeoutRunnable = createLongPressTimeoutRunnable(keyEvent);
+ }
+ mHandler.postDelayed(mLongPressTimeoutRunnable, LONG_PRESS_TIMEOUT);
+ } else {
+ resetLongPressTracking();
+ }
+ } else {
+ if (mKeyType == KEY_TYPE_VOLUME) {
+ dispatchVolumeKeyLongPressLocked(keyEvent);
+ } else if (isFirstLongPressKeyEvent(keyEvent)
+ && isVoiceKey(keyEvent.getKeyCode())) {
+ // Default implementation
+ startVoiceInput(needWakeLock);
+ resetLongPressTracking();
+ }
+ }
+ }
+
+ private Runnable createLongPressTimeoutRunnable(KeyEvent keyEvent) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (mCustomMediaKeyDispatcher != null) {
+ mCustomMediaKeyDispatcher.onLongPress(createCanceledKeyEvent(keyEvent));
+ }
+ resetLongPressTracking();
+ }
+ };
+ }
+
+ private void resetLongPressTracking() {
+ mTrackingFirstDownKeyEvent = null;
+ mIsLongPressing = false;
+ mLongPressTimeoutRunnable = null;
+ }
+
+ private KeyEvent createCanceledKeyEvent(KeyEvent keyEvent) {
+ KeyEvent upEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP);
+ return KeyEvent.changeTimeRepeat(upEvent, System.currentTimeMillis(), 0,
+ KeyEvent.FLAG_CANCELED);
+ }
+
+ private boolean isFirstLongPressKeyEvent(KeyEvent keyEvent) {
+ return ((keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0)
+ && keyEvent.getRepeatCount() == 1;
+ }
+
+ private boolean isFirstDownKeyEvent(KeyEvent keyEvent) {
+ return keyEvent.getAction() == KeyEvent.ACTION_DOWN
+ && keyEvent.getRepeatCount() == 0;
+ }
+
+ private void dispatchDownAndUpKeyEventsLocked(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
+ String opPackageName, int stream, boolean musicOnly) {
+ KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
+ if (mKeyType == KEY_TYPE_VOLUME) {
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+ asSystemService, downEvent, stream, musicOnly);
+ dispatchVolumeKeyEventLocked(packageName, opPackageName, pid, uid,
+ asSystemService, keyEvent, stream, musicOnly);
+ } else {
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, downEvent,
+ needWakeLock);
+ dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent,
+ needWakeLock);
+ }
+ }
+
+ Runnable createSingleTapRunnable(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
+ String opPackageName, int stream, boolean musicOnly, boolean overridden) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ resetMultiTapTrackingLocked();
+ if (overridden) {
+ mCustomMediaKeyDispatcher.onSingleTap(keyEvent);
+ } else {
+ dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock, opPackageName, stream, musicOnly);
+ }
+ }
+ };
+ };
+
+ Runnable createDoubleTapRunnable(String packageName, int pid, int uid,
+ boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock,
+ String opPackageName, int stream, boolean musicOnly,
+ boolean singleTapOverridden, boolean doubleTapOverridden) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ resetMultiTapTrackingLocked();
+ if (doubleTapOverridden) {
+ mCustomMediaKeyDispatcher.onDoubleTap(keyEvent);
+ } else if (singleTapOverridden) {
+ mCustomMediaKeyDispatcher.onSingleTap(keyEvent);
+ mCustomMediaKeyDispatcher.onSingleTap(keyEvent);
+ } else {
+ dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock, opPackageName, stream, musicOnly);
+ dispatchDownAndUpKeyEventsLocked(packageName, pid, uid, asSystemService,
+ keyEvent, needWakeLock, opPackageName, stream, musicOnly);
+ }
+ }
+ };
+ };
+
+ private void onTripleTap(KeyEvent keyEvent) {
+ resetMultiTapTrackingLocked();
+ mCustomMediaKeyDispatcher.onTripleTap(keyEvent);
+ }
+ }
}
final class MessageHandler extends Handler {
private static final int MSG_SESSIONS_1_CHANGED = 1;
private static final int MSG_SESSIONS_2_CHANGED = 2;
- private static final int MSG_VOLUME_INITIAL_DOWN = 3;
private final SparseArray<Integer> mIntegerCache = new SparseArray<>();
@Override
@@ -2672,16 +2694,6 @@ public class MediaSessionService extends SystemService implements Monitor {
case MSG_SESSIONS_2_CHANGED:
pushSession2Changed((int) msg.obj);
break;
- case MSG_VOLUME_INITIAL_DOWN:
- synchronized (mLock) {
- FullUserRecord user = mUserRecords.get((int) msg.arg1);
- if (user != null && user.mInitialDownVolumeKeyEvent != null) {
- dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent);
- // Mark that the key is already handled.
- user.mInitialDownVolumeKeyEvent = null;
- }
- }
- break;
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelLogger.java b/services/core/java/com/android/server/notification/NotificationChannelLogger.java
index a7b18778f868..5c127c31d6c2 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelLogger.java
@@ -99,6 +99,16 @@ public interface NotificationChannelLogger {
}
/**
+ * Log blocking or unblocking of the entire app's notifications.
+ * @param uid UID of the app.
+ * @param pkg Package name of the app.
+ * @param enabled If true, notifications are now allowed.
+ */
+ default void logAppNotificationsAllowed(int uid, String pkg, boolean enabled) {
+ logAppEvent(NotificationChannelEvent.getBlocked(enabled), uid, pkg);
+ }
+
+ /**
* Low-level interface for logging events, to be implemented.
* @param event Event to log.
* @param channel Notification channel.
@@ -124,6 +134,13 @@ public interface NotificationChannelLogger {
boolean wasBlocked);
/**
+ * Low-level interface for logging app-as-a-whole events, to be implemented.
+ * @param uid UID of app.
+ * @param pkg Package of app.
+ */
+ void logAppEvent(@NonNull NotificationChannelEvent event, int uid, String pkg);
+
+ /**
* The UiEvent enums that this class can log.
*/
enum NotificationChannelEvent implements UiEventLogger.UiEventEnum {
@@ -144,8 +161,11 @@ public interface NotificationChannelLogger {
@UiEvent(doc = "System created a new conversation (sub-channel in a notification channel)")
NOTIFICATION_CHANNEL_CONVERSATION_CREATED(272),
@UiEvent(doc = "System deleted a new conversation (sub-channel in a notification channel)")
- NOTIFICATION_CHANNEL_CONVERSATION_DELETED(274);
-
+ NOTIFICATION_CHANNEL_CONVERSATION_DELETED(274),
+ @UiEvent(doc = "All notifications for the app were blocked.")
+ APP_NOTIFICATIONS_BLOCKED(557),
+ @UiEvent(doc = "Notifications for the app as a whole were unblocked.")
+ APP_NOTIFICATIONS_UNBLOCKED(558);
private final int mId;
NotificationChannelEvent(int id) {
@@ -178,6 +198,10 @@ public interface NotificationChannelLogger {
? NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_CREATED
: NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED;
}
+
+ public static NotificationChannelEvent getBlocked(boolean enabled) {
+ return enabled ? APP_NOTIFICATIONS_UNBLOCKED : APP_NOTIFICATIONS_BLOCKED;
+ }
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java
index 2f7772eec2d2..fd3dd568f634 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java
@@ -19,6 +19,8 @@ package com.android.server.notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.FrameworkStatsLog;
/**
@@ -27,6 +29,8 @@ import com.android.internal.util.FrameworkStatsLog;
* should live in the interface so it can be tested.
*/
public class NotificationChannelLoggerImpl implements NotificationChannelLogger {
+ UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+
@Override
public void logNotificationChannel(NotificationChannelEvent event,
NotificationChannel channel, int uid, String pkg,
@@ -51,4 +55,9 @@ public class NotificationChannelLoggerImpl implements NotificationChannelLogger
/* int old_importance*/ NotificationChannelLogger.getImportance(wasBlocked),
/* int importance*/ NotificationChannelLogger.getImportance(channelGroup));
}
+
+ @Override
+ public void logAppEvent(NotificationChannelEvent event, int uid, String pkg) {
+ mUiEventLogger.log(event, uid, pkg);
+ }
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index e472e3097777..afc75572ae4f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1654,6 +1654,7 @@ public class PreferencesHelper implements RankingConfig {
}
setImportance(packageName, uid,
enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
+ mNotificationChannelLogger.logAppNotificationsAllowed(uid, packageName, enabled);
}
/**
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 5b9db64dd9b1..ccda5875d9fa 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -19,6 +19,8 @@ package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +29,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
+import android.content.pm.UserInfo;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -108,12 +111,25 @@ public class AppsFilter {
private final boolean mSystemAppsQueryable;
private final FeatureConfig mFeatureConfig;
-
private final OverlayReferenceMapper mOverlayReferenceMapper;
+ private final StateProvider mStateProvider;
+
private PackageParser.SigningDetails mSystemSigningDetails;
private Set<String> mProtectedBroadcasts = new ArraySet<>();
- AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
+ /**
+ * This structure maps uid -> uid and indicates whether access from the first should be
+ * filtered to the second. It's essentially a cache of the
+ * {@link #shouldFilterApplicationInternal(int, SettingBase, PackageSetting, int)} call.
+ * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on
+ * initial scam and is null until {@link #onSystemReady()} is called.
+ */
+ private volatile SparseArray<SparseBooleanArray> mShouldFilterCache;
+
+ @VisibleForTesting(visibility = PRIVATE)
+ AppsFilter(StateProvider stateProvider,
+ FeatureConfig featureConfig,
+ String[] forceQueryableWhitelist,
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider) {
mFeatureConfig = featureConfig;
@@ -121,8 +137,23 @@ public class AppsFilter {
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
+ mStateProvider = stateProvider;
}
+ /**
+ * Provides system state to AppsFilter via {@link CurrentStateCallback} after properly guarding
+ * the data with the package lock.
+ */
+ @VisibleForTesting(visibility = PRIVATE)
+ public interface StateProvider {
+ void runWithState(CurrentStateCallback callback);
+
+ interface CurrentStateCallback {
+ void currentState(ArrayMap<String, PackageSetting> settings, UserInfo[] users);
+ }
+ }
+
+ @VisibleForTesting(visibility = PRIVATE)
public interface FeatureConfig {
/** Called when the system is ready and components can be queried. */
@@ -139,6 +170,7 @@ public class AppsFilter {
/**
* Turns on logging for the given appId
+ *
* @param enable true if logging should be enabled, false if disabled.
*/
void enableLogging(int appId, boolean enable);
@@ -146,6 +178,7 @@ public class AppsFilter {
/**
* Initializes the package enablement state for the given package. This gives opportunity
* to do any expensive operations ahead of the actual checks.
+ *
* @param removed true if adding, false if removing
*/
void updatePackageState(PackageSetting setting, boolean removed);
@@ -161,6 +194,7 @@ public class AppsFilter {
@Nullable
private SparseBooleanArray mLoggingEnabled = null;
+ private AppsFilter mAppsFilter;
private FeatureConfigImpl(
PackageManagerInternal pmInternal, PackageManagerService.Injector injector) {
@@ -168,6 +202,10 @@ public class AppsFilter {
mInjector = injector;
}
+ public void setAppsFilter(AppsFilter filter) {
+ mAppsFilter = filter;
+ }
+
@Override
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
@@ -235,11 +273,12 @@ public class AppsFilter {
@Override
public void onCompatChange(String packageName) {
updateEnabledState(mPmInternal.getPackage(packageName));
+ mAppsFilter.updateShouldFilterCacheForPackage(packageName);
}
private void updateEnabledState(AndroidPackage pkg) {
// TODO(b/135203078): Do not use toAppInfo
- final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
+ final boolean enabled = mInjector.getCompatibility().isChangeEnabled(
PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
if (enabled) {
mDisabledPackages.remove(pkg.getPackageName());
@@ -267,7 +306,7 @@ public class AppsFilter {
final boolean forceSystemAppsQueryable =
injector.getContext().getResources()
.getBoolean(R.bool.config_forceSystemPackagesQueryable);
- final FeatureConfig featureConfig = new FeatureConfigImpl(pms, injector);
+ final FeatureConfigImpl featureConfig = new FeatureConfigImpl(pms, injector);
final String[] forcedQueryablePackageNames;
if (forceSystemAppsQueryable) {
// all system apps already queryable, no need to read and parse individual exceptions
@@ -280,8 +319,16 @@ public class AppsFilter {
forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
}
}
- return new AppsFilter(featureConfig, forcedQueryablePackageNames,
- forceSystemAppsQueryable, null);
+ final StateProvider stateProvider = command -> {
+ synchronized (injector.getLock()) {
+ command.currentState(injector.getSettings().mPackages,
+ injector.getUserManagerInternal().getUserInfos());
+ }
+ };
+ AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
+ forcedQueryablePackageNames, forceSystemAppsQueryable, null);
+ featureConfig.setAppsFilter(appsFilter);
+ return appsFilter;
}
public FeatureConfig getFeatureConfig() {
@@ -404,27 +451,59 @@ public class AppsFilter {
* visibility of the caller from the target.
*
* @param recipientUid the uid gaining visibility of the {@code visibleUid}.
- * @param visibleUid the uid becoming visible to the {@recipientUid}
+ * @param visibleUid the uid becoming visible to the {@recipientUid}
*/
public void grantImplicitAccess(int recipientUid, int visibleUid) {
- if (recipientUid != visibleUid
- && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
- Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
+ if (recipientUid != visibleUid) {
+ if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
+ Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
+ }
+ if (mShouldFilterCache != null) {
+ // update the cache in a one-off manner since we've got all the information we need.
+ SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
+ if (visibleUids == null) {
+ visibleUids = new SparseBooleanArray();
+ mShouldFilterCache.put(recipientUid, visibleUids);
+ }
+ visibleUids.put(visibleUid, false);
+ }
}
}
public void onSystemReady() {
+ mStateProvider.runWithState(new StateProvider.CurrentStateCallback() {
+ @Override
+ public void currentState(ArrayMap<String, PackageSetting> settings,
+ UserInfo[] users) {
+ mShouldFilterCache = new SparseArray<>(users.length * settings.size());
+ }
+ });
mFeatureConfig.onSystemReady();
mOverlayReferenceMapper.rebuildIfDeferred();
+ updateEntireShouldFilterCache();
}
/**
* Adds a package that should be considered when filtering visibility between apps.
*
- * @param newPkgSetting the new setting being added
- * @param existingSettings all other settings currently on the device.
+ * @param newPkgSetting the new setting being added
*/
- public void addPackage(PackageSetting newPkgSetting,
+ public void addPackage(PackageSetting newPkgSetting) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
+ try {
+ mStateProvider.runWithState((settings, users) -> {
+ addPackageInternal(newPkgSetting, settings);
+ if (mShouldFilterCache != null) {
+ updateShouldFilterCacheForPackage(
+ null, newPkgSetting, settings, users, settings.size());
+ } // else, rebuild entire cache when system is ready
+ });
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private void addPackageInternal(PackageSetting newPkgSetting,
ArrayMap<String, PackageSetting> existingSettings) {
if (Objects.equals("android", newPkgSetting.name)) {
// let's set aside the framework signatures
@@ -438,79 +517,153 @@ public class AppsFilter {
}
}
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
- try {
- final AndroidPackage newPkg = newPkgSetting.pkg;
- if (newPkg == null) {
- // nothing to add
- return;
- }
+ final AndroidPackage newPkg = newPkgSetting.pkg;
+ if (newPkg == null) {
+ // nothing to add
+ return;
+ }
- if (!newPkg.getProtectedBroadcasts().isEmpty()) {
- mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
- recomputeComponentVisibility(existingSettings, newPkg.getPackageName());
- }
+ if (!newPkg.getProtectedBroadcasts().isEmpty()) {
+ mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
+ recomputeComponentVisibility(existingSettings, newPkg.getPackageName());
+ }
- final boolean newIsForceQueryable =
- mForceQueryable.contains(newPkgSetting.appId)
- /* shared user that is already force queryable */
- || newPkg.isForceQueryable()
- || newPkgSetting.forceQueryableOverride
- || (newPkgSetting.isSystem() && (mSystemAppsQueryable
- || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
- newPkg.getPackageName())));
- if (newIsForceQueryable
- || (mSystemSigningDetails != null
- && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
- mForceQueryable.add(newPkgSetting.appId);
- }
+ final boolean newIsForceQueryable =
+ mForceQueryable.contains(newPkgSetting.appId)
+ /* shared user that is already force queryable */
+ || newPkg.isForceQueryable()
+ || newPkgSetting.forceQueryableOverride
+ || (newPkgSetting.isSystem() && (mSystemAppsQueryable
+ || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
+ newPkg.getPackageName())));
+ if (newIsForceQueryable
+ || (mSystemSigningDetails != null
+ && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
+ mForceQueryable.add(newPkgSetting.appId);
+ }
- for (int i = existingSettings.size() - 1; i >= 0; i--) {
- final PackageSetting existingSetting = existingSettings.valueAt(i);
- if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) {
- continue;
+ for (int i = existingSettings.size() - 1; i >= 0; i--) {
+ final PackageSetting existingSetting = existingSettings.valueAt(i);
+ if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) {
+ continue;
+ }
+ final AndroidPackage existingPkg = existingSetting.pkg;
+ // let's evaluate the ability of already added packages to see this new package
+ if (!newIsForceQueryable) {
+ if (canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) {
+ mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
}
- final AndroidPackage existingPkg = existingSetting.pkg;
- // let's evaluate the ability of already added packages to see this new package
- if (!newIsForceQueryable) {
- if (canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
- }
- if (canQueryViaPackage(existingPkg, newPkg)
- || canQueryAsInstaller(existingSetting, newPkg)) {
- mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
- }
+ if (canQueryViaPackage(existingPkg, newPkg)
+ || canQueryAsInstaller(existingSetting, newPkg)) {
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
- // now we'll evaluate our new package's ability to see existing packages
- if (!mForceQueryable.contains(existingSetting.appId)) {
- if (canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
- }
- if (canQueryViaPackage(newPkg, existingPkg)
- || canQueryAsInstaller(newPkgSetting, existingPkg)) {
- mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
- }
+ }
+ // now we'll evaluate our new package's ability to see existing packages
+ if (!mForceQueryable.contains(existingSetting.appId)) {
+ if (canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) {
+ mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
}
- // if either package instruments the other, mark both as visible to one another
- if (pkgInstruments(newPkgSetting, existingSetting)
- || pkgInstruments(existingSetting, newPkgSetting)) {
+ if (canQueryViaPackage(newPkg, existingPkg)
+ || canQueryAsInstaller(newPkgSetting, existingPkg)) {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
- mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
}
+ // if either package instruments the other, mark both as visible to one another
+ if (pkgInstruments(newPkgSetting, existingSetting)
+ || pkgInstruments(existingSetting, newPkgSetting)) {
+ mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ }
+ }
+
+ int existingSize = existingSettings.size();
+ ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
+ for (int index = 0; index < existingSize; index++) {
+ PackageSetting pkgSetting = existingSettings.valueAt(index);
+ if (pkgSetting.pkg != null) {
+ existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
+ }
+ }
+ mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
+ mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
+ }
- int existingSize = existingSettings.size();
- ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
- for (int index = 0; index < existingSize; index++) {
- PackageSetting pkgSetting = existingSettings.valueAt(index);
- if (pkgSetting.pkg != null) {
- existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
+ private void removeAppIdFromVisibilityCache(int appId) {
+ if (mShouldFilterCache == null) {
+ return;
+ }
+ for (int i = mShouldFilterCache.size() - 1; i >= 0; i--) {
+ if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) {
+ mShouldFilterCache.removeAt(i);
+ continue;
+ }
+ SparseBooleanArray targetSparseArray = mShouldFilterCache.valueAt(i);
+ for (int j = targetSparseArray.size() - 1; j >= 0; j--) {
+ if (UserHandle.getAppId(targetSparseArray.keyAt(j)) == appId) {
+ targetSparseArray.removeAt(j);
+ }
+ }
+ }
+ }
+
+ private void updateEntireShouldFilterCache() {
+ mStateProvider.runWithState((settings, users) -> {
+ mShouldFilterCache.clear();
+ for (int i = settings.size() - 1; i >= 0; i--) {
+ updateShouldFilterCacheForPackage(
+ null /*skipPackage*/, settings.valueAt(i), settings, users, i);
+ }
+ });
+ }
+
+ public void onUsersChanged() {
+ if (mShouldFilterCache != null) {
+ updateEntireShouldFilterCache();
+ }
+ }
+
+ private void updateShouldFilterCacheForPackage(String packageName) {
+ mStateProvider.runWithState((settings, users) -> {
+ updateShouldFilterCacheForPackage(null /* skipPackage */, settings.get(packageName),
+ settings, users, settings.size() /*maxIndex*/);
+ });
+
+ }
+
+ private void updateShouldFilterCacheForPackage(@Nullable String skipPackageName,
+ PackageSetting subjectSetting, ArrayMap<String, PackageSetting> allSettings,
+ UserInfo[] allUsers, int maxIndex) {
+ for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
+ PackageSetting otherSetting = allSettings.valueAt(i);
+ if (subjectSetting.appId == otherSetting.appId) {
+ continue;
+ }
+ //noinspection StringEquality
+ if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) {
+ continue;
+ }
+ final int userCount = allUsers.length;
+ final int appxUidCount = userCount * allSettings.size();
+ for (int su = 0; su < userCount; su++) {
+ int subjectUser = allUsers[su].id;
+ for (int ou = su; ou < userCount; ou++) {
+ int otherUser = allUsers[ou].id;
+ int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
+ if (!mShouldFilterCache.contains(subjectUid)) {
+ mShouldFilterCache.put(subjectUid, new SparseBooleanArray(appxUidCount));
+ }
+ int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
+ if (!mShouldFilterCache.contains(otherUid)) {
+ mShouldFilterCache.put(otherUid, new SparseBooleanArray(appxUidCount));
+ }
+ mShouldFilterCache.get(subjectUid).put(otherUid,
+ shouldFilterApplicationInternal(
+ subjectUid, subjectSetting, otherSetting, otherUser));
+ mShouldFilterCache.get(otherUid).put(subjectUid,
+ shouldFilterApplicationInternal(
+ otherUid, otherSetting, subjectSetting, subjectUser));
}
}
- mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
- mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@@ -561,6 +714,7 @@ public class AppsFilter {
}
}
}
+
/**
* Fetches all app Ids that a given setting is currently visible to, per provided user. This
* only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see
@@ -569,11 +723,11 @@ public class AppsFilter {
* If the setting is visible to all UIDs, null is returned. If an app is not visible to any
* applications, the int array will be empty.
*
- * @param users the set of users that should be evaluated for this calculation
+ * @param users the set of users that should be evaluated for this calculation
* @param existingSettings the set of all package settings that currently exist on device
* @return a SparseArray mapping userIds to a sorted int array of appIds that may view the
- * provided setting or null if the app is visible to all and no whitelist should be
- * applied.
+ * provided setting or null if the app is visible to all and no whitelist should be
+ * applied.
*/
@Nullable
public SparseArray<int[]> getVisibilityWhitelist(PackageSetting setting, int[] users,
@@ -618,52 +772,61 @@ public class AppsFilter {
/**
* Removes a package for consideration when filtering visibility between apps.
*
- * @param setting the setting of the package being removed.
- * @param allUsers array of all current users on device.
+ * @param setting the setting of the package being removed.
*/
- public void removePackage(PackageSetting setting, int[] allUsers,
- ArrayMap<String, PackageSetting> existingSettings) {
- mForceQueryable.remove(setting.appId);
-
- for (int u = 0; u < allUsers.length; u++) {
- final int userId = allUsers[u];
- final int removingUid = UserHandle.getUid(userId, setting.appId);
- mImplicitlyQueryable.remove(removingUid);
- for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
- mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
+ public void removePackage(PackageSetting setting) {
+ removeAppIdFromVisibilityCache(setting.appId);
+ mStateProvider.runWithState((settings, users) -> {
+ final int userCount = users.length;
+ for (int u = 0; u < userCount; u++) {
+ final int userId = users[u].id;
+ final int removingUid = UserHandle.getUid(userId, setting.appId);
+ mImplicitlyQueryable.remove(removingUid);
+ for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
+ mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
+ }
}
- }
- mQueriesViaComponent.remove(setting.appId);
- for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
- mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
- }
- mQueriesViaPackage.remove(setting.appId);
- for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
- mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
- }
+ mQueriesViaComponent.remove(setting.appId);
+ for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
+ mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
+ }
+ mQueriesViaPackage.remove(setting.appId);
+ for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
+ mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
+ }
- // re-add other shared user members to re-establish visibility between them and other
- // packages
- if (setting.sharedUser != null) {
- for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
- if (setting.sharedUser.packages.valueAt(i) == setting) {
- continue;
+ // re-add other shared user members to re-establish visibility between them and other
+ // packages
+ if (setting.sharedUser != null) {
+ for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
+ if (setting.sharedUser.packages.valueAt(i) == setting) {
+ continue;
+ }
+ addPackageInternal(
+ setting.sharedUser.packages.valueAt(i), settings);
}
- addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
}
- }
- if (!setting.pkg.getProtectedBroadcasts().isEmpty()) {
- final String removingPackageName = setting.pkg.getPackageName();
- mProtectedBroadcasts.clear();
- mProtectedBroadcasts.addAll(
- collectProtectedBroadcasts(existingSettings, removingPackageName));
- recomputeComponentVisibility(existingSettings, removingPackageName);
- }
+ if (!setting.pkg.getProtectedBroadcasts().isEmpty()) {
+ final String removingPackageName = setting.pkg.getPackageName();
+ mProtectedBroadcasts.clear();
+ mProtectedBroadcasts.addAll(
+ collectProtectedBroadcasts(settings, removingPackageName));
+ recomputeComponentVisibility(settings, removingPackageName);
+ }
+
+ mOverlayReferenceMapper.removePkg(setting.name);
+ mFeatureConfig.updatePackageState(setting, true /*removed*/);
+
+ if (mShouldFilterCache != null) {
+ updateShouldFilterCacheForPackage(
+ setting.name, setting, settings, users, settings.size());
+ }
+ });
+ mForceQueryable.remove(setting.appId);
+
- mOverlayReferenceMapper.removePkg(setting.name);
- mFeatureConfig.updatePackageState(setting, true /*removed*/);
}
/**
@@ -680,11 +843,32 @@ public class AppsFilter {
PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
try {
-
- if (!shouldFilterApplicationInternal(
- callingUid, callingSetting, targetPkgSetting, userId)) {
+ if (callingUid < Process.FIRST_APPLICATION_UID
+ || UserHandle.getAppId(callingUid) == targetPkgSetting.appId) {
return false;
}
+ if (mShouldFilterCache != null) { // use cache
+ SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid);
+ final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
+ if (shouldFilterTargets == null) {
+ Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + callingUid);
+ return true;
+ }
+ int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid);
+ if (indexOfTargetUid < 0) {
+ Slog.w(TAG, "Encountered calling -> target with no cached rules: "
+ + callingUid + " -> " + targetUid);
+ return true;
+ }
+ if (!shouldFilterTargets.valueAt(indexOfTargetUid)) {
+ return false;
+ }
+ } else {
+ if (!shouldFilterApplicationInternal(
+ callingUid, callingSetting, targetPkgSetting, userId)) {
+ return false;
+ }
+ }
if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(UserHandle.getAppId(callingUid))) {
log(callingSetting, targetPkgSetting, "BLOCKED");
}
@@ -695,7 +879,7 @@ public class AppsFilter {
}
private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
- PackageSetting targetPkgSetting, int userId) {
+ PackageSetting targetPkgSetting, int targetUserId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
try {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
@@ -705,12 +889,6 @@ public class AppsFilter {
}
return false;
}
- if (callingUid < Process.FIRST_APPLICATION_UID) {
- if (DEBUG_LOGGING) {
- Slog.d(TAG, "filtering skipped; " + callingUid + " is system");
- }
- return false;
- }
if (callingSetting == null) {
Slog.wtf(TAG, "No setting found for non system uid " + callingUid);
return true;
@@ -719,8 +897,14 @@ public class AppsFilter {
final ArraySet<PackageSetting> callingSharedPkgSettings;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
if (callingSetting instanceof PackageSetting) {
- callingPkgSetting = (PackageSetting) callingSetting;
- callingSharedPkgSettings = null;
+ if (((PackageSetting) callingSetting).sharedUser == null) {
+ callingPkgSetting = (PackageSetting) callingSetting;
+ callingSharedPkgSettings = null;
+ } else {
+ callingPkgSetting = null;
+ callingSharedPkgSettings =
+ ((PackageSetting) callingSetting).sharedUser.packages;
+ }
} else {
callingPkgSetting = null;
callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
@@ -778,13 +962,19 @@ public class AppsFilter {
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "hasPermission");
- if (callingSetting.getPermissionsState().hasPermission(
- Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
- if (DEBUG_LOGGING) {
- log(callingSetting, targetPkgSetting, "has query-all permission");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages");
+ if (callingPkgSetting != null) {
+ if (callingPkgSetting.pkg != null
+ && requestsQueryAllPackages(callingPkgSetting.pkg)) {
+ return false;
+ }
+ } else {
+ for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
+ AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).pkg;
+ if (pkg != null && requestsQueryAllPackages(pkg)) {
+ return false;
+ }
}
- return false;
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -825,7 +1015,7 @@ public class AppsFilter {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable");
- final int targetUid = UserHandle.getUid(userId, targetAppId);
+ final int targetUid = UserHandle.getUid(targetUserId, targetAppId);
if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "implicitly queryable for user");
@@ -863,13 +1053,20 @@ public class AppsFilter {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
-
return true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
+
+ private static boolean requestsQueryAllPackages(@NonNull AndroidPackage pkg) {
+ // we're not guaranteed to have permissions yet analyzed at package add, so we inspect the
+ // package directly
+ return pkg.getRequestedPermissions().contains(
+ Manifest.permission.QUERY_ALL_PACKAGES);
+ }
+
/** Returns {@code true} if the source package instruments the target package. */
private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ae8b3a0e9acc..f3bc0561b6ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12362,7 +12362,7 @@ public class PackageManagerService extends IPackageManager.Stub
ksms.addScannedPackageLPw(pkg);
mComponentResolver.addAllComponents(pkg, chatty);
- mAppsFilter.addPackage(pkgSetting, mSettings.mPackages);
+ mAppsFilter.addPackage(pkgSetting);
// Don't allow ephemeral applications to define new permissions groups.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
@@ -12536,8 +12536,6 @@ public class PackageManagerService extends IPackageManager.Stub
void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
mComponentResolver.removeAllComponents(pkg, chatty);
- mAppsFilter.removePackage(getPackageSetting(pkg.getPackageName()),
- mInjector.getUserManagerInternal().getUserIds(), mSettings.mPackages);
mPermissionManager.removeAllPermissions(pkg, chatty);
final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations());
@@ -14264,7 +14262,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Okay!
targetPackageSetting.setInstallerPackageName(installerPackageName);
mSettings.addInstallerPackageNames(targetPackageSetting.installSource);
- mAppsFilter.addPackage(targetPackageSetting, mSettings.mPackages);
+ mAppsFilter.addPackage(targetPackageSetting);
scheduleWriteSettingsLocked();
}
}
@@ -18717,6 +18715,7 @@ public class PackageManagerService extends IPackageManager.Stub
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
clearDefaultBrowserIfNeeded(packageName);
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
+ mAppsFilter.removePackage(getPackageSetting(packageName));
removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
@@ -23474,6 +23473,7 @@ public class PackageManagerService extends IPackageManager.Stub
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
primeDomainVerificationsLPw(userId);
+ mAppsFilter.onUsersChanged();
}
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 51e07faf8443..8000c639139f 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -63,6 +63,10 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Objects;
/**
@@ -557,6 +561,20 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK = 12;
private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = 13;
private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = 14;
+ // Filter with IORap
+ private static final int TRON_COMPILATION_FILTER_ASSUMED_VERIFIED_IORAP = 15;
+ private static final int TRON_COMPILATION_FILTER_EXTRACT_IORAP = 16;
+ private static final int TRON_COMPILATION_FILTER_VERIFY_IORAP = 17;
+ private static final int TRON_COMPILATION_FILTER_QUICKEN_IORAP = 18;
+ private static final int TRON_COMPILATION_FILTER_SPACE_PROFILE_IORAP = 19;
+ private static final int TRON_COMPILATION_FILTER_SPACE_IORAP = 20;
+ private static final int TRON_COMPILATION_FILTER_SPEED_PROFILE_IORAP = 21;
+ private static final int TRON_COMPILATION_FILTER_SPEED_IORAP = 22;
+ private static final int TRON_COMPILATION_FILTER_EVERYTHING_PROFILE_IORAP = 23;
+ private static final int TRON_COMPILATION_FILTER_EVERYTHING_IORAP = 24;
+ private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_IORAP = 25;
+ private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK_IORAP = 26;
+ private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK_IORAP = 27;
// Constants used for logging compilation reason to TRON.
// DO NOT CHANGE existing values.
@@ -623,6 +641,22 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
case "run-from-vdex-fallback" :
return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+ case "assume-verified-iorap" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED_IORAP;
+ case "extract-iorap" : return TRON_COMPILATION_FILTER_EXTRACT_IORAP;
+ case "verify-iorap" : return TRON_COMPILATION_FILTER_VERIFY_IORAP;
+ case "quicken-iorap" : return TRON_COMPILATION_FILTER_QUICKEN_IORAP;
+ case "space-profile-iorap" : return TRON_COMPILATION_FILTER_SPACE_PROFILE_IORAP;
+ case "space-iorap" : return TRON_COMPILATION_FILTER_SPACE_IORAP;
+ case "speed-profile-iorap" : return TRON_COMPILATION_FILTER_SPEED_PROFILE_IORAP;
+ case "speed-iorap" : return TRON_COMPILATION_FILTER_SPEED_IORAP;
+ case "everything-profile-iorap" :
+ return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE_IORAP;
+ case "everything-iorap" : return TRON_COMPILATION_FILTER_EVERYTHING_IORAP;
+ case "run-from-apk-iorap" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_IORAP;
+ case "run-from-apk-fallback-iorap" :
+ return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK_IORAP;
+ case "run-from-vdex-fallback-iorap" :
+ return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK_IORAP;
default: return TRON_COMPILATION_FILTER_UNKNOWN;
}
}
@@ -640,9 +674,12 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
}
private class ArtManagerInternalImpl extends ArtManagerInternal {
+ private static final String IORAP_DIR = "/data/misc/iorapd";
+ private static final String TAG = "ArtManagerInternalImpl";
+
@Override
public PackageOptimizationInfo getPackageOptimizationInfo(
- ApplicationInfo info, String abi) {
+ ApplicationInfo info, String abi, String activityName) {
String compilationReason;
String compilationFilter;
try {
@@ -662,11 +699,45 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
compilationReason = "error";
}
+ if (checkIorapCompiledTrace(info.packageName, activityName, info.longVersionCode)) {
+ compilationFilter = compilationFilter + "-iorap";
+ }
+
int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter);
int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason);
return new PackageOptimizationInfo(
compilationFilterTronValue, compilationReasonTronValue);
}
+
+ /*
+ * Checks the existence of IORap compiled trace for an app.
+ *
+ * @return true if the compiled trace exists and the size is greater than 1kb.
+ */
+ private boolean checkIorapCompiledTrace(
+ String packageName, String activityName, long version) {
+ // For example: /data/misc/iorapd/com.google.android.GoogleCamera/
+ // 60092239/com.android.camera.CameraLauncher/compiled_traces/compiled_trace.pb
+ Path tracePath = Paths.get(IORAP_DIR,
+ packageName,
+ Long.toString(version),
+ activityName,
+ "compiled_traces",
+ "compiled_trace.pb");
+ try {
+ boolean exists = Files.exists(tracePath);
+ Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist"));
+ if (exists) {
+ long bytes = Files.size(tracePath);
+ Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes));
+ return bytes > 0L;
+ }
+ return exists;
+ } catch (IOException e) {
+ Log.d(TAG, e.getMessage());
+ return false;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d3f3ba1dc6bb..1b11e2d0860d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2509,6 +2509,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (String permissionName : requestedPermissions) {
BasePermission permission = mSettings.getPermission(permissionName);
+ if (permission == null) {
+ continue;
+ }
if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME)
&& permission.isRuntime() && !permission.isRemoved()) {
if (permission.isHardOrSoftRestricted()
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7fe21e32cbaf..802a35560ba5 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -236,6 +236,17 @@ public class StatsPullAtomService extends SystemService {
private static final String DANGEROUS_PERMISSION_STATE_SAMPLE_RATE =
"dangerous_permission_state_sample_rate";
+ /** Parameters relating to ProcStats data upload. */
+ // Maximum shards to use when generating StatsEvent objects from ProcStats.
+ private static final int MAX_PROCSTATS_SHARDS = 5;
+ // Should match MAX_PAYLOAD_SIZE in StatsEvent, minus a small amount for overhead/metadata.
+ private static final int MAX_PROCSTATS_SHARD_SIZE = 48 * 1024; // 48 KB
+ // In ProcessStats, we measure the size of a raw ProtoOutputStream, before compaction. This
+ // typically runs 35-45% larger than the compacted size that will be written to StatsEvent.
+ // Hence, we can allow a little more room in each shard before moving to the next. Make this
+ // 20% as a conservative estimate.
+ private static final int MAX_PROCSTATS_RAW_SHARD_SIZE = (int) (MAX_PROCSTATS_SHARD_SIZE * 1.20);
+
private final Object mThermalLock = new Object();
@GuardedBy("mThermalLock")
private IThermalService mThermalService;
@@ -2554,19 +2565,26 @@ public class StatsPullAtomService extends SystemService {
long lastHighWaterMark = readProcStatsHighWaterMark(section);
List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+ ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
+ for (int i = 0; i < protoStreams.length; i++) {
+ protoStreams[i] = new ProtoOutputStream();
+ }
+
ProcessStats procStats = new ProcessStats(false);
+ // Force processStatsService to aggregate all in-storage and in-memory data.
long highWaterMark = processStatsService.getCommittedStatsMerged(
lastHighWaterMark, section, true, statsFiles, procStats);
+ procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
- // aggregate the data together for westworld consumption
- ProtoOutputStream proto = new ProtoOutputStream();
- procStats.dumpAggregatedProtoForStatsd(proto);
-
- StatsEvent e = StatsEvent.newBuilder()
- .setAtomId(atomTag)
- .writeByteArray(proto.getBytes())
- .build();
- pulledData.add(e);
+ for (ProtoOutputStream proto : protoStreams) {
+ if (proto.getBytes().length > 0) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeByteArray(proto.getBytes())
+ .build();
+ pulledData.add(e);
+ }
+ }
new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
.delete();
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index fb29f9a93215..189b21fb81a6 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -842,7 +842,8 @@ class ActivityMetricsLogger {
? PackageOptimizationInfo.createWithNoInfo()
: artManagerInternal.getPackageOptimizationInfo(
info.applicationInfo,
- info.launchedActivityAppRecordRequiredAbi);
+ info.launchedActivityAppRecordRequiredAbi,
+ info.launchedActivityName);
builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
packageOptimizationInfo.getCompilationReason());
builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5668454b7bb6..304860c2588f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6456,14 +6456,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
public boolean matchParentBounds() {
- if (super.matchParentBounds() && mCompatDisplayInsets == null) {
+ final Rect overrideBounds = getResolvedOverrideBounds();
+ if (overrideBounds.isEmpty()) {
return true;
}
- // An activity in size compatibility mode may have resolved override bounds, so the exact
- // bounds should also be checked. Otherwise IME window will show with offset. See
- // {@link DisplayContent#isImeAttachedToApp}.
+ // An activity in size compatibility mode may have override bounds which equals to its
+ // parent bounds, so the exact bounds should also be checked to allow IME window to attach
+ // to the activity. See {@link DisplayContent#isImeAttachedToApp}.
final WindowContainer parent = getParent();
- return parent == null || parent.getBounds().equals(getResolvedOverrideBounds());
+ return parent == null || parent.getBounds().equals(overrideBounds);
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fb602573e31b..c24c1e466133 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -32,6 +32,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
@@ -1526,12 +1527,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
- * Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed
+ * Sets the provided record to {@link #mFixedRotationLaunchingApp} if possible to apply fixed
* rotation transform to it and indicate that the display may be rotated after it is launched.
*/
void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Surface.Rotation int rotation) {
final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp;
- if (prevRotatedLaunchingApp != null && prevRotatedLaunchingApp == r
+ if (prevRotatedLaunchingApp == r
&& r.getWindowConfiguration().getRotation() == rotation) {
// The given launching app and target rotation are the same as the existing ones.
return;
@@ -2175,6 +2176,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
}
+ boolean isTrusted() {
+ return mDisplay.isTrusted();
+ }
+
/**
* Returns the topmost stack on the display that is compatible with the input windowing mode and
* activity type. Null is no compatible stack on the display.
@@ -3522,7 +3527,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
boolean canShowIme() {
- if (isUntrustedVirtualDisplay()) {
+ if (!isTrusted()) {
return false;
}
return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this)
@@ -4753,15 +4758,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// VR virtual display will be used to run and render 2D app within a VR experience.
&& mDisplayId != mWmService.mVr2dDisplayId
// Do not show system decorations on untrusted virtual display.
- && !isUntrustedVirtualDisplay();
- }
-
- /**
- * @return {@code true} if the display is non-system created virtual display.
- */
- boolean isUntrustedVirtualDisplay() {
- return mDisplay.getType() == Display.TYPE_VIRTUAL
- && mDisplay.getOwnerUid() != Process.SYSTEM_UID;
+ && isTrusted();
}
/**
@@ -5663,6 +5660,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
+ /**
+ * Return {@code true} if there is an ongoing animation to the "Recents" activity and this
+ * activity as a fixed orientation so shouldn't be rotated.
+ */
+ boolean isFixedOrientationRecentsAnimating() {
+ return mAnimatingRecents != null
+ && mAnimatingRecents.getRequestedConfigurationOrientation()
+ != ORIENTATION_UNDEFINED;
+ }
+
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
final ActivityRecord r = getActivityRecord(token);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 831491dd145e..f093fd34bcc0 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -430,6 +430,15 @@ public class DisplayRotation {
"Deferring rotation, still finishing previous rotation");
return false;
}
+
+ if (mDisplayContent.mFixedRotationTransitionListener
+ .isFixedOrientationRecentsAnimating()) {
+ // During the recents animation, the closing app might still be considered on top.
+ // In order to ignore its requested orientation to avoid a sensor led rotation (e.g
+ // user rotating the device while the recents animation is running), we ignore
+ // rotation update while the animation is running.
+ return false;
+ }
}
if (!mService.mDisplayEnabled) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index be6e4b76e8ed..3d7873ad2f7c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -384,7 +384,7 @@ class InsetsPolicy {
InsetsPolicyAnimationControlCallbacks mControlCallbacks;
InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
- super(show, false /* hasCallbacks */, types);
+ super(show, false /* hasCallbacks */, types, false /* disable */);
mFinishCallback = finishCallback;
mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c749125ec531..6670dbfea282 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2927,9 +2927,17 @@ class Task extends WindowContainer<WindowContainer> {
// Don't crop HOME/RECENTS windows to stack bounds. This is because in split-screen
// they extend past their stack and sysui uses the stack surface to control cropping.
// TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
- final boolean isTopHomeOrRecents = (isActivityTypeHome() || isActivityTypeRecents())
- && getRootTask().getTopMostTask() == this;
- return isResizeable() && !isTopHomeOrRecents;
+ if (isActivityTypeHome() || isActivityTypeRecents()) {
+ // Make sure this is the top-most non-organizer root task (if not top-most, it means
+ // another translucent task could be above this, so this needs to stay cropped.
+ final Task rootTask = getRootTask();
+ final Task topNonOrgTask =
+ rootTask.mCreatedByOrganizer ? rootTask.getTopMostTask() : rootTask;
+ if (isDescendantOf(topNonOrgTask)) {
+ return false;
+ }
+ }
+ return isResizeable();
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index e0568af12944..07e309e1126a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -353,7 +353,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
}
// We don't allow untrusted display to top when task stack moves to top,
// until user tapping this display to change display position as top intentionally.
- if (mDisplayContent.isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
+ if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) {
includingParents = false;
}
final int targetPosition = findPositionForStack(position, child, false /* adding */);
@@ -1529,8 +1529,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
@Nullable
ActivityStack getOrCreateRootHomeTask(boolean onTop) {
ActivityStack homeTask = getRootHomeTask();
- if (homeTask == null && mDisplayContent.supportsSystemDecorations()
- && !mDisplayContent.isUntrustedVirtualDisplay()) {
+ if (homeTask == null && mDisplayContent.supportsSystemDecorations()) {
homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
}
return homeTask;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 1a2672bd0132..51cf858715b4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -28,6 +28,7 @@ import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.RecordingCanvas;
@@ -37,8 +38,11 @@ import android.os.Environment;
import android.os.Handler;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
+import android.view.WindowInsets;
import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
@@ -475,9 +479,12 @@ class TaskSnapshotController {
final int color = ColorUtils.setAlphaComponent(
task.getTaskDescription().getBackgroundColor(), 255);
final LayoutParams attrs = mainWindow.getAttrs();
+ final InsetsPolicy insetsPolicy = mainWindow.getDisplayContent().getInsetsPolicy();
+ final InsetsState insetsState = insetsPolicy.getInsetsForDispatch(mainWindow);
+ final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState);
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
- mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState());
+ mHighResTaskSnapshotScale, insetsState);
final int taskWidth = task.getBounds().width();
final int taskHeight = task.getBounds().height();
final int width = (int) (taskWidth * mHighResTaskSnapshotScale);
@@ -488,7 +495,7 @@ class TaskSnapshotController {
node.setClipToBounds(false);
final RecordingCanvas c = node.start(width, height);
c.drawColor(color);
- decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
+ decorPainter.setInsets(systemBarInsets);
decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
node.end(c);
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
@@ -593,6 +600,13 @@ class TaskSnapshotController {
return 0;
}
+ static Rect getSystemBarInsets(Rect frame, InsetsState state) {
+ return state.calculateInsets(frame, null /* ignoringVisibilityState */,
+ false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
+ null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacySystemUiFlags */,
+ null /* typeSideMap */).getInsets(WindowInsets.Type.systemBars()).toRect();
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "mHighResTaskSnapshotScale=" + mHighResTaskSnapshotScale);
mCache.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index e26f1e1fe06f..f1f576220a9a 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -39,10 +39,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
-import static com.android.internal.policy.DecorView.getColorViewLeftInset;
-import static com.android.internal.policy.DecorView.getColorViewTopInset;
import static com.android.internal.policy.DecorView.getNavigationBarRect;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -131,9 +130,8 @@ class TaskSnapshotSurface implements StartingSurface {
private final IWindowSession mSession;
private final WindowManagerService mService;
private final Rect mTaskBounds;
- private final Rect mStableInsets = new Rect();
- private final Rect mContentInsets = new Rect();
private final Rect mFrame = new Rect();
+ private final Rect mSystemBarInsets = new Rect();
private TaskSnapshot mSnapshot;
private final RectF mTmpSnapshotSize = new RectF();
private final RectF mTmpDstFrame = new RectF();
@@ -174,6 +172,7 @@ class TaskSnapshotSurface implements StartingSurface {
final int windowFlags;
final int windowPrivateFlags;
final int currentOrientation;
+ final InsetsState insetsState;
synchronized (service.mGlobalLock) {
final WindowState mainWindow = activity.findMainWindow();
final Task task = activity.getTask();
@@ -241,6 +240,10 @@ class TaskSnapshotSurface implements StartingSurface {
taskBounds = new Rect();
task.getBounds(taskBounds);
currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
+
+ final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
+ .getInsetsPolicy();
+ insetsState = insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow);
}
try {
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
@@ -255,8 +258,7 @@ class TaskSnapshotSurface implements StartingSurface {
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
- windowFlags, windowPrivateFlags, taskBounds,
- currentOrientation, topFullscreenOpaqueWindow.getRequestedInsetsState());
+ windowFlags, windowPrivateFlags, taskBounds, currentOrientation, insetsState);
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
@@ -266,7 +268,9 @@ class TaskSnapshotSurface implements StartingSurface {
} catch (RemoteException e) {
// Local call.
}
- snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
+
+ final Rect systemBarInsets = getSystemBarInsets(tmpFrame, insetsState);
+ snapshotSurface.setFrames(tmpFrame, systemBarInsets);
snapshotSurface.drawSnapshot();
return snapshotSurface;
}
@@ -315,13 +319,12 @@ class TaskSnapshotSurface implements StartingSurface {
}
@VisibleForTesting
- void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
+ void setFrames(Rect frame, Rect systemBarInsets) {
mFrame.set(frame);
- mContentInsets.set(contentInsets);
- mStableInsets.set(stableInsets);
+ mSystemBarInsets.set(systemBarInsets);
mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth()
|| mFrame.height() != mSnapshot.getSnapshot().getHeight());
- mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets);
+ mSystemBarBackgroundPainter.setInsets(systemBarInsets);
}
private void drawSnapshot() {
@@ -453,9 +456,7 @@ class TaskSnapshotSurface implements StartingSurface {
);
// However, we also need to make space for the navigation bar on the left side.
- final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
- mContentInsets.left);
- frame.offset(colorViewLeftInset, 0);
+ frame.offset(mSystemBarInsets.left, 0);
return frame;
}
@@ -540,8 +541,6 @@ class TaskSnapshotSurface implements StartingSurface {
*/
static class SystemBarBackgroundPainter {
- private final Rect mContentInsets = new Rect();
- private final Rect mStableInsets = new Rect();
private final Paint mStatusBarPaint = new Paint();
private final Paint mNavigationBarPaint = new Paint();
private final int mStatusBarColor;
@@ -551,6 +550,7 @@ class TaskSnapshotSurface implements StartingSurface {
private final int mSysUiVis;
private final float mScale;
private final InsetsState mInsetsState;
+ private final Rect mSystemBarInsets = new Rect();
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
TaskDescription taskDescription, float scale, InsetsState insetsState) {
@@ -576,9 +576,8 @@ class TaskSnapshotSurface implements StartingSurface {
mInsetsState = insetsState;
}
- void setInsets(Rect contentInsets, Rect stableInsets) {
- mContentInsets.set(contentInsets);
- mStableInsets.set(stableInsets);
+ void setInsets(Rect systemBarInsets) {
+ mSystemBarInsets.set(systemBarInsets);
}
int getStatusBarColorViewHeight() {
@@ -589,7 +588,7 @@ class TaskSnapshotSurface implements StartingSurface {
mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)
: STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
- return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale);
+ return (int) (mSystemBarInsets.top * mScale);
} else {
return 0;
}
@@ -615,8 +614,7 @@ class TaskSnapshotSurface implements StartingSurface {
int statusBarHeight) {
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
- final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right,
- mContentInsets.right) * mScale);
+ final int rightInset = (int) (mSystemBarInsets.right * mScale);
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
}
@@ -625,8 +623,8 @@ class TaskSnapshotSurface implements StartingSurface {
@VisibleForTesting
void drawNavigationBarBackground(Canvas c) {
final Rect navigationBarRect = new Rect();
- getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
- navigationBarRect, mScale);
+ getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
+ mScale);
final boolean visible = isNavigationBarColorViewVisible();
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
c.drawRect(navigationBarRect, mNavigationBarPaint);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6406f0ae51a6..dd08f4208eca 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1032,7 +1032,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @return Whether this child is on top of the window hierarchy.
*/
boolean isOnTop() {
- return getParent().getTopChild() == this && getParent().isOnTop();
+ final WindowContainer parent = getParent();
+ return parent != null && parent.getTopChild() == this && parent.isOnTop();
}
/** Returns the top child container. */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0590288a7f8b..10d07573f8c6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7150,9 +7150,6 @@ public class WindowManagerService extends IWindowManager.Stub
+ "not exist: %d", displayId);
return false;
}
- if (displayContent.isUntrustedVirtualDisplay()) {
- return false;
- }
return displayContent.supportsSystemDecorations();
}
}
@@ -7171,7 +7168,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ "does not exist: %d", displayId);
return;
}
- if (displayContent.isUntrustedVirtualDisplay()) {
+ if (!displayContent.isTrusted()) {
throw new SecurityException("Attempted to set system decors flag to an "
+ "untrusted virtual display: " + displayId);
}
@@ -7219,7 +7216,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ "exist: %d", displayId);
return;
}
- if (displayContent.isUntrustedVirtualDisplay()) {
+ if (!displayContent.isTrusted()) {
throw new SecurityException("Attempted to set IME flag to an untrusted "
+ "virtual display: " + displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6bbc019ed5e7..5fc519c86f11 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2339,6 +2339,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
+ if (inPinnedWindowingMode()) {
+ return false;
+ }
+
final boolean windowsAreFocusable = mActivityRecord == null || mActivityRecord.windowsAreFocusable();
if (!windowsAreFocusable) {
// This window can't be an IME target if the app's windows should not be focusable.
@@ -2830,7 +2834,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Do not allow untrusted virtual display to receive keys unless user intentionally
// touches the display.
return fromUserTouch || getDisplayContent().isOnTop()
- || !getDisplayContent().isUntrustedVirtualDisplay();
+ || getDisplayContent().isTrusted();
}
@Override
@@ -3412,6 +3416,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) {
final Task task = getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
+ handle.setTouchableRegionCrop(null);
return;
}
@@ -5158,17 +5163,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
- int x = mSurfacePosition.x;
- int y = mSurfacePosition.y;
+ transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
+ int x = mSurfacePosition.x + mTmpPoint.x;
+ int y = mSurfacePosition.y + mTmpPoint.y;
// We might be on a display which has been re-parented to a view in another window, so here
// computes the global location of our display.
DisplayContent dc = getDisplayContent();
while (dc != null && dc.getParentWindow() != null) {
final WindowState displayParent = dc.getParentWindow();
- x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left
+ x += displayParent.mWindowFrames.mFrame.left
+ (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
- y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top
+ y += displayParent.mWindowFrames.mFrame.top
+ (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
dc = displayParent.getDisplayContent();
}
@@ -5426,6 +5432,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final WindowState imeTarget = getDisplayContent().mInputMethodTarget;
boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
&& imeTarget.mToken == mToken
+ && mAttrs.type != TYPE_APPLICATION_STARTING
&& getParent() != null
&& imeTarget.compareTo(this) <= 0;
return inTokenWithAndAboveImeTarget;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c0252363a159..8115ac8c6bef 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -247,10 +247,6 @@ class WindowStateAnimator {
private final SurfaceControl.Transaction mPostDrawTransaction =
new SurfaceControl.Transaction();
- // Used to track whether we have called detach children on the way to invisibility, in which
- // case we need to give the client a new Surface if it lays back out to a visible state.
- boolean mChildrenDetached = false;
-
// Set to true after the first frame of the Pinned stack animation
// and reset after the last to ensure we only reset mForceScaleUntilResize
// once per animation.
@@ -425,7 +421,8 @@ class WindowStateAnimator {
// transparent to the app.
// If the children are detached, we don't want to reparent them to the new surface.
// Instead let the children get removed when the old surface is deleted.
- if (mSurfaceController != null && mPendingDestroySurface != null && !mChildrenDetached
+ if (mSurfaceController != null && mPendingDestroySurface != null
+ && !mPendingDestroySurface.mChildrenDetached
&& (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
mPostDrawTransaction.reparentChildren(
mPendingDestroySurface.getClientViewRootSurface(),
@@ -461,7 +458,6 @@ class WindowStateAnimator {
if (mSurfaceController != null) {
return mSurfaceController;
}
- mChildrenDetached = false;
if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT;
@@ -1365,7 +1361,7 @@ class WindowStateAnimator {
mPostDrawTransaction.reparent(pendingSurfaceControl, null);
// If the children are detached, we don't want to reparent them to the new surface.
// Instead let the children get removed when the old surface is deleted.
- if (!mChildrenDetached) {
+ if (!mPendingDestroySurface.mChildrenDetached) {
mPostDrawTransaction.reparentChildren(
mPendingDestroySurface.getClientViewRootSurface(),
mSurfaceController.mSurfaceControl);
@@ -1596,7 +1592,6 @@ class WindowStateAnimator {
if (mSurfaceController != null) {
mSurfaceController.detachChildren();
}
- mChildrenDetached = true;
// If the children are detached, it means the app is exiting. We don't want to tear the
// content down too early, otherwise we could end up with a flicker. By preserving the
// current surface, we ensure the content remains on screen until the window is completely
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 0a7ca5a0cf35..b2bfcdc8a900 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -90,6 +90,9 @@ class WindowSurfaceController {
private final SurfaceControl.Transaction mTmpTransaction;
+ // Used to track whether we have called detach children on the way to invisibility.
+ boolean mChildrenDetached;
+
WindowSurfaceController(String name, int w, int h, int format,
int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
mAnimator = animator;
@@ -144,6 +147,7 @@ class WindowSurfaceController {
void detachChildren() {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SEVER CHILDREN");
+ mChildrenDetached = true;
if (mSurfaceControl != null) {
mSurfaceControl.detachChildren();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7b624cae8141..401649a2e522 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12712,7 +12712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
for (ResolveInfo receiver : receivers) {
final String packageName = receiver.getComponentInfo().packageName;
if (checkCrossProfilePackagePermissions(packageName, userId,
- requiresPermission)) {
+ requiresPermission)
+ || checkModifyQuietModePermission(packageName, userId)) {
Slog.i(LOG_TAG,
String.format("Sending %s broadcast to %s.", intent.getAction(),
packageName));
@@ -12730,6 +12731,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
/**
+ * Checks whether the package {@code packageName} has the {@code MODIFY_QUIET_MODE}
+ * permission granted for the user {@code userId}.
+ */
+ private boolean checkModifyQuietModePermission(String packageName, @UserIdInt int userId) {
+ try {
+ final int uid = Objects.requireNonNull(
+ mInjector.getPackageManager().getApplicationInfoAsUser(
+ Objects.requireNonNull(packageName), /* flags= */ 0, userId)).uid;
+ return PackageManager.PERMISSION_GRANTED
+ == ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */
+ -1, /* exported= */ true);
+ } catch (NameNotFoundException ex) {
+ Slog.w(LOG_TAG,
+ String.format("Cannot find the package %s to check for permissions.",
+ packageName));
+ return false;
+ }
+ }
+
+ /**
* Checks whether the package {@code packageName} has the required permissions to receive
* cross-profile broadcasts on behalf of the user {@code userId}.
*/
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index c34b8e19a41d..ac44ccea2106 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -264,6 +264,19 @@ public class HdmiControlServiceTest {
}
@Test
+ public void disableAndReenableCec_volumeControlReturnsToOriginalValue() {
+ boolean volumeControlEnabled = true;
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(volumeControlEnabled);
+
+ mHdmiControlService.setControlEnabled(false);
+ assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
+
+ mHdmiControlService.setControlEnabled(true);
+ assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isEqualTo(
+ volumeControlEnabled);
+ }
+
+ @Test
public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_enabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index f205fde88c0d..4f21ee13a5e2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +33,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -39,9 +41,11 @@ import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedProvider;
import android.os.Build;
import android.os.Process;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -57,26 +61,36 @@ import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.security.cert.CertificateException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.IntFunction;
+import java.util.stream.Collectors;
@Presubmit
@RunWith(JUnit4.class)
public class AppsFilterTest {
- private static final int DUMMY_CALLING_UID = 10345;
- private static final int DUMMY_TARGET_UID = 10556;
- private static final int DUMMY_ACTOR_UID = 10656;
- private static final int DUMMY_OVERLAY_UID = 10756;
- private static final int DUMMY_ACTOR_TWO_UID = 10856;
+ private static final int DUMMY_CALLING_APPID = 10345;
+ private static final int DUMMY_TARGET_APPID = 10556;
+ private static final int DUMMY_ACTOR_APPID = 10656;
+ private static final int DUMMY_OVERLAY_APPID = 10756;
+ private static final int SYSTEM_USER = 0;
+ private static final int SECONDARY_USER = 10;
+ private static final int[] USER_ARRAY = {SYSTEM_USER, SECONDARY_USER};
+ private static final UserInfo[] USER_INFO_LIST = Arrays.stream(USER_ARRAY).mapToObj(
+ id -> new UserInfo(id, Integer.toString(id), 0)).toArray(UserInfo[]::new);
@Mock
AppsFilter.FeatureConfig mFeatureConfigMock;
+ @Mock
+ AppsFilter.StateProvider mStateProvider;
private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
@@ -170,15 +184,24 @@ public class AppsFilterTest {
mExisting = new ArrayMap<>();
MockitoAnnotations.initMocks(this);
+ doAnswer(invocation -> {
+ ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0))
+ .currentState(mExisting, USER_INFO_LIST);
+ return null;
+ }).when(mStateProvider)
+ .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class));
+
when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
- when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
- .thenReturn(true);
+ when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer(
+ (Answer<Boolean>) invocation ->
+ ((AndroidPackage)invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion()
+ >= Build.VERSION_CODES.R);
}
@Test
public void testSystemReadyPropogates() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
verify(mFeatureConfigMock).onSystemReady();
}
@@ -186,22 +209,23 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_FilterMatches() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
+ pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
final Signature frameworkSignature = Mockito.mock(Signature.class);
final PackageParser.SigningDetails frameworkSigningDetails =
new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
@@ -211,164 +235,174 @@ public class AppsFilterTest {
b -> b.setSigningDetails(frameworkSigningDetails));
appsFilter.onSystemReady();
- final int activityUid = DUMMY_TARGET_UID;
+ final int activityUid = DUMMY_TARGET_APPID;
PackageSetting targetActivity = simulateAddPackage(appsFilter,
pkg("com.target.activity", new IntentFilter("TEST_ACTION")), activityUid);
- final int receiverUid = DUMMY_TARGET_UID + 1;
+ final int receiverUid = DUMMY_TARGET_APPID + 1;
PackageSetting targetReceiver = simulateAddPackage(appsFilter,
pkgWithReceiver("com.target.receiver", new IntentFilter("TEST_ACTION")),
receiverUid);
- final int callingUid = DUMMY_CALLING_UID;
+ final int callingUid = DUMMY_CALLING_APPID;
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.calling.action", new Intent("TEST_ACTION")), callingUid);
- final int wildcardUid = DUMMY_CALLING_UID + 1;
+ final int wildcardUid = DUMMY_CALLING_APPID + 1;
PackageSetting callingWildCard = simulateAddPackage(appsFilter,
pkg("com.calling.wildcard", new Intent("*")), wildcardUid);
- assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity, 0));
- assertTrue(appsFilter.shouldFilterApplication(callingUid, calling, targetReceiver, 0));
+ assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity,
+ SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(callingUid, calling, targetReceiver,
+ SYSTEM_USER));
assertFalse(appsFilter.shouldFilterApplication(
- wildcardUid, callingWildCard, targetActivity, 0));
+ wildcardUid, callingWildCard, targetActivity, SYSTEM_USER));
assertTrue(appsFilter.shouldFilterApplication(
- wildcardUid, callingWildCard, targetReceiver, 0));
+ wildcardUid, callingWildCard, targetReceiver, SYSTEM_USER));
}
@Test
public void testQueriesProvider_FilterMatches() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
+ pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkgQueriesProvider("com.some.other.package", "com.some.authority"),
- DUMMY_CALLING_UID);
+ DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testQueriesDifferentProvider_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
+ pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkgQueriesProvider("com.some.other.package", "com.some.other.authority"),
- DUMMY_CALLING_UID);
+ DUMMY_CALLING_APPID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkgWithProvider("com.some.package", "com.some.authority;com.some.other.authority"),
- DUMMY_TARGET_UID);
+ DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkgQueriesProvider("com.some.other.package", "com.some.authority"),
- DUMMY_CALLING_UID);
+ DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
- PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package",
- new Intent("TEST_ACTION"))
- .setTargetSdkVersion(Build.VERSION_CODES.P),
- DUMMY_CALLING_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
+ ParsingPackage callingPkg = pkg("com.some.other.package",
+ new Intent("TEST_ACTION"))
+ .setTargetSdkVersion(Build.VERSION_CODES.P);
+ PackageSetting calling = simulateAddPackage(appsFilter, callingPkg,
+ DUMMY_CALLING_APPID);
- when(mFeatureConfigMock.packageIsEnabled(calling.pkg)).thenReturn(false);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testNoQueries_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_UID);
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testForceQueryable_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
+ pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_UID);
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
+ false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID,
+ pkg("com.some.package"), DUMMY_TARGET_APPID,
setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_UID);
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -382,62 +416,67 @@ public class AppsFilterTest {
simulateAddPackage(appsFilter, pkg("android"), 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_UID,
+ DUMMY_TARGET_APPID,
b -> b.setSigningDetails(frameworkSigningDetails)
.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_UID,
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID,
b -> b.setSigningDetails(otherSigningDetails));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
+ false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_UID);
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testSystemQueryable_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{},
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{},
true /* system force queryable */, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID,
+ pkg("com.some.package"), DUMMY_TARGET_APPID,
setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_UID);
+ pkg("com.some.other.package"), DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testQueriesPackage_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_UID);
+ pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
@@ -445,63 +484,67 @@ public class AppsFilterTest {
when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
.thenReturn(false);
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(
- appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
+ appsFilter, pkg("com.some.package"), DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(
- appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_UID);
+ appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testSystemUid_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
- assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(SYSTEM_USER, null, target, SYSTEM_USER));
assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1,
- null, target, 0));
+ null, target, SYSTEM_USER));
}
@Test
public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_UID);
+ pkg("com.some.package"), DUMMY_TARGET_APPID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, null, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, null, target,
+ SYSTEM_USER));
}
@Test
public void testNoTargetPackage_filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = new PackageSettingBuilder()
+ .setAppId(DUMMY_TARGET_APPID)
.setName("com.some.package")
.setCodePath("/")
.setResourcePath("/")
.setPVersionCode(1L)
.build();
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
@@ -516,7 +559,11 @@ public class AppsFilterTest {
.setOverlayTargetName("overlayableName");
ParsingPackage actor = pkg("com.some.package.actor");
- final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
+ final AppsFilter appsFilter = new AppsFilter(
+ mStateProvider,
+ mFeatureConfigMock,
+ new String[]{},
+ false,
new OverlayReferenceMapper.Provider() {
@Nullable
@Override
@@ -544,31 +591,34 @@ public class AppsFilterTest {
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
- PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
- PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
- PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID);
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
+ PackageSetting overlaySetting =
+ simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
+ PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_APPID);
// Actor can see both target and overlay
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
- targetSetting, 0));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
- overlaySetting, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
+ targetSetting, SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
+ overlaySetting, SYSTEM_USER));
// But target/overlay can't see each other
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
- overlaySetting, 0));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
- targetSetting, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, targetSetting,
+ overlaySetting, SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting,
+ targetSetting, SYSTEM_USER));
// And can't see the actor
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
- actorSetting, 0));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
- actorSetting, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, targetSetting,
+ actorSetting, SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting,
+ actorSetting, SYSTEM_USER));
}
@Test
public void testActsOnTargetOfOverlayThroughSharedUser() throws Exception {
+// Debug.waitForDebugger();
+
final String actorName = "overlay://test/actorName";
ParsingPackage target = pkg("com.some.package.target")
@@ -580,7 +630,11 @@ public class AppsFilterTest {
ParsingPackage actorOne = pkg("com.some.package.actor.one");
ParsingPackage actorTwo = pkg("com.some.package.actor.two");
- final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
+ final AppsFilter appsFilter = new AppsFilter(
+ mStateProvider,
+ mFeatureConfigMock,
+ new String[]{},
+ false,
new OverlayReferenceMapper.Provider() {
@Nullable
@Override
@@ -609,108 +663,114 @@ public class AppsFilterTest {
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
- PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
- PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
- PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID);
- PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo,
- DUMMY_ACTOR_TWO_UID);
-
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
- actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags);
- actorSharedSetting.addPackage(actorOneSetting);
- actorSharedSetting.addPackage(actorTwoSetting);
+ targetSetting.pkgFlags, targetSetting.pkgPrivateFlags);
+ PackageSetting overlaySetting =
+ simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
+ simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID,
+ null /*settingBuilder*/, actorSharedSetting);
+ simulateAddPackage(appsFilter, actorTwo, DUMMY_ACTOR_APPID,
+ null /*settingBuilder*/, actorSharedSetting);
+
// actorTwo can see both target and overlay
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
- targetSetting, 0));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
- overlaySetting, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting,
+ targetSetting, SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting,
+ overlaySetting, SYSTEM_USER));
}
@Test
public void testInitiatingApp_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_UID);
+ DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_UID, withInstallSource(target.name, null, null, false));
+ DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, false));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testUninstalledInitiatingApp_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_UID);
+ DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_UID, withInstallSource(target.name, null, null, true));
+ DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, true));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testOriginatingApp_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_UID);
+ DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_UID, withInstallSource(null, target.name, null, false));
+ DUMMY_CALLING_APPID, withInstallSource(null, target.name, null, false));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testInstallingApp_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_UID);
+ DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_UID, withInstallSource(null, null, target.name, false));
+ DUMMY_CALLING_APPID, withInstallSource(null, null, target.name, false));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+ SYSTEM_USER));
}
@Test
public void testInstrumentation_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_UID);
+ DUMMY_TARGET_APPID);
PackageSetting instrumentation = simulateAddPackage(appsFilter,
pkgWithInstrumentation("com.some.other.package", "com.some.package"),
- DUMMY_CALLING_UID);
+ DUMMY_CALLING_APPID);
assertFalse(
- appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, instrumentation, target, 0));
+ appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target,
+ SYSTEM_USER));
assertFalse(
- appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
+ appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation,
+ SYSTEM_USER));
}
@Test
public void testWhoCanSee() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -718,6 +778,7 @@ public class AppsFilterTest {
final int seesNothingAppId = Process.FIRST_APPLICATION_UID;
final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1;
final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2;
+
PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId);
PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"),
seesNothingAppId);
@@ -727,23 +788,25 @@ public class AppsFilterTest {
pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"),
queriesProviderAppId);
- final int[] systemFilter =
- appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0);
- assertThat(toList(systemFilter), empty());
+ final SparseArray<int[]> systemFilter =
+ appsFilter.getVisibilityWhitelist(system, USER_ARRAY, mExisting);
+ assertThat(toList(systemFilter.get(SYSTEM_USER)), empty());
- final int[] seesNothingFilter =
- appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0);
- assertThat(toList(seesNothingFilter),
+ final SparseArray<int[]> seesNothingFilter =
+ appsFilter.getVisibilityWhitelist(seesNothing, USER_ARRAY, mExisting);
+ assertThat(toList(seesNothingFilter.get(SYSTEM_USER)),
+ contains(seesNothingAppId));
+ assertThat(toList(seesNothingFilter.get(SECONDARY_USER)),
contains(seesNothingAppId));
- final int[] hasProviderFilter =
- appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0);
- assertThat(toList(hasProviderFilter),
+ final SparseArray<int[]> hasProviderFilter =
+ appsFilter.getVisibilityWhitelist(hasProvider, USER_ARRAY, mExisting);
+ assertThat(toList(hasProviderFilter.get(SYSTEM_USER)),
contains(hasProviderAppId, queriesProviderAppId));
- int[] queriesProviderFilter =
- appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
- assertThat(toList(queriesProviderFilter),
+ SparseArray<int[]> queriesProviderFilter =
+ appsFilter.getVisibilityWhitelist(queriesProvider, USER_ARRAY, mExisting);
+ assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)),
contains(queriesProviderAppId));
// provider read
@@ -751,8 +814,8 @@ public class AppsFilterTest {
// ensure implicit access is included in the filter
queriesProviderFilter =
- appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
- assertThat(toList(queriesProviderFilter),
+ appsFilter.getVisibilityWhitelist(queriesProvider, USER_ARRAY, mExisting);
+ assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)),
contains(hasProviderAppId, queriesProviderAppId));
}
@@ -779,11 +842,17 @@ public class AppsFilterTest {
private PackageSetting simulateAddPackage(AppsFilter filter,
ParsingPackage newPkgBuilder, int appId) {
- return simulateAddPackage(filter, newPkgBuilder, appId, null);
+ return simulateAddPackage(filter, newPkgBuilder, appId, null /*settingBuilder*/);
}
private PackageSetting simulateAddPackage(AppsFilter filter,
ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action) {
+ return simulateAddPackage(filter, newPkgBuilder, appId, action, null /*sharedUserSetting*/);
+ }
+
+ private PackageSetting simulateAddPackage(AppsFilter filter,
+ ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action,
+ @Nullable SharedUserSetting sharedUserSetting) {
AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
final PackageSettingBuilder settingBuilder = new PackageSettingBuilder()
@@ -795,8 +864,12 @@ public class AppsFilterTest {
.setPVersionCode(1L);
final PackageSetting setting =
(action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
- filter.addPackage(setting, mExisting);
mExisting.put(newPkg.getPackageName(), setting);
+ if (sharedUserSetting != null) {
+ sharedUserSetting.addPackage(setting);
+ setting.sharedUser = sharedUserSetting;
+ }
+ filter.addPackage(setting);
return setting;
}
@@ -809,4 +882,3 @@ public class AppsFilterTest {
return setting -> setting.setInstallSource(installSource);
}
}
-
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java
index b6ea063ccc14..f609306e44b0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java
@@ -51,4 +51,9 @@ public class NotificationChannelLoggerFake implements NotificationChannelLogger
NotificationChannelGroup channelGroup, int uid, String pkg, boolean wasBlocked) {
mCalls.add(new CallRecord(event));
}
+
+ @Override
+ public void logAppEvent(NotificationChannelEvent event, int uid, String pkg) {
+ mCalls.add(new CallRecord(event));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 622a203c5242..2e49929ec032 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -2266,6 +2266,14 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testAppBlockedLogging() {
+ mHelper.setEnabled(PKG_N_MR1, 1020, false);
+ assertEquals(1, mLogger.getCalls().size());
+ assertEquals(
+ NotificationChannelLogger.NotificationChannelEvent.APP_NOTIFICATIONS_BLOCKED,
+ mLogger.get(0).event);
+ }
+ @Test
public void testXml_statusBarIcons_default() throws Exception {
String preQXml = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d3f677ced329..8cf850736cb2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -79,6 +80,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
import android.annotation.SuppressLint;
import android.app.ActivityTaskManager;
@@ -1231,11 +1233,29 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testRecentsNotRotatingWithFixedRotation() {
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean());
+ doCallRealMethod().when(displayRotation).updateOrientation(anyInt(), anyBoolean());
+
+ final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS);
+ recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
+ displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
+ assertFalse(displayRotation.updateRotationUnchecked(false));
+
+ mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(false);
+ assertTrue(displayRotation.updateRotationUnchecked(false));
+ }
+
+ @Test
public void testRemoteRotation() {
DisplayContent dc = createNewDisplay();
final DisplayRotation dr = dc.getDisplayRotation();
- Mockito.doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
+ doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt());
final boolean[] continued = new boolean[1];
// TODO(display-merge): Remove cast
@@ -1287,7 +1307,6 @@ public class DisplayContentTests extends WindowTestsBase {
public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
DisplayContent display = createNewDisplay();
doReturn(true).when(display).supportsSystemDecorations();
- doReturn(false).when(display).isUntrustedVirtualDisplay();
// Remove the current home stack if it exists so a new one can be created below.
TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
@@ -1311,10 +1330,10 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
- public void testGetOrCreateRootHomeTask_untrustedVirtualDisplay() {
+ public void testGetOrCreateRootHomeTask_untrustedDisplay() {
DisplayContent display = createNewDisplay();
TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
- doReturn(true).when(display).isUntrustedVirtualDisplay();
+ doReturn(false).when(display).isTrusted();
assertNull(taskDisplayArea.getRootHomeTask());
assertNull(taskDisplayArea.getOrCreateRootHomeTask());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 15b395c8814e..e742b32ff4b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -251,6 +251,13 @@ public class SizeCompatTests extends ActivityTestsBase {
mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
+
+ // Recompute the natural configuration without resolving size compat configuration.
+ mActivity.clearSizeCompatMode();
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ // It should keep non-attachable because the resolved bounds will be computed according to
+ // the aspect ratio that won't match its parent bounds.
+ assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 512042cdf7b9..786f8d8af024 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -150,9 +150,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
@Test
public void testDisplayPositionWithPinnedStack() {
- // Make sure the display is system owned display which capable to move the stack to top.
+ // Make sure the display is trusted display which capable to move the stack to top.
spyOn(mDisplayContent);
- doReturn(false).when(mDisplayContent).isUntrustedVirtualDisplay();
+ doReturn(true).when(mDisplayContent).isTrusted();
// The display contains pinned stack that was added in {@link #setUp}.
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 4907bdc5e1f0..d6ec78837f7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -190,7 +190,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
public void testCalculateSnapshotFrame() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 0, 10);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
assertEquals(new Rect(0, 0, 100, 80),
mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
}
@@ -199,7 +199,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
public void testCalculateSnapshotFrame_navBarLeft() {
setupSurface(100, 100);
final Rect insets = new Rect(10, 10, 0, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
assertEquals(new Rect(10, 0, 100, 90),
mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
}
@@ -208,7 +208,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
public void testCalculateSnapshotFrame_waterfall() {
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
final Rect insets = new Rect(0, 10, 0, 10);
- mSurface.setFrames(new Rect(5, 0, 95, 100), insets, insets);
+ mSurface.setFrames(new Rect(5, 0, 95, 100), insets);
assertEquals(new Rect(0, 0, 90, 90),
mSurface.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
}
@@ -217,7 +217,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
public void testDrawStatusBarBackground() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
@@ -230,7 +230,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
public void testDrawStatusBarBackground_nullFrame() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
@@ -243,7 +243,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
public void testDrawStatusBarBackground_nope() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
@@ -257,7 +257,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
final Rect insets = new Rect(0, 10, 0, 10);
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
@@ -270,7 +270,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
final Rect insets = new Rect(10, 10, 0, 0);
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
@@ -283,7 +283,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
final Rect insets = new Rect(0, 10, 10, 0);
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
- mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
+ mSurface.setFrames(new Rect(0, 0, 100, 100), insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index e9ed20bd9683..4a0f48cf2ccb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -244,6 +245,12 @@ public class WindowStateTests extends WindowTestsBase {
appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
assertTrue(appWindow.canBeImeTarget());
+ // Verify PINNED windows can't be IME target.
+ int initialMode = appWindow.mActivityRecord.getWindowingMode();
+ appWindow.mActivityRecord.setWindowingMode(WINDOWING_MODE_PINNED);
+ assertFalse(appWindow.canBeImeTarget());
+ appWindow.mActivityRecord.setWindowingMode(initialMode);
+
// Make windows invisible
appWindow.hideLw(false /* doAnimation */);
imeWindow.hideLw(false /* doAnimation */);
@@ -646,6 +653,7 @@ public class WindowStateTests extends WindowTestsBase {
final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
win1.mHasSurface = true;
win1.mSurfaceControl = mock(SurfaceControl.class);
+ win1.mAttrs.surfaceInsets.set(1, 2, 3, 4);
win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0);
win1.updateSurfacePosition(t);
win1.getTransformationMatrix(values, matrix);
@@ -692,4 +700,14 @@ public class WindowStateTests extends WindowTestsBase {
sameTokenWindow.removeImmediately();
assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
}
+
+ @Test
+ public void testNeedsRelativeLayeringToIme_startingWindow() {
+ WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
+ mAppWindow.mToken, "SameTokenWindow");
+ mDisplayContent.mInputMethodTarget = mAppWindow;
+ sameTokenWindow.mActivityRecord.getStack().setWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
+ }
}
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 0fc9be32f4cf..6eba62e63740 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.net.RouteInfo.RTN_THROW;
+import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
@@ -1282,4 +1284,20 @@ public class LinkPropertiesTest {
assertTrue(lp.hasIpv6UnreachableDefaultRoute());
assertFalse(lp.hasIpv4UnreachableDefaultRoute());
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testRouteAddWithSameKey() throws Exception {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan0");
+ final IpPrefix v6 = new IpPrefix("64:ff9b::/96");
+ lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1280));
+ assertEquals(1, lp.getRoutes().size());
+ lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1500));
+ assertEquals(1, lp.getRoutes().size());
+ final IpPrefix v4 = new IpPrefix("192.0.2.128/25");
+ lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_UNICAST, 1460));
+ assertEquals(2, lp.getRoutes().size());
+ lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_THROW, 1460));
+ assertEquals(2, lp.getRoutes().size());
+ }
}
diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java
index 8204b494bbb8..60cac0b6b0f5 100644
--- a/tests/net/common/java/android/net/RouteInfoTest.java
+++ b/tests/net/common/java/android/net/RouteInfoTest.java
@@ -25,6 +25,7 @@ import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -56,7 +57,7 @@ public class RouteInfoTest {
private static final int INVALID_ROUTE_TYPE = -1;
private InetAddress Address(String addr) {
- return InetAddress.parseNumericAddress(addr);
+ return InetAddresses.parseNumericAddress(addr);
}
private IpPrefix Prefix(String prefix) {
@@ -391,4 +392,43 @@ public class RouteInfoTest {
r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0");
assertEquals(0, r.getMtu());
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testRouteKey() {
+ RouteInfo.RouteKey k1, k2;
+ // Only prefix, null gateway and null interface
+ k1 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey();
+ k2 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey();
+ assertEquals(k1, k2);
+ assertEquals(k1.hashCode(), k2.hashCode());
+
+ // With prefix, gateway and interface. Type and MTU does not affect RouteKey equality
+ k1 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0",
+ RTN_UNREACHABLE, 1450).getRouteKey();
+ k2 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0",
+ RouteInfo.RTN_UNICAST, 1400).getRouteKey();
+ assertEquals(k1, k2);
+ assertEquals(k1.hashCode(), k2.hashCode());
+
+ // Different scope IDs are ignored by the kernel, so we consider them equal here too.
+ k1 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%1"), "wlan0").getRouteKey();
+ k2 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%2"), "wlan0").getRouteKey();
+ assertEquals(k1, k2);
+ assertEquals(k1.hashCode(), k2.hashCode());
+
+ // Different prefix
+ k1 = new RouteInfo(Prefix("192.0.2.0/24"), null).getRouteKey();
+ k2 = new RouteInfo(Prefix("192.0.3.0/24"), null).getRouteKey();
+ assertNotEquals(k1, k2);
+
+ // Different gateway
+ k1 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::1"), null).getRouteKey();
+ k2 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::2"), null).getRouteKey();
+ assertNotEquals(k1, k2);
+
+ // Different interface
+ k1 = new RouteInfo(Prefix("ff02::1/128"), null, "tun0").getRouteKey();
+ k2 = new RouteInfo(Prefix("ff02::1/128"), null, "tun1").getRouteKey();
+ assertNotEquals(k1, k2);
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c86d388a3db9..385005f90c3b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -100,6 +100,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
@@ -164,6 +165,8 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
@@ -6800,6 +6803,30 @@ public class ConnectivityServiceTest {
assertEquals(wifiLp, mService.getActiveLinkProperties());
}
+ @Test
+ public void testLegacyExtraInfoSentToNetworkMonitor() throws Exception {
+ class TestNetworkAgent extends NetworkAgent {
+ TestNetworkAgent(Context context, Looper looper, NetworkAgentConfig config) {
+ super(context, looper, "MockAgent", new NetworkCapabilities(),
+ new LinkProperties(), 40 , config, null /* provider */);
+ }
+ }
+ final NetworkAgent naNoExtraInfo = new TestNetworkAgent(
+ mServiceContext, mCsHandlerThread.getLooper(), new NetworkAgentConfig());
+ naNoExtraInfo.register();
+ verify(mNetworkStack).makeNetworkMonitor(any(), isNull(String.class), any());
+ naNoExtraInfo.unregister();
+
+ reset(mNetworkStack);
+ final NetworkAgentConfig config =
+ new NetworkAgentConfig.Builder().setLegacyExtraInfo("legacyinfo").build();
+ final NetworkAgent naExtraInfo = new TestNetworkAgent(
+ mServiceContext, mCsHandlerThread.getLooper(), config);
+ naExtraInfo.register();
+ verify(mNetworkStack).makeNetworkMonitor(any(), eq("legacyinfo"), any());
+ naExtraInfo.unregister();
+ }
+
private void setupLocationPermissions(
int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
final ApplicationInfo applicationInfo = new ApplicationInfo();