diff options
45 files changed, 943 insertions, 264 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index cc7d97a6ee50..2ff1f766ad8f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -14204,7 +14204,8 @@ package android.telecom { method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); - method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean); + method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle); + method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED"; diff --git a/core/java/Android.bp b/core/java/Android.bp index eba500dd32b4..34b8a878b3d1 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -126,6 +126,7 @@ filegroup { srcs: [ "android/os/IExternalVibrationController.aidl", "android/os/IExternalVibratorService.aidl", + "android/os/ExternalVibrationScale.aidl", ], } diff --git a/core/java/android/os/ExternalVibrationScale.aidl b/core/java/android/os/ExternalVibrationScale.aidl new file mode 100644 index 000000000000..cf6f8ed52f7d --- /dev/null +++ b/core/java/android/os/ExternalVibrationScale.aidl @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * ExternalVibrationScale holds the vibration scale level and adaptive haptics scale. These + * can be used to scale external vibrations. + * + * @hide + */ +parcelable ExternalVibrationScale { + @Backing(type="int") + enum ScaleLevel { + SCALE_MUTE = -100, + SCALE_VERY_LOW = -2, + SCALE_LOW = -1, + SCALE_NONE = 0, + SCALE_HIGH = 1, + SCALE_VERY_HIGH = 2 + } + + /** + * The scale level that will be applied to external vibrations. + */ + ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE; + + /** + * The adaptive haptics scale that will be applied to external vibrations. + */ + float adaptiveHapticsScale = 1f; +} diff --git a/core/java/android/os/IExternalVibratorService.aidl b/core/java/android/os/IExternalVibratorService.aidl index 666171fcbcb3..a9df15a088bf 100644 --- a/core/java/android/os/IExternalVibratorService.aidl +++ b/core/java/android/os/IExternalVibratorService.aidl @@ -17,6 +17,7 @@ package android.os; import android.os.ExternalVibration; +import android.os.ExternalVibrationScale; /** * The communication channel by which an external system that wants to control the system @@ -32,29 +33,24 @@ import android.os.ExternalVibration; * {@hide} */ interface IExternalVibratorService { - const int SCALE_MUTE = -100; - const int SCALE_VERY_LOW = -2; - const int SCALE_LOW = -1; - const int SCALE_NONE = 0; - const int SCALE_HIGH = 1; - const int SCALE_VERY_HIGH = 2; - /** * A method called by the external system to start a vibration. * - * If this returns {@code SCALE_MUTE}, then the vibration should <em>not</em> play. If this - * returns any other scale level, then any currently playing vibration controlled by the - * requesting system must be muted and this vibration can begin playback. + * This returns an {@link ExternalVibrationScale} which includes the vibration scale level and + * the adaptive haptics scale. + * + * If the returned scale level is {@link ExternalVibrationScale.ScaleLevel#SCALE_MUTE}, then + * the vibration should <em>not</em> play. If it returns any other scale level, then + * any currently playing vibration controlled by the requesting system must be muted and this + * vibration can begin playback. * * Note that the IExternalVibratorService implementation will not call mute on any currently * playing external vibrations in order to avoid re-entrancy with the system on the other side. * - * @param vibration An ExternalVibration - * - * @return {@code SCALE_MUTE} if the external vibration should not play, and any other scale - * level if it should. + * @param vib The external vibration starting. + * @return {@link ExternalVibrationScale} including scale level and adaptive haptics scale. */ - int onExternalVibrationStart(in ExternalVibration vib); + ExternalVibrationScale onExternalVibrationStart(in ExternalVibration vib); /** * A method called by the external system when a vibration no longer wants to play. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 383d631c1713..596c52dcfdf6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10893,8 +10893,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setAutofillId(new AutofillId(getAutofillId(), AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()))); } - structure.setCredentialManagerRequest(getCredentialManagerRequest(), - getCredentialManagerCallback()); + if (getViewCredentialHandler() != null) { + structure.setCredentialManagerRequest( + getViewCredentialHandler().getRequest(), + getViewCredentialHandler().getCallback()); + } CharSequence cname = info.getClassName(); structure.setClassName(cname != null ? cname.toString() : null); structure.setContentDescription(info.getContentDescription()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index e0f0556d03f0..369258e15454 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -683,6 +683,17 @@ public class BubbleController implements ConfigurationChangeListener, mDataRepository.removeBubblesForUser(removedUserId, parentUserId); } + /** Called when sensitive notification state has changed */ + public void onSensitiveNotificationProtectionStateChanged( + boolean sensitiveNotificationProtectionActive) { + if (mStackView != null) { + mStackView.onSensitiveNotificationProtectionStateChanged( + sensitiveNotificationProtectionActive); + ProtoLog.d(WM_SHELL_BUBBLES, "onSensitiveNotificationProtectionStateChanged=%b", + sensitiveNotificationProtectionActive); + } + } + /** Whether bubbles are showing in the bubble bar. */ public boolean isShowingAsBubbleBar() { return canShowAsBubbleBar() && mBubbleStateListener != null; @@ -2583,6 +2594,14 @@ public class BubbleController implements ConfigurationChangeListener, mMainExecutor.execute( () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded)); } + + @Override + public void onSensitiveNotificationProtectionStateChanged( + boolean sensitiveNotificationProtectionActive) { + mMainExecutor.execute( + () -> BubbleController.this.onSensitiveNotificationProtectionStateChanged( + sensitiveNotificationProtectionActive)); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index b23fd5269eae..e7da034a7422 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -291,6 +291,11 @@ public class BubbleStackView extends FrameLayout */ private boolean mRemovingLastBubbleWhileExpanded = false; + /** + * Whether sensitive notification protection should disable flyout + */ + private boolean mSensitiveNotificationProtectionActive = false; + /** Animator for animating the expanded view's alpha (including the TaskView inside it). */ private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f); @@ -2199,6 +2204,11 @@ public class BubbleStackView extends FrameLayout } } + void onSensitiveNotificationProtectionStateChanged( + boolean sensitiveNotificationProtectionActive) { + mSensitiveNotificationProtectionActive = sensitiveNotificationProtectionActive; + } + /** * Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or * not. @@ -2842,6 +2852,7 @@ public class BubbleStackView extends FrameLayout || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress + || mSensitiveNotificationProtectionActive || mBubbleToExpandAfterFlyoutCollapse != null || bubbleView == null) { if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 28af0ca6ac6c..26077cf7057b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -286,6 +286,16 @@ public interface Bubbles { void onUserRemoved(int removedUserId); /** + * Called when the Sensitive notification protection state has changed, such as when media + * projection starts and stops. + * + * @param sensitiveNotificationProtectionActive {@code true} if notifications should be + * protected + */ + void onSensitiveNotificationProtectionStateChanged( + boolean sensitiveNotificationProtectionActive); + + /** * A listener to be notified of bubble state changes, used by launcher to render bubbles in * its process. */ diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 4156d833b0de..ce96d75da666 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -18,7 +18,9 @@ package com.android.systemui.communal.domain.interactor import android.app.smartspace.SmartspaceTarget +import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo +import android.os.UserHandle import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -51,6 +53,8 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.fakeUserTracker import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository import com.android.systemui.testKosmos @@ -96,6 +100,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter private lateinit var sceneInteractor: SceneInteractor + private lateinit var userTracker: FakeUserTracker private lateinit var underTest: CommunalInteractor @@ -113,6 +118,7 @@ class CommunalInteractorTest : SysuiTestCase() { editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter communalPrefsRepository = kosmos.fakeCommunalPrefsRepository sceneInteractor = kosmos.sceneInteractor + userTracker = kosmos.fakeUserTracker whenever(mainUser.isMain).thenReturn(true) whenever(secondaryUser.isMain).thenReturn(false) @@ -207,25 +213,19 @@ class CommunalInteractorTest : SysuiTestCase() { keyguardRepository.setKeyguardOccluded(false) tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) - // Widgets are available. - val widgets = - listOf( - CommunalWidgetContentModel( - appWidgetId = 0, - priority = 30, - providerInfo = mock(), - ), - CommunalWidgetContentModel( - appWidgetId = 1, - priority = 20, - providerInfo = mock(), - ), - CommunalWidgetContentModel( - appWidgetId = 2, - priority = 10, - providerInfo = mock(), - ), - ) + val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK) + userRepository.setUserInfos(userInfos) + userTracker.set( + userInfos = userInfos, + selectedUserIndex = 0, + ) + runCurrent() + + // Widgets available. + val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) + val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) + val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) + val widgets = listOf(widget1, widget2, widget3) widgetRepository.setCommunalWidgets(widgets) val widgetContent by collectLastValue(underTest.widgetContent) @@ -752,6 +752,38 @@ class CommunalInteractorTest : SysuiTestCase() { verify(editWidgetsActivityStarter).startActivity(widgetKey) } + @Test + fun filterWidgets_whenUserProfileRemoved() = + testScope.runTest { + // Keyguard showing, and tutorial completed. + keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setKeyguardOccluded(false) + tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED) + + // Only main user exists. + val userInfos = listOf(MAIN_USER_INFO) + userRepository.setUserInfos(userInfos) + userTracker.set( + userInfos = userInfos, + selectedUserIndex = 0, + ) + runCurrent() + + val widgetContent by collectLastValue(underTest.widgetContent) + // Given three widgets, and one of them is associated with pre-existing work profile. + val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) + val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) + val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) + val widgets = listOf(widget1, widget2, widget3) + widgetRepository.setCommunalWidgets(widgets) + + // One widget is filtered out and the remaining two link to main user id. + assertThat(checkNotNull(widgetContent).size).isEqualTo(2) + widgetContent!!.forEachIndexed { _, model -> + assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id) + } + } + private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget { val timer = mock(SmartspaceTarget::class.java) whenever(timer.smartspaceTargetId).thenReturn(id) @@ -760,4 +792,17 @@ class CommunalInteractorTest : SysuiTestCase() { whenever(timer.creationTimeMillis).thenReturn(timestamp) return timer } + + private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel = + mock<CommunalWidgetContentModel> { + whenever(this.appWidgetId).thenReturn(appWidgetId) + val providerInfo = mock<AppWidgetProviderInfo>() + whenever(providerInfo.profile).thenReturn(UserHandle(userId)) + whenever(this.providerInfo).thenReturn(providerInfo) + } + + private companion object { + val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) + val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index 352bacc56ca5..5ee88cb92fa0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget +import android.appwidget.AppWidgetProviderInfo +import android.content.pm.UserInfo +import android.os.UserHandle import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -39,6 +42,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.settings.fakeUserTracker import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository import com.android.systemui.testKosmos @@ -59,6 +63,7 @@ import org.mockito.MockitoAnnotations class CommunalEditModeViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock private lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var providerInfo: AppWidgetProviderInfo private val kosmos = testKosmos() private val testScope = kosmos.testScope @@ -78,6 +83,11 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { widgetRepository = kosmos.fakeCommunalWidgetRepository smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository + kosmos.fakeUserTracker.set( + userInfos = listOf(MAIN_USER_INFO), + selectedUserIndex = 0, + ) + whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) underTest = CommunalEditModeViewModel( @@ -100,12 +110,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, - providerInfo = mock(), + providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, - providerInfo = mock(), + providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) @@ -156,12 +166,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, - providerInfo = mock(), + providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, - providerInfo = mock(), + providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) @@ -205,4 +215,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { underTest.onReorderWidgetCancel() verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL) } + + private companion object { + val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index cc322d085acd..1e523dd2a9cc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -17,7 +17,9 @@ package com.android.systemui.communal.view.viewmodel import android.app.smartspace.SmartspaceTarget +import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo +import android.os.UserHandle import android.provider.Settings import android.widget.RemoteViews import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -45,13 +47,13 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost +import com.android.systemui.settings.fakeUserTracker import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.fakeUserRepository -import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -71,6 +73,7 @@ import org.mockito.MockitoAnnotations class CommunalViewModelTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock private lateinit var user: UserInfo + @Mock private lateinit var providerInfo: AppWidgetProviderInfo private val kosmos = testKosmos() private val testScope = kosmos.testScope @@ -98,6 +101,12 @@ class CommunalViewModelTest : SysuiTestCase() { kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) + kosmos.fakeUserTracker.set( + userInfos = listOf(MAIN_USER_INFO), + selectedUserIndex = 0, + ) + whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id)) + underTest = CommunalViewModel( testScope, @@ -147,12 +156,12 @@ class CommunalViewModelTest : SysuiTestCase() { CommunalWidgetContentModel( appWidgetId = 0, priority = 30, - providerInfo = mock(), + providerInfo = providerInfo, ), CommunalWidgetContentModel( appWidgetId = 1, priority = 20, - providerInfo = mock(), + providerInfo = providerInfo, ), ) widgetRepository.setCommunalWidgets(widgets) @@ -225,4 +234,8 @@ class CommunalViewModelTest : SysuiTestCase() { userRepository.setUserInfos(listOf(user)) userRepository.setSelectedUserInfo(user) } + + private companion object { + val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt index 8488843905f7..2c9d72c423bc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt @@ -16,7 +16,9 @@ package com.android.systemui.communal.widgets +import android.appwidget.AppWidgetProviderInfo import android.content.pm.UserInfo +import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_COMMUNAL_HUB @@ -32,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope +import com.android.systemui.settings.fakeUserTracker import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.mock @@ -65,7 +68,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO)) + kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO, USER_INFO_WORK)) kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true) mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB) @@ -76,6 +79,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { CommunalAppWidgetHostStartable( appWidgetHost, kosmos.communalInteractor, + kosmos.fakeUserTracker, kosmos.applicationCoroutineScope, kosmos.testDispatcher, ) @@ -170,6 +174,46 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { } } + @Test + fun removeWidgetsForDeletedProfile_whenCommunalIsAvailable() = + with(kosmos) { + testScope.runTest { + // Communal is available and work profile is configured. + setCommunalAvailable(true) + kosmos.fakeUserTracker.set( + userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK), + selectedUserIndex = 0, + ) + val widget1 = createWidgetForUser(1, USER_INFO_WORK.id) + val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id) + val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id) + val widgets = listOf(widget1, widget2, widget3) + fakeCommunalWidgetRepository.setCommunalWidgets(widgets) + + underTest.start() + runCurrent() + + val communalWidgets by + collectLastValue(fakeCommunalWidgetRepository.communalWidgets) + assertThat(communalWidgets).containsExactly(widget1, widget2, widget3) + + // Unlock the device and remove work profile. + fakeKeyguardRepository.setKeyguardShowing(false) + kosmos.fakeUserTracker.set( + userInfos = listOf(MAIN_USER_INFO), + selectedUserIndex = 0, + ) + runCurrent() + + // Communal becomes available. + fakeKeyguardRepository.setKeyguardShowing(true) + runCurrent() + + // Widget created for work profile is removed. + assertThat(communalWidgets).containsExactly(widget2, widget3) + } + } + private suspend fun setCommunalAvailable(available: Boolean) = with(kosmos) { fakeKeyguardRepository.setIsEncryptedOrLockdown(false) @@ -179,7 +223,16 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() { fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id) } + private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel = + mock<CommunalWidgetContentModel> { + whenever(this.appWidgetId).thenReturn(appWidgetId) + val providerInfo = mock<AppWidgetProviderInfo>() + whenever(providerInfo.profile).thenReturn(UserHandle(userId)) + whenever(this.providerInfo).thenReturn(providerInfo) + } + private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) + val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE) } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java index 00bbb20ed4f9..6af0fa069dbc 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java @@ -40,6 +40,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.media.MediaOutputConstants; import com.android.systemui.broadcast.BroadcastSender; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.media.controls.util.MediaDataUtils; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.res.R; @@ -74,7 +75,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate { private final SystemUIDialog.Factory mSystemUIDialogFactory; private final String mCurrentBroadcastApp; private final String mOutputPackageName; - private final Executor mExecutor; + private final Executor mBgExecutor; private boolean mShouldLaunchLeBroadcastDialog; private Button mSwitchBroadcast; @@ -159,7 +160,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate { MediaOutputDialogFactory mediaOutputDialogFactory, @Nullable LocalBluetoothManager localBluetoothManager, UiEventLogger uiEventLogger, - Executor executor, + @Background Executor bgExecutor, BroadcastSender broadcastSender, SystemUIDialog.Factory systemUIDialogFactory, @Assisted(CURRENT_BROADCAST_APP) String currentBroadcastApp, @@ -171,7 +172,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate { mCurrentBroadcastApp = currentBroadcastApp; mOutputPackageName = outputPkgName; mUiEventLogger = uiEventLogger; - mExecutor = executor; + mBgExecutor = bgExecutor; mBroadcastSender = broadcastSender; if (DEBUG) { @@ -187,7 +188,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate { @Override public void onStart(SystemUIDialog dialog) { mDialogs.add(dialog); - registerBroadcastCallBack(mExecutor, mBroadcastCallback); + registerBroadcastCallBack(mBgExecutor, mBroadcastCallback); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index d0044a4c029e..5397837423ff 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -29,6 +29,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.FULL import com.android.systemui.communal.shared.model.CommunalContentSize.HALF import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD import com.android.systemui.communal.shared.model.CommunalSceneKey +import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter @@ -45,6 +46,7 @@ import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlags import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.settings.UserTracker import com.android.systemui.smartspace.data.repository.SmartspaceRepository import com.android.systemui.util.kotlin.BooleanFlowOperators.and import com.android.systemui.util.kotlin.BooleanFlowOperators.not @@ -59,6 +61,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf @@ -82,6 +85,7 @@ constructor( communalSettingsInteractor: CommunalSettingsInteractor, private val appWidgetHost: CommunalAppWidgetHost, private val editWidgetsActivityStarter: EditWidgetsActivityStarter, + private val userTracker: UserTracker, sceneInteractor: SceneInteractor, sceneContainerFlags: SceneContainerFlags, @CommunalLog logBuffer: LogBuffer, @@ -262,10 +266,16 @@ constructor( fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) = widgetRepository.updateWidgetOrder(widgetIdToPriorityMap) + /** All widgets present in db. */ + val communalWidgets: Flow<List<CommunalWidgetContentModel>> = + isCommunalAvailable.flatMapLatest { available -> + if (!available) emptyFlow() else widgetRepository.communalWidgets + } + /** A list of widget content to be displayed in the communal hub. */ val widgetContent: Flow<List<CommunalContentModel.Widget>> = widgetRepository.communalWidgets.map { widgets -> - widgets.map Widget@{ widget -> + filterWidgetsByExistingUsers(widgets).map Widget@{ widget -> return@Widget CommunalContentModel.Widget( appWidgetId = widget.appWidgetId, providerInfo = widget.providerInfo, @@ -345,6 +355,19 @@ constructor( return@combine ongoingContent } + /** + * Filter and retain widgets associated with an existing user, safeguarding against displaying + * stale data following user deletion. + */ + private fun filterWidgetsByExistingUsers( + list: List<CommunalWidgetContentModel>, + ): List<CommunalWidgetContentModel> { + val currentUserIds = userTracker.userProfiles.map { it.id }.toSet() + return list.filter { widget -> + currentUserIds.contains(widget.providerInfo.profile?.identifier) + } + } + companion object { /** * The user activity timeout which should be used when the communal hub is opened. A value diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt index 4ddd7681dd98..8390d62b23db 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt @@ -18,11 +18,14 @@ package com.android.systemui.communal.widgets import com.android.systemui.CoreStartable import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.settings.UserTracker import com.android.systemui.util.kotlin.BooleanFlowOperators.or import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -37,6 +40,7 @@ class CommunalAppWidgetHostStartable constructor( private val appWidgetHost: CommunalAppWidgetHost, private val communalInteractor: CommunalInteractor, + private val userTracker: UserTracker, @Background private val bgScope: CoroutineScope, @Main private val uiDispatcher: CoroutineDispatcher ) : CoreStartable { @@ -47,6 +51,14 @@ constructor( .pairwise(false) .filter { (previous, new) -> previous != new } .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) } + .sample(communalInteractor.communalWidgets, ::Pair) + .onEach { (withPrev, widgets) -> + val (_, isActive) = withPrev + // The validation is performed once the hub becomes active. + if (isActive) { + validateWidgetsAndDeleteOrphaned(widgets) + } + } .launchIn(bgScope) appWidgetHost.appWidgetIdToRemove @@ -63,4 +75,15 @@ constructor( appWidgetHost.stopListening() } } + + /** + * Ensure the existence of all associated users for widgets, and remove widgets belonging to + * users who have been deleted. + */ + private fun validateWidgetsAndDeleteOrphaned(widgets: List<CommunalWidgetContentModel>) { + val currentUserIds = userTracker.userProfiles.map { it.id }.toSet() + widgets + .filter { widget -> !currentUserIds.contains(widget.providerInfo.profile?.identifier) } + .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) } + } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index 0f038e10dd4e..bc07b95c5d7f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -36,6 +36,7 @@ import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.VibratorHelper @@ -47,16 +48,16 @@ import javax.inject.Inject @SysUISingleton class ControlActionCoordinatorImpl @Inject constructor( - private val context: Context, - private val bgExecutor: DelayableExecutor, - @Main private val uiExecutor: DelayableExecutor, - private val activityStarter: ActivityStarter, - private val broadcastSender: BroadcastSender, - private val keyguardStateController: KeyguardStateController, - private val taskViewFactory: Optional<TaskViewFactory>, - private val controlsMetricsLogger: ControlsMetricsLogger, - private val vibrator: VibratorHelper, - private val controlsSettingsRepository: ControlsSettingsRepository, + private val context: Context, + @Background private val bgExecutor: DelayableExecutor, + @Main private val uiExecutor: DelayableExecutor, + private val activityStarter: ActivityStarter, + private val broadcastSender: BroadcastSender, + private val keyguardStateController: KeyguardStateController, + private val taskViewFactory: Optional<TaskViewFactory>, + private val controlsMetricsLogger: ControlsMetricsLogger, + private val vibrator: VibratorHelper, + private val controlsSettingsRepository: ControlsSettingsRepository, ) : ControlActionCoordinator { private var dialog: Dialog? = null private var pendingAction: Action? = null diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index e8931770b15e..1157d97f2f2e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -126,6 +126,7 @@ import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PolicyModule; +import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule; import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule; @@ -358,6 +359,7 @@ public abstract class SystemUIModule { VisualInterruptionDecisionProvider visualInterruptionDecisionProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, + SensitiveNotificationProtectionController sensitiveNotificationProtectionController, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, @@ -376,6 +378,7 @@ public abstract class SystemUIModule { visualInterruptionDecisionProvider, zenModeController, notifUserManager, + sensitiveNotificationProtectionController, notifCollection, notifPipeline, sysUiState, diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 416eae1b2d0c..4f062afc2af7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -72,7 +72,7 @@ open class MediaTttChipControllerReceiver @Inject constructor( context: Context, logger: MediaTttReceiverLogger, windowManager: WindowManager, - mainExecutor: DelayableExecutor, + @Main mainExecutor: DelayableExecutor, accessibilityManager: AccessibilityManager, configurationController: ConfigurationController, dumpManager: DumpManager, diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index f2fa0ef3f30f..125f7fc0619b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -121,7 +121,6 @@ open class UserTrackerImpl internal constructor( @GuardedBy("callbacks") private val callbacks: MutableList<DataItem> = ArrayList() - private var beforeUserSwitchingJob: Job? = null private var userSwitchingJob: Job? = null private var afterUserSwitchingJob: Job? = null @@ -194,14 +193,7 @@ open class UserTrackerImpl internal constructor( private fun registerUserSwitchObserver() { iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() { override fun onBeforeUserSwitching(newUserId: Int) { - if (isBackgroundUserSwitchEnabled) { - beforeUserSwitchingJob?.cancel() - beforeUserSwitchingJob = appScope.launch(backgroundContext) { - handleBeforeUserSwitching(newUserId) - } - } else { - handleBeforeUserSwitching(newUserId) - } + handleBeforeUserSwitching(newUserId) } override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) { @@ -233,15 +225,24 @@ open class UserTrackerImpl internal constructor( @WorkerThread protected open fun handleBeforeUserSwitching(newUserId: Int) { - Assert.isNotMainThread() setUserIdInternal(newUserId) val list = synchronized(callbacks) { callbacks.toList() } + val latch = CountDownLatch(list.size) list.forEach { - it.callback.get()?.onBeforeUserSwitching(newUserId) + val callback = it.callback.get() + if (callback != null) { + it.executor.execute { + callback.onBeforeUserSwitching(newUserId) + latch.countDown() + } + } else { + latch.countDown() + } } + latch.await() } @WorkerThread diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 8189fe03b2ed..dfe6cd5f25b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -25,6 +25,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -92,7 +93,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { @Inject public VisualStabilityCoordinator( - DelayableExecutor delayableExecutor, + @Background DelayableExecutor delayableExecutor, DumpManager dumpManager, HeadsUpManager headsUpManager, ShadeAnimationInteractor shadeAnimationInteractor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 4fd33ba458d8..5610ed926f70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -55,6 +55,7 @@ import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.power.domain.interactor.PowerInteractor; @@ -138,7 +139,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Context context, @DisplayId int displayId, Handler mainThreadHandler, - Executor uiBgExecutor, + @Background Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, HeadsUpManager headsUpManager, ActivityStarter activityStarter, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index f73d089c36b9..3e3ea855ccf7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -315,8 +315,8 @@ constructor( // TTL for satellite polling is one hour const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 60 - // Let the system boot up (5s) and stabilize before we check for system support - const val MIN_UPTIME: Long = 1000 * 5 + // Let the system boot up and stabilize before we check for system support + const val MIN_UPTIME: Long = 1000 * 60 private const val TAG = "DeviceBasedSatelliteRepo" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt index a078dd5cf28c..2ad4d361df1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt @@ -23,6 +23,7 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.net.Uri +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import com.android.systemui.util.concurrency.DelayableExecutor import javax.inject.Inject @@ -34,7 +35,7 @@ import javax.inject.Inject class BatteryStateNotifier @Inject constructor( val controller: BatteryController, val noMan: NotificationManager, - val delayableExecutor: DelayableExecutor, + @Background val delayableExecutor: DelayableExecutor, val context: Context ) : BatteryController.BatteryStateChangeCallback { var stateUnknown = false diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java index 6124f6383fff..2cad8442e3ba 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java @@ -124,15 +124,6 @@ public abstract class SysUIConcurrencyModule { } /** - * Provide a Background-Thread Executor by default. - */ - @Provides - @SysUISingleton - public static Executor provideExecutor(@Background Looper looper) { - return new ExecutorImpl(looper); - } - - /** * Provide a BroadcastRunning Executor (for sending and receiving broadcasts). */ @Provides @@ -174,15 +165,6 @@ public abstract class SysUIConcurrencyModule { } /** - * Provide a Background-Thread Executor by default. - */ - @Provides - @SysUISingleton - public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) { - return new ExecutorImpl(looper); - } - - /** * Provide a Background-Thread Executor. */ @Provides @@ -193,15 +175,6 @@ public abstract class SysUIConcurrencyModule { } /** - * Provide a Background-Thread Executor by default. - */ - @Provides - @SysUISingleton - public static RepeatableExecutor provideRepeatableExecutor(@Background DelayableExecutor exec) { - return new RepeatableExecutorImpl(exec); - } - - /** * Provide a Background-Thread Executor. */ @Provides diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java index 9b72eb710588..5979f3e60cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java +++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java @@ -28,6 +28,7 @@ import android.util.Log; import androidx.annotation.NonNull; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.SystemClock; @@ -94,10 +95,12 @@ public class PersistentConnectionManager<T> implements Dumpable { } }; + // TODO: b/326449074 - Ensure the DelayableExecutor is on the correct thread, and update the + // qualifier (to @Main) or name (to bgExecutor) to be consistent with that. @Inject public PersistentConnectionManager( SystemClock clock, - DelayableExecutor mainExecutor, + @Background DelayableExecutor mainExecutor, DumpManager dumpManager, @Named(DUMPSYS_NAME) String dumpsysName, @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 139d190ae63c..65dede83f3d6 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -25,6 +25,7 @@ import static android.service.notification.NotificationListenerService.REASON_GR import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; +import static com.android.server.notification.Flags.screenshareNotificationHiding; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -69,6 +70,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleEntry; @@ -102,6 +104,7 @@ public class BubblesManager { private final NotificationVisibilityProvider mVisibilityProvider; private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider; private final NotificationLockscreenUserManager mNotifUserManager; + private final SensitiveNotificationProtectionController mSensitiveNotifProtectionController; private final CommonNotifCollection mCommonNotifCollection; private final NotifPipeline mNotifPipeline; private final NotifPipelineFlags mNotifPipelineFlags; @@ -111,6 +114,7 @@ public class BubblesManager { // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline private final List<NotifCallback> mCallbacks = new ArrayList<>(); private final StatusBarWindowCallback mStatusBarWindowCallback; + private final Runnable mSensitiveStateChangedListener; private boolean mPanelExpanded; /** @@ -130,6 +134,7 @@ public class BubblesManager { VisualInterruptionDecisionProvider visualInterruptionDecisionProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, + SensitiveNotificationProtectionController sensitiveNotificationProtectionController, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, @@ -149,6 +154,7 @@ public class BubblesManager { visualInterruptionDecisionProvider, zenModeController, notifUserManager, + sensitiveNotificationProtectionController, notifCollection, notifPipeline, sysUiState, @@ -173,6 +179,7 @@ public class BubblesManager { VisualInterruptionDecisionProvider visualInterruptionDecisionProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, + SensitiveNotificationProtectionController sensitiveNotificationProtectionController, CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, @@ -188,6 +195,7 @@ public class BubblesManager { mVisibilityProvider = visibilityProvider; mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider; mNotifUserManager = notifUserManager; + mSensitiveNotifProtectionController = sensitiveNotificationProtectionController; mCommonNotifCollection = notifCollection; mNotifPipeline = notifPipeline; mNotifPipelineFlags = notifPipelineFlags; @@ -251,6 +259,22 @@ public class BubblesManager { }; notificationShadeWindowController.registerCallback(mStatusBarWindowCallback); + mSensitiveStateChangedListener = new Runnable() { + @Override + public void run() { + if (!screenshareNotificationHiding()) { + return; + } + bubbles.onSensitiveNotificationProtectionStateChanged( + mSensitiveNotifProtectionController.isSensitiveStateActive()); + } + }; + + if (screenshareNotificationHiding()) { + mSensitiveNotifProtectionController + .registerSensitiveStateListener(mSensitiveStateChangedListener); + } + mSysuiProxy = new Bubbles.SysuiProxy() { @Override public void isNotificationPanelExpand(Consumer<Boolean> callback) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt index 032ec7440923..774aa517672e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt @@ -371,7 +371,6 @@ class UserTrackerImplTest : SysuiTestCase() { val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java) verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString()) - captor.value.onBeforeUserSwitching(newID) captor.value.onUserSwitching(newID, userSwitchingReply) assertThat(callback.calledOnUserChanging).isEqualTo(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index b25ac24093c5..a9308601a314 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -26,6 +26,7 @@ import static android.service.notification.NotificationListenerService.REASON_AP import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING; import static com.google.common.truth.Truth.assertThat; @@ -46,6 +47,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static kotlinx.coroutines.flow.FlowKt.emptyFlow; @@ -73,6 +75,8 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.service.dreams.IDreamManager; import android.service.notification.NotificationListenerService; import android.service.notification.ZenModeConfig; @@ -161,6 +165,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; +import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository; @@ -257,6 +262,8 @@ public class BubblesTest extends SysuiTestCase { private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private AuthController mAuthController; + @Mock + private SensitiveNotificationProtectionController mSensitiveNotificationProtectionController; private SysUiState mSysUiState; private boolean mSysUiStateBubblesExpanded; @@ -272,6 +279,8 @@ public class BubblesTest extends SysuiTestCase { private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; @Captor private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor; + @Captor + private ArgumentCaptor<Runnable> mSensitiveStateChangedListener; private BubblesManager mBubblesManager; private TestableBubbleController mBubbleController; @@ -594,6 +603,7 @@ public class BubblesTest extends SysuiTestCase { interruptionDecisionProvider, mZenModeController, mLockscreenUserManager, + mSensitiveNotificationProtectionController, mCommonNotifCollection, mNotifPipeline, mSysUiState, @@ -2203,6 +2213,33 @@ public class BubblesTest extends SysuiTestCase { assertThat(mBubbleController.getLayerView().isExpanded()).isFalse(); } + @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) + @Test + public void doesNotRegisterSensitiveStateListener() { + verifyZeroInteractions(mSensitiveNotificationProtectionController); + } + + @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) + @Test + public void registerSensitiveStateListener() { + verify(mSensitiveNotificationProtectionController).registerSensitiveStateListener(any()); + } + + @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) + @Test + public void onSensitiveNotificationProtectionStateChanged() { + verify(mSensitiveNotificationProtectionController, atLeastOnce()) + .registerSensitiveStateListener(mSensitiveStateChangedListener.capture()); + + when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(true); + mSensitiveStateChangedListener.getValue().run(); + verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(true); + + when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(false); + mSensitiveStateChangedListener.getValue().run(); + verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(false); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt index 6af08d3df554..f74cf71f9e7b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt @@ -31,6 +31,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.log.logcatLogBuffer import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags +import com.android.systemui.settings.userTracker import com.android.systemui.smartspace.data.repository.smartspaceRepository import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.util.mockito.mock @@ -46,6 +47,7 @@ val Kosmos.communalInteractor by Fixture { appWidgetHost = mock(), keyguardInteractor = keyguardInteractor, editWidgetsActivityStarter = editWidgetsActivityStarter, + userTracker = userTracker, logBuffer = logcatLogBuffer("CommunalInteractor"), tableLogBuffer = mock(), communalSettingsInteractor = communalSettingsInteractor, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index fc7b87317344..3a7ac0bd659d 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -8206,7 +8206,7 @@ public class NotificationManagerService extends SystemService { try { return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall(pkg, - UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true); + /* hasCrossUserAccess */ true); } catch (IllegalStateException ise) { // Telecom is not ready (this is likely early boot), so there are no calls. return false; diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index 7091c47b8e83..ecfc040ae29c 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -17,6 +17,7 @@ package com.android.server.search; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.ISearchManager; import android.app.SearchManager; import android.app.SearchableInfo; @@ -24,6 +25,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.os.Binder; @@ -32,6 +34,7 @@ import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -47,6 +50,7 @@ import com.android.server.statusbar.StatusBarManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; /** @@ -71,11 +75,6 @@ public class SearchManagerService extends ISearchManager.Stub { } @Override - public void onUserUnlocking(@NonNull TargetUser user) { - mService.mHandler.post(() -> mService.onUnlockUser(user.getUserIdentifier())); - } - - @Override public void onUserStopped(@NonNull TargetUser user) { mService.onCleanupUser(user.getUserIdentifier()); } @@ -102,10 +101,6 @@ public class SearchManagerService extends ISearchManager.Stub { } private Searchables getSearchables(int userId) { - return getSearchables(userId, false); - } - - private Searchables getSearchables(int userId, boolean forceUpdate) { final long token = Binder.clearCallingIdentity(); try { final UserManager um = mContext.getSystemService(UserManager.class); @@ -122,21 +117,11 @@ public class SearchManagerService extends ISearchManager.Stub { Searchables searchables = mSearchables.get(userId); if (searchables == null) { searchables = new Searchables(mContext, userId); - searchables.updateSearchableList(); - mSearchables.append(userId, searchables); - } else if (forceUpdate) { - searchables.updateSearchableList(); + mSearchables.put(userId, searchables); } - return searchables; - } - } - private void onUnlockUser(int userId) { - try { - getSearchables(userId, true); - } catch (IllegalStateException ignored) { - // We're just trying to warm a cache, so we don't mind if the user - // was stopped or destroyed before we got here. + searchables.updateSearchableListIfNeeded(); + return searchables; } } @@ -150,28 +135,110 @@ public class SearchManagerService extends ISearchManager.Stub { * Refreshes the "searchables" list when packages are added/removed. */ class MyPackageMonitor extends PackageMonitor { + /** + * Packages that are appeared, disappeared, or modified for whatever reason. + */ + private final ArrayList<String> mChangedPackages = new ArrayList<>(); + + /** + * {@code true} if one or more packages that contain {@link SearchableInfo} appeared. + */ + private boolean mSearchablePackageAppeared = false; + + @Override + public void onBeginPackageChanges() { + clearPackageChangeState(); + } + + @Override + public void onPackageAppeared(String packageName, int reason) { + if (!mSearchablePackageAppeared) { + // Check if the new appeared package contains SearchableInfo. + mSearchablePackageAppeared = + hasSearchableForPackage(packageName, getChangingUserId()); + } + mChangedPackages.add(packageName); + } @Override - public void onSomePackagesChanged() { - updateSearchables(); + public void onPackageDisappeared(String packageName, int reason) { + mChangedPackages.add(packageName); } @Override - public void onPackageModified(String pkg) { - updateSearchables(); + public void onPackageModified(String packageName) { + mChangedPackages.add(packageName); } - private void updateSearchables() { + @Override + public void onFinishPackageChanges() { + onFinishPackageChangesInternal(); + clearPackageChangeState(); + } + + private void clearPackageChangeState() { + mChangedPackages.clear(); + mSearchablePackageAppeared = false; + } + + private boolean hasSearchableForPackage(String packageName, int userId) { + final List<ResolveInfo> searchList = querySearchableActivities(mContext, + new Intent(Intent.ACTION_SEARCH).setPackage(packageName), userId); + if (!searchList.isEmpty()) { + return true; + } + + final List<ResolveInfo> webSearchList = querySearchableActivities(mContext, + new Intent(Intent.ACTION_WEB_SEARCH).setPackage(packageName), userId); + if (!webSearchList.isEmpty()) { + return true; + } + + final List<ResolveInfo> globalSearchList = querySearchableActivities(mContext, + new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH).setPackage(packageName), + userId); + return !globalSearchList.isEmpty(); + } + + private boolean shouldRebuildSearchableList(@UserIdInt int changingUserId) { + // This method is guaranteed to be called only on getRegisteredHandler() + if (mSearchablePackageAppeared) { + return true; + } + + ArraySet<String> knownSearchablePackageNames = new ArraySet<>(); + synchronized (mSearchables) { + Searchables searchables = mSearchables.get(changingUserId); + if (searchables != null) { + knownSearchablePackageNames = searchables.getKnownSearchablePackageNames(); + } + } + + final int numOfPackages = mChangedPackages.size(); + for (int i = 0; i < numOfPackages; i++) { + final String packageName = mChangedPackages.get(i); + if (knownSearchablePackageNames.contains(packageName)) { + return true; + } + } + + return false; + } + + private void onFinishPackageChangesInternal() { final int changingUserId = getChangingUserId(); + if (!shouldRebuildSearchableList(changingUserId)) { + return; + } + synchronized (mSearchables) { - // Update list of searchable activities - for (int i = 0; i < mSearchables.size(); i++) { - if (changingUserId == mSearchables.keyAt(i)) { - mSearchables.valueAt(i).updateSearchableList(); - break; - } + // Invalidate the searchable list. + Searchables searchables = mSearchables.get(changingUserId); + if (searchables != null) { + searchables.invalidateSearchableList(); } } + // Inform all listeners that the list of searchables has been updated. Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING @@ -180,6 +247,17 @@ public class SearchManagerService extends ISearchManager.Stub { } } + @NonNull + static List<ResolveInfo> querySearchableActivities(Context context, Intent searchIntent, + @UserIdInt int userId) { + final List<ResolveInfo> activities = context.getPackageManager() + .queryIntentActivitiesAsUser(searchIntent, PackageManager.GET_META_DATA + | PackageManager.MATCH_INSTANT + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); + return activities; + } + + class GlobalSearchProviderObserver extends ContentObserver { private final ContentResolver mResolver; @@ -196,7 +274,7 @@ public class SearchManagerService extends ISearchManager.Stub { public void onChange(boolean selfChange) { synchronized (mSearchables) { for (int i = 0; i < mSearchables.size(); i++) { - mSearchables.valueAt(i).updateSearchableList(); + mSearchables.valueAt(i).invalidateSearchableList(); } } Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java index 7b397755173d..dc6733941357 100644 --- a/services/core/java/com/android/server/search/Searchables.java +++ b/services/core/java/com/android/server/search/Searchables.java @@ -35,8 +35,10 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import java.io.FileDescriptor; @@ -62,7 +64,6 @@ public class Searchables { private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*"; private Context mContext; - private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; private ArrayList<SearchableInfo> mSearchablesList = null; private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; @@ -81,6 +82,12 @@ public class Searchables { final private IPackageManager mPm; // User for which this Searchables caches information private int mUserId; + @GuardedBy("this") + private boolean mRebuildSearchables = true; + + // Package names that are known to contain {@link SearchableInfo} + @GuardedBy("this") + private ArraySet<String> mKnownSearchablePackageNames = new ArraySet<>(); /** * @@ -224,7 +231,14 @@ public class Searchables { * * TODO: sort the list somehow? UI choice. */ - public void updateSearchableList() { + public void updateSearchableListIfNeeded() { + synchronized (this) { + if (!mRebuildSearchables) { + // The searchable list is valid, no need to rebuild. + return; + } + } + // These will become the new values at the end of the method HashMap<ComponentName, SearchableInfo> newSearchablesMap = new HashMap<ComponentName, SearchableInfo>(); @@ -232,6 +246,7 @@ public class Searchables { = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); + ArraySet<String> newKnownSearchablePackageNames = new ArraySet<>(); // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. List<ResolveInfo> searchList; @@ -264,6 +279,7 @@ public class Searchables { mUserId); if (searchable != null) { newSearchablesList.add(searchable); + newKnownSearchablePackageNames.add(ai.packageName); newSearchablesMap.put(searchable.getSearchActivity(), searchable); if (searchable.shouldIncludeInGlobalSearch()) { newSearchablesInGlobalSearchList.add(searchable); @@ -286,16 +302,41 @@ public class Searchables { synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; + mKnownSearchablePackageNames = newKnownSearchablePackageNames; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; mGlobalSearchActivities = newGlobalSearchActivities; mCurrentGlobalSearchActivity = newGlobalSearchActivity; mWebSearchActivity = newWebSearchActivity; + for (ResolveInfo globalSearchActivity: mGlobalSearchActivities) { + mKnownSearchablePackageNames.add( + globalSearchActivity.getComponentInfo().packageName); + } + if (mCurrentGlobalSearchActivity != null) { + mKnownSearchablePackageNames.add( + mCurrentGlobalSearchActivity.getPackageName()); + } + if (mWebSearchActivity != null) { + mKnownSearchablePackageNames.add(mWebSearchActivity.getPackageName()); + } + + mRebuildSearchables = false; } } finally { Binder.restoreCallingIdentity(ident); } } + synchronized ArraySet<String> getKnownSearchablePackageNames() { + return mKnownSearchablePackageNames; + } + + synchronized void invalidateSearchableList() { + mRebuildSearchables = true; + + // Don't rebuild the searchable list, it will be rebuilt + // when the next updateSearchableList gets called. + } + /** * Returns a sorted list of installed search providers as per * the following heuristics: @@ -532,6 +573,8 @@ public class Searchables { pw.print(" "); pw.println(info.getSuggestAuthority()); } } + + pw.println("mRebuildSearchables = " + mRebuildSearchables); } } } diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java index 7163319f281a..5d17884c769b 100644 --- a/services/core/java/com/android/server/vibrator/VibrationScaler.java +++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java @@ -19,7 +19,7 @@ package com.android.server.vibrator; import android.annotation.NonNull; import android.content.Context; import android.hardware.vibrator.V1_0.EffectStrength; -import android.os.IExternalVibratorService; +import android.os.ExternalVibrationScale; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; @@ -37,11 +37,13 @@ final class VibrationScaler { // Scale levels. Each level, except MUTE, is defined as the delta between the current setting // and the default intensity for that type of vibration (i.e. current - default). - private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2 - private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1 - private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0 - private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1 - private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2 + private static final int SCALE_VERY_LOW = + ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2 + private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1 + private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0 + private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1 + private static final int SCALE_VERY_HIGH = + ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2 // Scale factors for each level. private static final float SCALE_FACTOR_VERY_LOW = 0.6f; @@ -83,9 +85,9 @@ final class VibrationScaler { * Calculates the scale to be applied to external vibration with given usage. * * @param usageHint one of VibrationAttributes.USAGE_* - * @return one of IExternalVibratorService.SCALE_* + * @return one of ExternalVibrationScale.ScaleLevel.SCALE_* */ - public int getExternalVibrationScale(int usageHint) { + public int getExternalVibrationScaleLevel(int usageHint) { int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint); int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); @@ -107,6 +109,22 @@ final class VibrationScaler { } /** + * Returns the adaptive haptics scale that should be applied to the vibrations with + * the given usage. When no adaptive scales are available for the usages, then returns 1 + * indicating no scaling will be applied + * + * @param usageHint one of VibrationAttributes.USAGE_* + * @return The adaptive haptics scale. + */ + public float getAdaptiveHapticsScale(int usageHint) { + if (shouldApplyAdaptiveHapticsScale(usageHint)) { + return mAdaptiveHapticsScales.get(usageHint); + } + + return 1f; // no scaling + } + + /** * Scale a {@link VibrationEffect} based on the given usage hint for this vibration. * * @param effect the effect to be scaled @@ -152,9 +170,7 @@ final class VibrationScaler { } // If adaptive haptics scaling is available for this usage, apply it to the segment. - if (Flags.adaptiveHapticsEnabled() - && mAdaptiveHapticsScales.size() > 0 - && mAdaptiveHapticsScales.contains(usageHint)) { + if (shouldApplyAdaptiveHapticsScale(usageHint)) { float adaptiveScale = mAdaptiveHapticsScales.get(usageHint); segment = segment.scaleLinearly(adaptiveScale); } @@ -224,6 +240,10 @@ final class VibrationScaler { mAdaptiveHapticsScales.clear(); } + private boolean shouldApplyAdaptiveHapticsScale(int usageHint) { + return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint); + } + /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */ private static int intensityToEffectStrength(int intensity) { switch (intensity) { diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index be5d15877e32..78e0ebbb53fa 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE; import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; import static android.os.VibrationEffect.VibrationParameter.targetFrequency; @@ -35,6 +36,7 @@ import android.os.Binder; import android.os.Build; import android.os.CombinedVibration; import android.os.ExternalVibration; +import android.os.ExternalVibrationScale; import android.os.Handler; import android.os.IBinder; import android.os.IExternalVibratorService; @@ -277,7 +279,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); - if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) { + if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) { injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService); } @@ -1427,6 +1429,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { VibratorControllerHolder createVibratorControllerHolder() { return new VibratorControllerHolder(); } + + boolean isServiceDeclared(String name) { + return ServiceManager.isDeclared(name); + } } /** @@ -1594,7 +1600,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { IBinder.DeathRecipient { public final ExternalVibration externalVibration; - public int scale; + public ExternalVibrationScale scale = new ExternalVibrationScale(); private Vibration.Status mStatus; @@ -1605,7 +1611,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // instead of using DEVICE_ID_INVALID here and relying on the UID checks. Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null)); this.externalVibration = externalVibration; - this.scale = IExternalVibratorService.SCALE_NONE; mStatus = Vibration.Status.RUNNING; } @@ -1658,7 +1663,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { public Vibration.DebugInfo getDebugInfo() { return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null, - /* originalEffect= */ null, scale, callerInfo); + /* originalEffect= */ null, scale.scaleLevel, callerInfo); } public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { @@ -1988,11 +1993,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */ @VisibleForTesting final class ExternalVibratorService extends IExternalVibratorService.Stub { + private static final ExternalVibrationScale SCALE_MUTE = new ExternalVibrationScale(); + + static { + SCALE_MUTE.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE; + } @Override - public int onExternalVibrationStart(ExternalVibration vib) { + public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) { + if (!hasExternalControlCapability()) { - return IExternalVibratorService.SCALE_MUTE; + return SCALE_MUTE; } if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, vib.getUid(), -1 /*owningUid*/, true /*exported*/) @@ -2000,7 +2011,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid() + " tried to play externally controlled vibration" + " without VIBRATE permission, ignoring."); - return IExternalVibratorService.SCALE_MUTE; + return SCALE_MUTE; } // Create Vibration.Stats as close to the received request as possible, for tracking. @@ -2033,7 +2044,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } if (vibrationEndInfo != null) { - vibHolder.scale = IExternalVibratorService.SCALE_MUTE; + vibHolder.scale = SCALE_MUTE; // Failed to start the vibration, end it and report metrics right away. endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo); return vibHolder.scale; @@ -2074,7 +2085,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } mCurrentExternalVibration = vibHolder; vibHolder.linkToDeath(); - vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage()); + vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel( + attrs.getUsage()); + vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale( + attrs.getUsage()); } if (waitForCompletion) { @@ -2086,7 +2100,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING), /* continueExternalControl= */ false); } - return IExternalVibratorService.SCALE_MUTE; + return SCALE_MUTE; } } if (!alreadyUnderExternalControl) { diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp index b7af58c0fd54..3bce9b54e320 100644 --- a/services/tests/InputMethodSystemServerTests/Android.bp +++ b/services/tests/InputMethodSystemServerTests/Android.bp @@ -69,7 +69,7 @@ android_test { } android_ravenwood_test { - name: "FrameworksInputMethodSystemServerTests_host", + name: "FrameworksInputMethodSystemServerTestsRavenwood", static_libs: [ "androidx.annotation_annotation", "androidx.test.rules", diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java index f5c6795484fa..771a76517b22 100644 --- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java @@ -92,7 +92,7 @@ public class SearchablesTest { public void testNonSearchable() { // test basic array & hashmap Searchables searchables = new Searchables(mContext, 0); - searchables.updateSearchableList(); + searchables.updateSearchableListIfNeeded(); // confirm that we return null for non-searchy activities ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests", @@ -121,7 +121,7 @@ public class SearchablesTest { doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); Searchables searchables = new Searchables(mContext, 0); - searchables.updateSearchableList(); + searchables.updateSearchableListIfNeeded(); // tests with "real" searchables (deprecate, this should be a unit test) ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); int count = searchablesList.size(); @@ -139,7 +139,7 @@ public class SearchablesTest { doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); Searchables searchables = new Searchables(mContext, 0); - searchables.updateSearchableList(); + searchables.updateSearchableListIfNeeded(); ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); assertNotNull(searchablesList); MoreAsserts.assertEmpty(searchablesList); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index cff7f460feb5..715c9d4081b2 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -12008,7 +12008,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // style + self managed call - bypasses block when(mTelecomManager.isInSelfManagedCall( - r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true); + r.getSbn().getPackageName(), true)).thenReturn(true); assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue(); @@ -12091,7 +12091,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // style + self managed call - bypasses block mService.clearNotifications(); reset(mUsageStats); - when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true)) + when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), true)) .thenReturn(true); mService.addEnqueuedNotification(r); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java index b431888a72fb..3e59878f9e1e 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java @@ -35,8 +35,8 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContextWrapper; import android.content.pm.PackageManagerInternal; +import android.os.ExternalVibrationScale; import android.os.Handler; -import android.os.IExternalVibratorService; import android.os.PowerManagerInternal; import android.os.UserHandle; import android.os.VibrationAttributes; @@ -49,6 +49,7 @@ import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -119,29 +120,65 @@ public class VibrationScalerTest { public void testGetExternalVibrationScale() { setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); - assertEquals(IExternalVibratorService.SCALE_VERY_HIGH, - mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH, + mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM); - assertEquals(IExternalVibratorService.SCALE_HIGH, - mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH, + mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW); - assertEquals(IExternalVibratorService.SCALE_NONE, - mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE, + mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM); - assertEquals(IExternalVibratorService.SCALE_LOW, - mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW, + mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH); - assertEquals(IExternalVibratorService.SCALE_VERY_LOW, - mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW, + mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); // Vibration setting being bypassed will use default setting and not scale. - assertEquals(IExternalVibratorService.SCALE_NONE, - mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE, + mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + public void testAdaptiveHapticsScale_withAdaptiveHapticsAvailable() { + setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW); + setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); + + mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f); + mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f); + + assertEquals(0.5f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH)); + assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE)); + assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION)); + + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); + // Vibration setting being bypassed will apply adaptive haptics scales. + assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE)); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + public void testAdaptiveHapticsScale_flagDisabled_adaptiveHapticScaleAlwaysNone() { + setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW); + setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); + + mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f); + mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f); + + assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH)); + assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE)); + assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION)); } @Test diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java index 2823223e4859..417fbd06be66 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 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. @@ -35,7 +35,6 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.pm.PackageManagerInternal; import android.frameworks.vibrator.ScaleParam; -import android.frameworks.vibrator.VibrationParam; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -55,8 +54,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -135,7 +132,7 @@ public class VibratorControlServiceTest { vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); mVibratorControlService.onRequestVibrationParamsComplete(token, - generateVibrationParams(vibrationScales)); + VibrationParamGenerator.generateVibrationParams(vibrationScales)); verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f); verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_NOTIFICATION, 0.4f); @@ -162,7 +159,7 @@ public class VibratorControlServiceTest { vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); mVibratorControlService.onRequestVibrationParamsComplete(new Binder(), - generateVibrationParams(vibrationScales)); + VibrationParamGenerator.generateVibrationParams(vibrationScales)); verifyZeroInteractions(mMockVibrationScaler); } @@ -175,7 +172,8 @@ public class VibratorControlServiceTest { vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f); vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); - mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales), + mVibratorControlService.setVibrationParams( + VibrationParamGenerator.generateVibrationParams(vibrationScales), mFakeVibratorController); verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f); @@ -193,7 +191,8 @@ public class VibratorControlServiceTest { vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f); vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); - mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales), + mVibratorControlService.setVibrationParams( + VibrationParamGenerator.generateVibrationParams(vibrationScales), mFakeVibratorController); verifyZeroInteractions(mMockVibrationScaler); @@ -268,28 +267,6 @@ public class VibratorControlServiceTest { } } - private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) { - List<VibrationParam> vibrationParamList = new ArrayList<>(); - for (int i = 0; i < vibrationScales.size(); i++) { - int type = vibrationScales.keyAt(i); - float scale = vibrationScales.valueAt(i); - - vibrationParamList.add(generateVibrationParam(type, scale)); - } - - return vibrationParamList.toArray(new VibrationParam[0]); - } - - private VibrationParam generateVibrationParam(int type, float scale) { - ScaleParam scaleParam = new ScaleParam(); - scaleParam.typesMask = type; - scaleParam.scale = scale; - VibrationParam vibrationParam = new VibrationParam(); - vibrationParam.setScale(scaleParam); - - return vibrationParam; - } - private int buildVibrationTypesMask(int... types) { int typesMask = 0; for (int type : types) { diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index e7571ef47864..ed89ccf07453 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -50,6 +50,7 @@ import android.content.ContextWrapper; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.res.Resources; +import android.frameworks.vibrator.ScaleParam; import android.hardware.input.IInputManager; import android.hardware.input.InputManager; import android.hardware.input.InputManagerGlobal; @@ -59,16 +60,17 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.os.CombinedVibration; import android.os.ExternalVibration; +import android.os.ExternalVibrationScale; import android.os.Handler; import android.os.IBinder; import android.os.IExternalVibrationController; -import android.os.IExternalVibratorService; import android.os.IVibratorStateListener; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; +import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.VibrationAttributes; @@ -82,6 +84,10 @@ import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.util.SparseArray; @@ -153,6 +159,8 @@ public class VibratorManagerServiceTest { public MockitoRule rule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -185,8 +193,10 @@ public class VibratorManagerServiceTest { private Context mContextSpy; private TestLooper mTestLooper; private FakeVibrator mVibrator; + private FakeVibratorController mFakeVibratorController; private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener; private VibratorManagerService.ExternalVibratorService mExternalVibratorService; + private VibratorControlService mVibratorControlService; private VibrationConfig mVibrationConfig; private InputManagerGlobal.TestSession mInputManagerGlobalSession; private InputManager mInputManager; @@ -197,6 +207,7 @@ public class VibratorManagerServiceTest { mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mVibrationConfig = new VibrationConfig(mContextSpy.getResources()); + mFakeVibratorController = new FakeVibratorController(); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); @@ -310,6 +321,8 @@ public class VibratorManagerServiceTest { if (service instanceof VibratorManagerService.ExternalVibratorService) { mExternalVibratorService = (VibratorManagerService.ExternalVibratorService) service; + } else if (service instanceof VibratorControlService) { + mVibratorControlService = (VibratorControlService) service; } } @@ -321,9 +334,13 @@ public class VibratorManagerServiceTest { VibratorControllerHolder createVibratorControllerHolder() { VibratorControllerHolder holder = new VibratorControllerHolder(); - holder.setVibratorController(new FakeVibratorController()); + holder.setVibratorController(mFakeVibratorController); return holder; } + + boolean isServiceDeclared(String name) { + return true; + } }); return mService; } @@ -1108,12 +1125,13 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, controller, firstToken); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // The external vibration should have been cancelled verify(controller).mute(); assertEquals(Arrays.asList(false, true, false), @@ -1708,13 +1726,14 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class), binderToken); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID)) .thenReturn(true); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); } @Test @@ -1727,10 +1746,11 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class), binderToken); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart( + externalVibration); mExternalVibratorService.onExternalVibrationStop(externalVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); assertEquals(Arrays.asList(false, true, false), mVibratorProviders.get(1).getExternalControlStates()); @@ -1753,17 +1773,19 @@ public class VibratorManagerServiceTest { ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, firstController, firstToken); - int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration); + ExternalVibrationScale firstScale = + mExternalVibratorService.onExternalVibrationStart(firstVibration); AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) .build(); ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME, ringtoneAudioAttrs, secondController, secondToken); - int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration); + ExternalVibrationScale secondScale = + mExternalVibratorService.onExternalVibrationStart(secondVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, firstScale.scaleLevel); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, secondScale.scaleLevel); verify(firstController).mute(); verify(secondController, never()).mute(); // Set external control called only once. @@ -1799,8 +1821,9 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class)); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // Vibration is cancelled. assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); @@ -1825,9 +1848,10 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class)); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); // External vibration is ignored. - assertEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // Vibration is not cancelled. assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS)); @@ -1852,8 +1876,9 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class)); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // Vibration is cancelled. assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); @@ -1879,9 +1904,10 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_NOTIFICATION_ATTRS, mock(IExternalVibrationController.class)); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); // New vibration is ignored. - assertEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); // Vibration is not cancelled. assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS)); @@ -1901,18 +1927,19 @@ public class VibratorManagerServiceTest { setRingerMode(AudioManager.RINGER_MODE_SILENT); createSystemReadyService(); - int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertEquals(IExternalVibratorService.SCALE_MUTE, scale); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); setRingerMode(AudioManager.RINGER_MODE_NORMAL); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); setRingerMode(AudioManager.RINGER_MODE_VIBRATE); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); } @Test @@ -1935,14 +1962,14 @@ public class VibratorManagerServiceTest { ExternalVibration vib = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs, mock(IExternalVibrationController.class)); - int scale = mExternalVibratorService.onExternalVibrationStart(vib); - assertEquals(IExternalVibratorService.SCALE_MUTE, scale); + ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(vib); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); mExternalVibratorService.onExternalVibrationStop(vib); scale = mExternalVibratorService.onExternalVibrationStart( new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs, mock(IExternalVibrationController.class))); - assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); + assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); } @Test @@ -1956,10 +1983,91 @@ public class VibratorManagerServiceTest { .build(); createSystemReadyService(); - int scale = mExternalVibratorService.onExternalVibrationStart( - new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs, - mock(IExternalVibrationController.class))); - assertEquals(IExternalVibratorService.SCALE_MUTE, scale); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart( + new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs, + mock(IExternalVibrationController.class))); + assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel); + } + + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales() + throws RemoteException { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL, + IVibrator.CAP_AMPLITUDE_CONTROL); + createSystemReadyService(); + + SparseArray<Float> vibrationScales = new SparseArray<>(); + vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f); + vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); + + mVibratorControlService.setVibrationParams( + VibrationParamGenerator.generateVibrationParams(vibrationScales), + mFakeVibratorController); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, + mock(IExternalVibrationController.class)); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); + mExternalVibratorService.onExternalVibrationStop(externalVibration); + + assertEquals(scale.adaptiveHapticsScale, 0.7f, 0); + + externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_NOTIFICATION_ATTRS, + mock(IExternalVibrationController.class)); + scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + mExternalVibratorService.onExternalVibrationStop(externalVibration); + + assertEquals(scale.adaptiveHapticsScale, 0.4f, 0); + + AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build(); + externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + ringtoneAudioAttrs, + mock(IExternalVibrationController.class)); + scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + + assertEquals(scale.adaptiveHapticsScale, 1f, 0); + } + + @Test + @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) + public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone() + throws RemoteException { + mockVibrators(1); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL, + IVibrator.CAP_AMPLITUDE_CONTROL); + createSystemReadyService(); + + SparseArray<Float> vibrationScales = new SparseArray<>(); + vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f); + vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); + + mVibratorControlService.setVibrationParams( + VibrationParamGenerator.generateVibrationParams(vibrationScales), + mFakeVibratorController); + ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_ALARM_ATTRS, + mock(IExternalVibrationController.class)); + ExternalVibrationScale scale = + mExternalVibratorService.onExternalVibrationStart(externalVibration); + mExternalVibratorService.onExternalVibrationStop(externalVibration); + + assertEquals(scale.adaptiveHapticsScale, 1f, 0); + + mVibratorControlService.setVibrationParams( + VibrationParamGenerator.generateVibrationParams(vibrationScales), + mFakeVibratorController); + externalVibration = new ExternalVibration(UID, PACKAGE_NAME, + AUDIO_NOTIFICATION_ATTRS, + mock(IExternalVibrationController.class)); + scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); + + assertEquals(scale.adaptiveHapticsScale, 1f, 0); } @Test diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java new file mode 100644 index 000000000000..a606388da190 --- /dev/null +++ b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.frameworks.vibrator.ScaleParam; +import android.frameworks.vibrator.VibrationParam; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.List; + +/** + * A helper class that can be used to generate arrays of {@link VibrationParam}. + */ +public final class VibrationParamGenerator { + /** + * Generates an array of {@link VibrationParam}. + */ + public static VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) { + List<VibrationParam> vibrationParamList = new ArrayList<>(); + for (int i = 0; i < vibrationScales.size(); i++) { + int type = vibrationScales.keyAt(i); + float scale = vibrationScales.valueAt(i); + + vibrationParamList.add(generateVibrationParam(type, scale)); + } + + return vibrationParamList.toArray(new VibrationParam[0]); + } + + private static VibrationParam generateVibrationParam(int type, float scale) { + ScaleParam scaleParam = new ScaleParam(); + scaleParam.typesMask = type; + scaleParam.scale = scale; + VibrationParam vibrationParam = new VibrationParam(); + vibrationParam.setScale(scaleParam); + + return vibrationParam; + } +} diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 889f8429077c..c217780d90d6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -2582,7 +2582,6 @@ public class VoiceInteractionManagerService extends SystemService { if (anyPackagesAppearing()) { initRecognizer(userHandle); } - return; } if (curInteractor != null) { @@ -2631,15 +2630,16 @@ public class VoiceInteractionManagerService extends SystemService { } } - // There is no interactor, so just deal with a simple recognizer. - int change = isPackageDisappearing(curRecognizer.getPackageName()); - if (change == PACKAGE_PERMANENT_CHANGE - || change == PACKAGE_TEMPORARY_CHANGE) { - setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle); + if (curRecognizer != null) { + int change = isPackageDisappearing(curRecognizer.getPackageName()); + if (change == PACKAGE_PERMANENT_CHANGE + || change == PACKAGE_TEMPORARY_CHANGE) { + setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle); - } else if (isPackageModified(curRecognizer.getPackageName())) { - setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(), - userHandle), userHandle); + } else if (isPackageModified(curRecognizer.getPackageName())) { + setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(), + userHandle), userHandle); + } } } } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 08c76af70511..9792cdd80a00 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -2797,13 +2797,10 @@ public class TelecomManager { * calls for a given {@code packageName} and {@code userHandle}. * * @param packageName the package name of the app to check calls for. - * @param userHandle the user handle on which to check for calls. - * @param detectForAllUsers indicates if calls should be detected across all users. If it is - * set to {@code true}, and the caller has the ability to interact - * across users, the userHandle parameter is disregarded. + * @param userHandle the user handle to check calls for. * @return {@code true} if there are ongoing calls, {@code false} otherwise. - * @throws SecurityException if detectForAllUsers is true or userHandle is not the calling user - * and the caller does not grant the ability to interact across users. + * @throws SecurityException if the userHandle is not the calling user and the caller does not + * grant the ability to interact across users. * @hide */ @SystemApi @@ -2811,11 +2808,45 @@ public class TelecomManager { @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public boolean isInSelfManagedCall(@NonNull String packageName, - @NonNull UserHandle userHandle, boolean detectForAllUsers) { + @NonNull UserHandle userHandle) { ITelecomService service = getTelecomService(); if (service != null) { try { return service.isInSelfManagedCall(packageName, userHandle, + mContext.getOpPackageName(), false); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException isInSelfManagedCall: " + e); + e.rethrowFromSystemServer(); + return false; + } + } else { + throw new IllegalStateException("Telecom service is not present"); + } + } + + /** + * Determines whether there are any ongoing {@link PhoneAccount#CAPABILITY_SELF_MANAGED} + * calls for a given {@code packageName} amongst all users, given that detectForAllUsers is true + * and the caller has the ability to interact across users. If detectForAllUsers isn't enabled, + * the calls will be checked against the caller. + * + * @param packageName the package name of the app to check calls for. + * @param detectForAllUsers indicates if calls should be detected across all users. + * @return {@code true} if there are ongoing calls, {@code false} otherwise. + * @throws SecurityException if detectForAllUsers is true and the caller does not grant the + * ability to interact across users. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) + @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + public boolean isInSelfManagedCall(@NonNull String packageName, + boolean detectForAllUsers) { + ITelecomService service = getTelecomService(); + if (service != null) { + try { + return service.isInSelfManagedCall(packageName, null, mContext.getOpPackageName(), detectForAllUsers); } catch (RemoteException e) { Log.e(TAG, "RemoteException isInSelfManagedCall: " + e); diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java index 5af2c3458368..55245419c570 100644 --- a/telephony/java/android/service/euicc/EuiccService.java +++ b/telephony/java/android/service/euicc/EuiccService.java @@ -856,10 +856,22 @@ public abstract class EuiccService extends Service { int slotId, IGetAvailableMemoryInBytesCallback callback) { mExecutor.execute( () -> { - long availableMemoryInBytes = - EuiccService.this.onGetAvailableMemoryInBytes(slotId); + long availableMemoryInBytes = EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE; + String unsupportedOperationMessage = ""; try { - callback.onSuccess(availableMemoryInBytes); + availableMemoryInBytes = + EuiccService.this.onGetAvailableMemoryInBytes(slotId); + } catch (UnsupportedOperationException e) { + unsupportedOperationMessage = e.getMessage(); + } + + try { + if (!unsupportedOperationMessage.isEmpty()) { + callback.onUnsupportedOperationException( + unsupportedOperationMessage); + } else { + callback.onSuccess(availableMemoryInBytes); + } } catch (RemoteException e) { // Can't communicate with the phone process; ignore. } diff --git a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl index bd6d19b81d47..e550e77a3605 100644 --- a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl +++ b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl @@ -19,4 +19,5 @@ package android.service.euicc; /** @hide */ oneway interface IGetAvailableMemoryInBytesCallback { void onSuccess(long availableMemoryInBytes); + void onUnsupportedOperationException(String message); } |