diff options
29 files changed, 554 insertions, 169 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2f5cd5434b89..055b5cb70562 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6946,6 +6946,7 @@ public final class ViewRootImpl implements ViewParent, return; } final boolean needsStylusPointerIcon = event.isStylusPointer() + && event.isHoverEvent() && mInputManager.isStylusPointerIconEnabled(); if (needsStylusPointerIcon || event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 25b074d20b81..2307d6080f9f 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -401,8 +401,9 @@ public final class Bitmap implements Parcelable { /** * This is called by methods that want to throw an exception if the bitmap * has already been recycled. + * @hide */ - private void checkRecycled(String errorMessage) { + void checkRecycled(String errorMessage) { if (mRecycled) { throw new IllegalStateException(errorMessage); } diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java index 2f6dd468511b..5c065775eea2 100644 --- a/graphics/java/android/graphics/BitmapShader.java +++ b/graphics/java/android/graphics/BitmapShader.java @@ -120,6 +120,7 @@ public class BitmapShader extends Shader { if (bitmap == null) { throw new IllegalArgumentException("Bitmap must be non-null"); } + bitmap.checkRecycled("Cannot create BitmapShader for recycled bitmap"); mBitmap = bitmap; mTileX = tileX; mTileY = tileY; @@ -188,6 +189,8 @@ public class BitmapShader extends Shader { /** @hide */ @Override protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { + mBitmap.checkRecycled("BitmapShader's bitmap has been recycled"); + boolean enableLinearFilter = mFilterMode == FILTER_MODE_LINEAR; if (mFilterMode == FILTER_MODE_DEFAULT) { mFilterFromPaint = filterFromPaint; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index 56e73980079d..0abce82527f9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -92,6 +92,9 @@ interface DeviceEntryFaceAuthRepository { /** Current state of whether face authentication is running. */ val isAuthRunning: Flow<Boolean> + /** Whether bypass is currently enabled */ + val isBypassEnabled: Flow<Boolean> + /** * Trigger face authentication. * @@ -166,7 +169,7 @@ constructor( override val isAuthenticated: Flow<Boolean> get() = _isAuthenticated - private val bypassEnabled: Flow<Boolean> = + override val isBypassEnabled: Flow<Boolean> = keyguardBypassController?.let { conflatedCallbackFlow { val callback = @@ -222,7 +225,7 @@ constructor( // & detection is supported & biometric unlock is not allowed. listOf( canFaceAuthOrDetectRun(), - logAndObserve(bypassEnabled, "bypassEnabled"), + logAndObserve(isBypassEnabled, "isBypassEnabled"), logAndObserve( biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(), "nonStrongBiometricIsNotAllowed" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index e6715a133838..d1c6aef7b306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -24,6 +24,7 @@ import android.content.res.Resources; import android.graphics.Rect; import android.util.AttributeSet; import android.util.IndentingPrintWriter; +import android.util.Log; import android.util.MathUtils; import android.view.View; import android.view.ViewGroup; @@ -96,6 +97,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St private NotificationShelfController mController; private float mActualWidth = -1; private boolean mSensitiveRevealAnimEndabled; + private boolean mShelfRefactorFlagEnabled; + private boolean mCanModifyColorOfNotifications; public NotificationShelf(Context context, AttributeSet attrs) { super(context, attrs); @@ -425,7 +428,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St transitionAmount = inShelfAmount; } // We don't want to modify the color if the notification is hun'd - if (isLastChild && mController.canModifyColorOfNotifications()) { + if (isLastChild && canModifyColorOfNotifications()) { if (colorOfViewBeforeLast == NO_COLOR) { colorOfViewBeforeLast = ownColorUntinted; } @@ -490,6 +493,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private boolean canModifyColorOfNotifications() { + if (mShelfRefactorFlagEnabled) { + return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded(); + } else { + return mController.canModifyColorOfNotifications(); + } + } + private void updateCornerRoundnessOnScroll( ActivatableNotificationView anv, float viewStart, @@ -959,10 +970,31 @@ public class NotificationShelf extends ActivatableNotificationView implements St return false; } + private void assertRefactorFlagDisabled() { + if (mShelfRefactorFlagEnabled) { + throw new IllegalStateException( + "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled."); + } + } + + private boolean checkRefactorFlagEnabled() { + if (!mShelfRefactorFlagEnabled) { + Log.wtf(TAG, + "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled."); + } + return mShelfRefactorFlagEnabled; + } + public void setController(NotificationShelfController notificationShelfController) { + assertRefactorFlagDisabled(); mController = notificationShelfController; } + public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) { + if (!checkRefactorFlagEnabled()) return; + mCanModifyColorOfNotifications = canModifyColorOfNotifications; + } + public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) { mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf); } @@ -975,6 +1007,10 @@ public class NotificationShelf extends ActivatableNotificationView implements St mSensitiveRevealAnimEndabled = enabled; } + public void setRefactorFlagEnabled(boolean enabled) { + mShelfRefactorFlagEnabled = enabled; + } + /** * This method resets the OnScroll roundness of a view to 0f * <p> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt new file mode 100644 index 000000000000..db550c00b4a1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt @@ -0,0 +1,44 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.shelf.domain.interactor + +import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** Interactor for the [NotificationShelf] */ +@CentralSurfacesComponent.CentralSurfacesScope +class NotificationShelfInteractor +@Inject +constructor( + private val keyguardRepository: KeyguardRepository, + private val deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository, +) { + /** Is the system in a state where the shelf is just a static display of notification icons? */ + val isShelfStatic: Flow<Boolean> + get() = + combine( + keyguardRepository.isKeyguardShowing, + deviceEntryFaceAuthRepository.isBypassEnabled, + ) { isKeyguardShowing, isBypassEnabled -> + isKeyguardShowing && isBypassEnabled + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/view/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index a2351578ec98..bd531caccc5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/view/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.shelf.view +package com.android.systemui.statusbar.notification.shelf.ui.viewbinder import android.view.View import android.view.View.OnAttachStateChangeListener import android.view.accessibility.AccessibilityManager +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl import com.android.systemui.statusbar.NotificationShelf @@ -31,21 +34,16 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController import com.android.systemui.statusbar.notification.row.ExpandableOutlineViewController import com.android.systemui.statusbar.notification.row.ExpandableViewController +import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.NotificationTapHelper import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope -import dagger.Binds -import dagger.Module +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import javax.inject.Inject -/** Binds a [NotificationShelf] to its backend. */ -interface NotificationShelfViewBinder { - fun bind(shelf: NotificationShelf) -} - /** * Controller class for [NotificationShelf]. This implementation serves as a temporary wrapper * around a [NotificationShelfViewBinder], so that external code can continue to depend on the @@ -57,8 +55,7 @@ class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor( private val shelf: NotificationShelf, - private val viewBinder: NotificationShelfViewBinder, - private val keyguardBypassController: KeyguardBypassController, + private val viewModel: NotificationShelfViewModel, featureFlags: FeatureFlags, private val notifTapHelperFactory: NotificationTapHelper.Factory, private val a11yManager: AccessibilityManager, @@ -67,20 +64,19 @@ constructor( private val statusBarStateController: SysuiStatusBarStateController, ) : NotificationShelfController { - private var ambientState: AmbientState? = null - override val view: NotificationShelf get() = shelf init { shelf.apply { + setRefactorFlagEnabled(featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)) setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) } } fun init() { - viewBinder.bind(shelf) + NotificationShelfViewBinder.bind(viewModel, shelf) ActivatableNotificationViewController( shelf, @@ -91,7 +87,6 @@ constructor( falsingCollector, ) .init() - shelf.setController(this) val onAttachStateListener = object : OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { @@ -117,10 +112,7 @@ constructor( override val shelfIcons: NotificationIconContainer get() = shelf.shelfIcons - override fun canModifyColorOfNotifications(): Boolean { - return (ambientState?.isShadeExpanded == true && - !(ambientState?.isOnKeyguard == true && keyguardBypassController.bypassEnabled)) - } + override fun canModifyColorOfNotifications(): Boolean = unsupported override fun setOnActivatedListener(listener: ActivatableNotificationView.OnActivatedListener) { shelf.setOnActivatedListener(listener) @@ -128,25 +120,28 @@ constructor( override fun bind( ambientState: AmbientState, - notificationStackScrollLayoutController: NotificationStackScrollLayoutController + notificationStackScrollLayoutController: NotificationStackScrollLayoutController, ) { shelf.bind(ambientState, notificationStackScrollLayoutController) - this.ambientState = ambientState } override fun setOnClickListener(listener: View.OnClickListener) { shelf.setOnClickListener(listener) } -} -@Module(includes = [PrivateShelfViewBinderModule::class]) object NotificationShelfViewBinderModule - -@Module -private interface PrivateShelfViewBinderModule { - @Binds fun bindImpl(impl: NotificationShelfViewBinderImpl): NotificationShelfViewBinder + private val unsupported: Nothing + get() = error("Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled") } -@CentralSurfacesScope -private class NotificationShelfViewBinderImpl @Inject constructor() : NotificationShelfViewBinder { - override fun bind(shelf: NotificationShelf) {} +/** Binds a [NotificationShelf] to its backend. */ +object NotificationShelfViewBinder { + fun bind(viewModel: NotificationShelfViewModel, shelf: NotificationShelf) { + shelf.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.canModifyColorOfNotifications + .onEach(shelf::setCanModifyColorOfNotifications) + .launchIn(this) + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt new file mode 100644 index 000000000000..b84834adf122 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt @@ -0,0 +1,36 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.shelf.ui.viewmodel + +import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor +import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** ViewModel for [NotificationShelf]. */ +@CentralSurfacesScope +class NotificationShelfViewModel +@Inject +constructor( + private val interactor: NotificationShelfInteractor, +) { + /** Is the shelf allowed to modify the color of notifications in the host layout? */ + val canModifyColorOfNotifications: Flow<Boolean> + get() = interactor.isShelfStatic.map { static -> !static } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index cc2a0ba6f798..5d4addab240a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -52,8 +52,7 @@ import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; -import com.android.systemui.statusbar.notification.shelf.view.NotificationShelfViewBinderModule; -import com.android.systemui.statusbar.notification.shelf.view.NotificationShelfViewBinderWrapperControllerImpl; +import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator; @@ -87,8 +86,7 @@ import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoSet; -@Module(subcomponents = StatusBarFragmentComponent.class, - includes = { NotificationShelfViewBinderModule.class }) +@Module(subcomponents = StatusBarFragmentComponent.class) public abstract class StatusBarViewModule { public static final String SHADE_HEADER = "large_screen_shade_header"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 6e002f5a9a9a..2489e043c7db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -59,11 +59,10 @@ import com.android.systemui.statusbar.phone.FakeKeyguardStateController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.KotlinArgumentCaptor +import com.android.systemui.util.mockito.captureMany import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.SystemClock import com.google.common.truth.Truth.assertThat -import java.io.PrintWriter -import java.io.StringWriter import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher @@ -81,6 +80,7 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.isNull import org.mockito.Mockito.mock @@ -88,6 +88,8 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations +import java.io.PrintWriter +import java.io.StringWriter @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -120,6 +122,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { private lateinit var authStatus: FlowValue<AuthenticationStatus?> private lateinit var detectStatus: FlowValue<DetectionStatus?> private lateinit var authRunning: FlowValue<Boolean?> + private lateinit var bypassEnabled: FlowValue<Boolean?> private lateinit var lockedOut: FlowValue<Boolean?> private lateinit var canFaceAuthRun: FlowValue<Boolean?> private lateinit var authenticated: FlowValue<Boolean?> @@ -726,6 +729,23 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test + fun isBypassEnabledReflectsBypassControllerState() = + testScope.runTest { + initCollectors() + runCurrent() + val listeners = captureMany { + verify(bypassController, atLeastOnce()) + .registerOnBypassStateChangedListener(capture()) + } + + listeners.forEach { it.onBypassStateChanged(true) } + assertThat(bypassEnabled()).isTrue() + + listeners.forEach { it.onBypassStateChanged(false) } + assertThat(bypassEnabled()).isFalse() + } + + @Test fun detectDoesNotRunWhenNonStrongBiometricIsAllowed() = testScope.runTest { testGatingCheckForDetect { @@ -844,6 +864,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { lockedOut = collectLastValue(underTest.isLockedOut) canFaceAuthRun = collectLastValue(underTest.canRunFaceAuth) authenticated = collectLastValue(underTest.isAuthenticated) + bypassEnabled = collectLastValue(underTest.isBypassEnabled) fakeUserRepository.setSelectedUserInfo(primaryUser) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt new file mode 100644 index 000000000000..14e5f9e63ebe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -0,0 +1,71 @@ +/* + * 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. + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.shelf.domain.interactor + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class NotificationShelfInteractorTest : SysuiTestCase() { + + private val keyguardRepository = FakeKeyguardRepository() + private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() + private val underTest = + NotificationShelfInteractor(keyguardRepository, deviceEntryFaceAuthRepository) + + @Test + fun shelfIsNotStatic_whenKeyguardNotShowing() = runTest { + val shelfStatic by collectLastValue(underTest.isShelfStatic) + + keyguardRepository.setKeyguardShowing(false) + + assertThat(shelfStatic).isFalse() + } + + @Test + fun shelfIsNotStatic_whenKeyguardShowingAndNotBypass() = runTest { + val shelfStatic by collectLastValue(underTest.isShelfStatic) + + keyguardRepository.setKeyguardShowing(true) + deviceEntryFaceAuthRepository.isBypassEnabled.value = false + + assertThat(shelfStatic).isFalse() + } + + @Test + fun shelfIsStatic_whenBypass() = runTest { + val shelfStatic by collectLastValue(underTest.isShelfStatic) + + keyguardRepository.setKeyguardShowing(true) + deviceEntryFaceAuthRepository.isBypassEnabled.value = true + + assertThat(shelfStatic).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt new file mode 100644 index 000000000000..6c5fb8bcff22 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -0,0 +1,72 @@ +/* + * 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. + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.shelf.ui.viewmodel + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class NotificationShelfViewModelTest : SysuiTestCase() { + + private val keyguardRepository = FakeKeyguardRepository() + private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() + private val interactor = + NotificationShelfInteractor(keyguardRepository, deviceEntryFaceAuthRepository) + private val underTest = NotificationShelfViewModel(interactor) + + @Test + fun canModifyColorOfNotifications_whenKeyguardNotShowing() = runTest { + val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications) + + keyguardRepository.setKeyguardShowing(false) + + assertThat(canModifyNotifColor).isTrue() + } + + @Test + fun canModifyColorOfNotifications_whenKeyguardShowingAndNotBypass() = runTest { + val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications) + + keyguardRepository.setKeyguardShowing(true) + deviceEntryFaceAuthRepository.isBypassEnabled.value = false + + assertThat(canModifyNotifColor).isTrue() + } + + @Test + fun cannotModifyColorOfNotifications_whenBypass() = runTest { + val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications) + + keyguardRepository.setKeyguardShowing(true) + deviceEntryFaceAuthRepository.isBypassEnabled.value = true + + assertThat(canModifyNotifColor).isFalse() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt new file mode 100644 index 000000000000..c08ecd0e3b0c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt @@ -0,0 +1,59 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.keyguard.FaceAuthUiEvent +import com.android.systemui.keyguard.shared.model.AuthenticationStatus +import com.android.systemui.keyguard.shared.model.DetectionStatus +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map + +class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository { + + override val isAuthenticated = MutableStateFlow(false) + override val canRunFaceAuth = MutableStateFlow(false) + private val _authenticationStatus = MutableStateFlow<AuthenticationStatus?>(null) + override val authenticationStatus: Flow<AuthenticationStatus> = + _authenticationStatus.filterNotNull() + fun setAuthenticationStatus(status: AuthenticationStatus) { + _authenticationStatus.value = status + } + private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null) + override val detectionStatus: Flow<DetectionStatus> + get() = _detectionStatus.filterNotNull() + fun setDetectionStatus(status: DetectionStatus) { + _detectionStatus.value = status + } + override val isLockedOut = MutableStateFlow(false) + private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null) + val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> = + _runningAuthRequest.asStateFlow() + override val isAuthRunning = _runningAuthRequest.map { it != null } + override val isBypassEnabled = MutableStateFlow(false) + + override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) { + _runningAuthRequest.value = uiEvent to fallbackToDetection + } + + override fun cancel() { + _runningAuthRequest.value = null + } +} diff --git a/services/Android.bp b/services/Android.bp index 6e6c55325e3d..b0a0e5e44a8c 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -112,6 +112,7 @@ filegroup { ":services.searchui-sources", ":services.selectiontoolbar-sources", ":services.smartspace-sources", + ":services.soundtrigger-sources", ":services.systemcaptions-sources", ":services.translation-sources", ":services.texttospeech-sources", @@ -169,6 +170,7 @@ java_library { "services.searchui", "services.selectiontoolbar", "services.smartspace", + "services.soundtrigger", "services.systemcaptions", "services.translation", "services.texttospeech", diff --git a/services/core/Android.bp b/services/core/Android.bp index c8caab93d76c..cfdf3ac5915b 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -174,7 +174,6 @@ java_library_static { "android.hardware.configstore-V1.1-java", "android.hardware.ir-V1-java", "android.hardware.rebootescrow-V1-java", - "android.hardware.soundtrigger-V2.3-java", "android.hardware.power.stats-V2-java", "android.hardware.power-V4-java", "android.hidl.manager-V1.2-java", diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/core/java/com/android/server/SoundTriggerInternal.java index cc398d930c7e..e6c1750c4a1d 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java +++ b/services/core/java/com/android/server/SoundTriggerInternal.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.soundtrigger; +package com.android.server; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,15 +29,13 @@ import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.media.permission.Identity; import android.os.IBinder; -import com.android.server.voiceinteraction.VoiceInteractionManagerService; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; /** * Provides a local service for managing voice-related recoginition models. This is primarily used - * by the {@link VoiceInteractionManagerService}. + * by the {@code VoiceInteractionManagerService}. */ public interface SoundTriggerInternal { /** diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 8eaf70e81684..ab71acd5f21d 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -614,7 +614,7 @@ final class ProcessStateRecord { void forceProcessStateUpTo(int newState) { if (mRepProcState > newState) { synchronized (mProcLock) { - mRepProcState = newState; + setReportedProcState(newState); setCurProcState(newState); setCurRawProcState(newState); } diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index aeb4801628f2..208a4be9a70e 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -161,7 +161,8 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, @Override public void onProviderStatusChanged(ProviderSession.Status status, ComponentName componentName, ProviderSession.CredentialsSource source) { - Slog.d(TAG, "in onStatusChanged with status: " + status + ", and source: " + source); + Slog.d(TAG, "in onStatusChanged for: " + componentName + ", with status: " + + status + ", and source: " + source); // Auth entry was selected, and it did not have any underlying credentials if (status == ProviderSession.Status.NO_CREDENTIALS_FROM_AUTH_ENTRY) { diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java index 0c3d3f4ed6d2..9ec0ecd93b3c 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java @@ -26,7 +26,6 @@ import android.credentials.ui.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.service.credentials.CallingAppInfo; import android.service.credentials.ClearCredentialStateRequest; -import android.util.Log; import android.util.Slog; /** @@ -81,7 +80,7 @@ public final class ProviderClearSession extends ProviderSession<ClearCredentialS @Override public void onProviderResponseSuccess(@Nullable Void response) { - Log.i(TAG, "in onProviderResponseSuccess"); + Slog.d(TAG, "Remote provider responded with a valid response: " + mComponentName); mProviderResponseSet = true; updateStatusAndInvokeCallback(Status.COMPLETE, /*source=*/ CredentialsSource.REMOTE_PROVIDER); @@ -105,7 +104,7 @@ public final class ProviderClearSession extends ProviderSession<ClearCredentialS updateStatusAndInvokeCallback(Status.SERVICE_DEAD, /*source=*/ CredentialsSource.REMOTE_PROVIDER); } else { - Slog.i(TAG, "Component names different in onProviderServiceDied - " + Slog.w(TAG, "Component names different in onProviderServiceDied - " + "this should not happen"); } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 8b9255a9c56b..09433dbb0c52 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -37,7 +37,6 @@ import android.service.credentials.CreateCredentialRequest; import android.service.credentials.CreateEntry; import android.service.credentials.CredentialProviderService; import android.service.credentials.RemoteEntry; -import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -93,7 +92,8 @@ public final class ProviderCreateSession extends ProviderSession< createRequestSession.mHybridService ); } - Log.i(TAG, "Unable to create provider session"); + Slog.d(TAG, "Unable to create provider session for: " + + providerInfo.getComponentName()); return null; } @@ -122,7 +122,6 @@ public final class ProviderCreateSession extends ProviderSession< return new CreateCredentialRequest(callingAppInfo, capability, clientRequest.getCredentialData()); } - Log.i(TAG, "Unable to create provider request - capabilities do not match"); return null; } @@ -146,7 +145,7 @@ public final class ProviderCreateSession extends ProviderSession< @Override public void onProviderResponseSuccess( @Nullable BeginCreateCredentialResponse response) { - Log.i(TAG, "in onProviderResponseSuccess"); + Slog.d(TAG, "Remote provider responded with a valid response: " + mComponentName); onSetInitialRemoteResponse(response); } @@ -169,7 +168,7 @@ public final class ProviderCreateSession extends ProviderSession< updateStatusAndInvokeCallback(Status.SERVICE_DEAD, /*source=*/ CredentialsSource.REMOTE_PROVIDER); } else { - Slog.i(TAG, "Component names different in onProviderServiceDied - " + Slog.w(TAG, "Component names different in onProviderServiceDied - " + "this should not happen"); } } @@ -180,7 +179,6 @@ public final class ProviderCreateSession extends ProviderSession< } private void onSetInitialRemoteResponse(BeginCreateCredentialResponse response) { - Log.i(TAG, "onSetInitialRemoteResponse with save entries"); mProviderResponse = response; mProviderResponseDataHandler.addResponseContent(response.getCreateEntries(), response.getRemoteCreateEntry()); @@ -199,14 +197,12 @@ public final class ProviderCreateSession extends ProviderSession< @Nullable protected CreateCredentialProviderData prepareUiData() throws IllegalArgumentException { - Log.i(TAG, "In prepareUiData"); if (!ProviderSession.isUiInvokingStatus(getStatus())) { - Log.i(TAG, "In prepareUiData not in uiInvokingStatus"); + Slog.d(TAG, "No data for UI from: " + mComponentName.flattenToString()); return null; } if (mProviderResponse != null && !mProviderResponseDataHandler.isEmptyResponse()) { - Log.i(TAG, "In prepareUiData save entries not null"); return mProviderResponseDataHandler.toCreateCredentialProviderData(); } return null; @@ -218,7 +214,7 @@ public final class ProviderCreateSession extends ProviderSession< switch (entryType) { case SAVE_ENTRY_KEY: if (mProviderResponseDataHandler.getCreateEntry(entryKey) == null) { - Log.i(TAG, "Unexpected save entry key"); + Slog.w(TAG, "Unexpected save entry key"); invokeCallbackOnInternalInvalidState(); return; } @@ -226,14 +222,14 @@ public final class ProviderCreateSession extends ProviderSession< break; case REMOTE_ENTRY_KEY: if (mProviderResponseDataHandler.getRemoteEntry(entryKey) == null) { - Log.i(TAG, "Unexpected remote entry key"); + Slog.w(TAG, "Unexpected remote entry key"); invokeCallbackOnInternalInvalidState(); return; } onRemoteEntrySelected(providerPendingIntentResponse); break; default: - Log.i(TAG, "Unsupported entry type selected"); + Slog.w(TAG, "Unsupported entry type selected"); invokeCallbackOnInternalInvalidState(); } } @@ -268,7 +264,7 @@ public final class ProviderCreateSession extends ProviderSession< if (credentialResponse != null) { mCallbacks.onFinalResponseReceived(mComponentName, credentialResponse); } else { - Log.i(TAG, "onSaveEntrySelected - no response or error found in pending " + Slog.w(TAG, "onSaveEntrySelected - no response or error found in pending " + "intent response"); invokeCallbackOnInternalInvalidState(); } @@ -284,14 +280,14 @@ public final class ProviderCreateSession extends ProviderSession< private CreateCredentialException maybeGetPendingIntentException( ProviderPendingIntentResponse pendingIntentResponse) { if (pendingIntentResponse == null) { - Log.i(TAG, "pendingIntentResponse is null"); + Slog.w(TAG, "pendingIntentResponse is null"); return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREATE_OPTIONS); } if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) { CreateCredentialException exception = PendingIntentResultHandler .extractCreateCredentialException(pendingIntentResponse.getResultData()); if (exception != null) { - Log.i(TAG, "Pending intent contains provider exception"); + Slog.d(TAG, "Pending intent contains provider exception"); return exception; } } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) { @@ -343,7 +339,7 @@ public final class ProviderCreateSession extends ProviderSession< public void setRemoteEntry(@Nullable RemoteEntry remoteEntry) { if (!enforceRemoteEntryRestrictions(mExpectedRemoteEntryProviderService)) { - Log.i(TAG, "Remote entry being dropped as it does not meet the restriction" + Slog.w(TAG, "Remote entry being dropped as it does not meet the restriction" + "checks."); return; } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 8d3d06469944..0c2b5633d501 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -115,7 +115,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential getRequestSession.mHybridService ); } - Log.i(TAG, "Unable to create provider session"); + Slog.d(TAG, "Unable to create provider session for: " + + providerInfo.getComponentName()); return null; } @@ -146,17 +147,15 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential android.credentials.GetCredentialRequest clientRequest, CredentialProviderInfo info ) { + Slog.d(TAG, "Filtering request options for: " + info.getComponentName()); List<CredentialOption> filteredOptions = new ArrayList<>(); for (CredentialOption option : clientRequest.getCredentialOptions()) { if (providerCapabilities.contains(option.getType()) && isProviderAllowed(option, info.getComponentName()) && checkSystemProviderRequirement(option, info.isSystemProvider())) { - Log.i(TAG, "In createProviderRequest - capability found : " - + option.getType()); + Slog.d(TAG, "Option of type: " + option.getType() + " meets all filtering" + + "conditions"); filteredOptions.add(option); - } else { - Log.i(TAG, "In createProviderRequest - capability not " - + "found, or provider not allowed : " + option.getType()); } } if (!filteredOptions.isEmpty()) { @@ -165,15 +164,14 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential .setCredentialOptions( filteredOptions).build(); } - Log.i(TAG, "In createProviderRequest - returning null"); + Slog.d(TAG, "No options filtered"); return null; } private static boolean isProviderAllowed(CredentialOption option, ComponentName componentName) { if (!option.getAllowedProviders().isEmpty() && !option.getAllowedProviders().contains( componentName)) { - Log.d(TAG, "Provider allow list specified but does not contain this provider: " - + componentName.flattenToString()); + Slog.d(TAG, "Provider allow list specified but does not contain this provider"); return false; } return true; @@ -182,7 +180,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential private static boolean checkSystemProviderRequirement(CredentialOption option, boolean isSystemProvider) { if (option.isSystemProviderRequired() && !isSystemProvider) { - Log.d(TAG, "System provider required, but this service is not a system provider"); + Slog.d(TAG, "System provider required, but this service is not a system provider"); return false; } return true; @@ -210,6 +208,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Called when the provider response has been updated by an external source. */ @Override // Callback from the remote provider public void onProviderResponseSuccess(@Nullable BeginGetCredentialResponse response) { + Slog.d(TAG, "Remote provider responded with a valid response: " + mComponentName); onSetInitialRemoteResponse(response); } @@ -231,7 +230,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential updateStatusAndInvokeCallback(Status.SERVICE_DEAD, /*source=*/ CredentialsSource.REMOTE_PROVIDER); } else { - Slog.i(TAG, "Component names different in onProviderServiceDied - " + Slog.w(TAG, "Component names different in onProviderServiceDied - " + "this should not happen"); } } @@ -244,13 +243,14 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential @Override // Selection call from the request provider protected void onUiEntrySelected(String entryType, String entryKey, ProviderPendingIntentResponse providerPendingIntentResponse) { - Log.i(TAG, "onUiEntrySelected with entryKey: " + entryKey); + Slog.d(TAG, "onUiEntrySelected with entryType: " + entryType + ", and entryKey: " + + entryKey); switch (entryType) { case CREDENTIAL_ENTRY_KEY: CredentialEntry credentialEntry = mProviderResponseDataHandler .getCredentialEntry(entryKey); if (credentialEntry == null) { - Log.i(TAG, "Unexpected credential entry key"); + Slog.w(TAG, "Unexpected credential entry key"); invokeCallbackOnInternalInvalidState(); return; } @@ -259,7 +259,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential case ACTION_ENTRY_KEY: Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey); if (actionEntry == null) { - Log.i(TAG, "Unexpected action entry key"); + Slog.w(TAG, "Unexpected action entry key"); invokeCallbackOnInternalInvalidState(); return; } @@ -269,21 +269,21 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential Action authenticationEntry = mProviderResponseDataHandler .getAuthenticationAction(entryKey); if (authenticationEntry == null) { - Log.i(TAG, "Unexpected authenticationEntry key"); + Slog.w(TAG, "Unexpected authenticationEntry key"); invokeCallbackOnInternalInvalidState(); return; } boolean additionalContentReceived = onAuthenticationEntrySelected(providerPendingIntentResponse); if (additionalContentReceived) { - Log.i(TAG, "Additional content received - removing authentication entry"); + Slog.d(TAG, "Additional content received - removing authentication entry"); mProviderResponseDataHandler.removeAuthenticationAction(entryKey); if (!mProviderResponseDataHandler.isEmptyResponse()) { updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED, /*source=*/ CredentialsSource.AUTH_ENTRY); } } else { - Log.i(TAG, "Additional content not received"); + Slog.d(TAG, "Additional content not received from authentication entry"); mProviderResponseDataHandler .updateAuthEntryWithNoCredentialsReceived(entryKey); updateStatusAndInvokeCallback(Status.NO_CREDENTIALS_FROM_AUTH_ENTRY, @@ -294,12 +294,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) { onRemoteEntrySelected(providerPendingIntentResponse); } else { - Log.i(TAG, "Unexpected remote entry key"); + Slog.d(TAG, "Unexpected remote entry key"); invokeCallbackOnInternalInvalidState(); } break; default: - Log.i(TAG, "Unsupported entry type selected"); + Slog.w(TAG, "Unsupported entry type selected"); invokeCallbackOnInternalInvalidState(); } } @@ -320,26 +320,24 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential @Override // Call from request session to data to be shown on the UI @Nullable protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException { - Log.i(TAG, "In prepareUiData"); if (!ProviderSession.isUiInvokingStatus(getStatus())) { - Log.i(TAG, "In prepareUiData - provider does not want to show UI: " - + mComponentName.flattenToString()); + Slog.d(TAG, "No data for UI from: " + mComponentName.flattenToString()); return null; } if (mProviderResponse != null && !mProviderResponseDataHandler.isEmptyResponse()) { return mProviderResponseDataHandler.toGetCredentialProviderData(); } - Log.i(TAG, "In prepareUiData response null"); + Slog.d(TAG, "In prepareUiData response null"); return null; } - private Intent setUpFillInIntent(@NonNull String id) { + private Intent setUpFillInIntentWithFinalRequest(@NonNull String id) { // TODO: Determine if we should skip this entry if entry id is not set, or is set // but does not resolve to a valid option. For now, not skipping it because // it may be possible that the provider adds their own extras and expects to receive // those and complete the flow. if (mBeginGetOptionToCredentialOptionMap.get(id) == null) { - Log.i(TAG, "Id from Credential Entry does not resolve to a valid option"); + Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option"); return new Intent(); } return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST, @@ -382,7 +380,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential getCredentialResponse); return; } - Log.i(TAG, "Pending intent response contains no credential, or error"); + Slog.d(TAG, "Pending intent response contains no credential, or error " + + "for a credential entry"); invokeCallbackOnInternalInvalidState(); } @@ -390,14 +389,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential private GetCredentialException maybeGetPendingIntentException( ProviderPendingIntentResponse pendingIntentResponse) { if (pendingIntentResponse == null) { - Log.i(TAG, "pendingIntentResponse is null"); return null; } if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) { GetCredentialException exception = PendingIntentResultHandler .extractGetCredentialException(pendingIntentResponse.getResultData()); if (exception != null) { - Log.i(TAG, "Pending intent contains provider exception"); return exception; } } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) { @@ -463,7 +460,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Returns true if either an exception or a response is found. */ private void onActionEntrySelected(ProviderPendingIntentResponse providerPendingIntentResponse) { - Log.i(TAG, "onActionEntrySelected"); + Slog.d(TAG, "onActionEntrySelected"); onCredentialEntrySelected(providerPendingIntentResponse); } @@ -559,7 +556,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential String id = generateUniqueId(); Entry entry = new Entry(CREDENTIAL_ENTRY_KEY, id, credentialEntry.getSlice(), - setUpFillInIntent(credentialEntry.getBeginGetCredentialOptionId())); + setUpFillInIntentWithFinalRequest(credentialEntry + .getBeginGetCredentialOptionId())); mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry)); mCredentialEntryTypes.add(credentialEntry.getType()); } @@ -574,9 +572,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential public void addAuthenticationAction(Action authenticationAction, @AuthenticationEntry.Status int status) { - Log.i(TAG, "In addAuthenticationAction"); String id = generateUniqueId(); - Log.i(TAG, "In addAuthenticationAction, id : " + id); AuthenticationEntry entry = new AuthenticationEntry( AUTHENTICATION_ACTION_ENTRY_KEY, id, authenticationAction.getSlice(), @@ -591,7 +587,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential public void setRemoteEntry(@Nullable RemoteEntry remoteEntry) { if (!enforceRemoteEntryRestrictions(mExpectedRemoteEntryProviderService)) { - Log.i(TAG, "Remote entry being dropped as it does not meet the restriction" + Slog.w(TAG, "Remote entry being dropped as it does not meet the restriction" + " checks."); return; } @@ -715,7 +711,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT) .findFirst(); if (previousMostRecentAuthEntry.isEmpty()) { - Log.i(TAG, "In updatePreviousMostRecentAuthEntry - previous entry not found"); return; } String id = previousMostRecentAuthEntry.get().getKey(); diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java index 24292ef2cdab..c10f5640c466 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java @@ -33,7 +33,7 @@ import android.os.ICancellationSignal; import android.service.credentials.CallingAppInfo; import android.service.credentials.CredentialEntry; import android.service.credentials.CredentialProviderService; -import android.telecom.Log; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -145,14 +145,12 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption private List<Entry> prepareUiCredentialEntries( @NonNull List<CredentialEntry> credentialEntries) { - Log.i(TAG, "in prepareUiProviderDataWithCredentials"); List<Entry> credentialUiEntries = new ArrayList<>(); // Populate the credential entries for (CredentialEntry credentialEntry : credentialEntries) { String entryId = generateUniqueId(); mUiCredentialEntries.put(entryId, credentialEntry); - Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId); credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId, credentialEntry.getSlice(), setUpFillInIntent())); @@ -172,15 +170,13 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption @Override protected ProviderData prepareUiData() { - Log.i(TAG, "In prepareUiData"); if (!ProviderSession.isUiInvokingStatus(getStatus())) { - Log.i(TAG, "In prepareUiData - provider does not want to show UI: " - + mComponentName.flattenToString()); + Slog.d(TAG, "No date for UI coming from: " + mComponentName.flattenToString()); return null; } if (mProviderResponse == null) { - Log.i(TAG, "In prepareUiData response null"); - throw new IllegalStateException("Response must be in completion mode"); + Slog.w(TAG, "In prepareUiData but response is null. This is strange."); + return null; } return new GetCredentialProviderData.Builder( mComponentName.flattenToString()).setActionChips(null) @@ -200,13 +196,13 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption case CREDENTIAL_ENTRY_KEY: CredentialEntry credentialEntry = mUiCredentialEntries.get(entryKey); if (credentialEntry == null) { - Log.i(TAG, "Unexpected credential entry key"); + Slog.w(TAG, "Unexpected credential entry key"); return; } onCredentialEntrySelected(credentialEntry, providerPendingIntentResponse); break; default: - Log.i(TAG, "Unsupported entry type selected"); + Slog.w(TAG, "Unsupported entry type selected"); } } @@ -233,10 +229,8 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption } return; } - - Log.i(TAG, "Pending intent response contains no credential, or error"); } - Log.i(TAG, "CredentialEntry does not have a credential or a pending intent result"); + Slog.w(TAG, "CredentialEntry does not have a credential or a pending intent result"); } @Override @@ -279,14 +273,13 @@ public class ProviderRegistryGetSession extends ProviderSession<CredentialOption protected GetCredentialException maybeGetPendingIntentException( ProviderPendingIntentResponse pendingIntentResponse) { if (pendingIntentResponse == null) { - android.util.Log.i(TAG, "pendingIntentResponse is null"); return null; } if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) { GetCredentialException exception = PendingIntentResultHandler .extractGetCredentialException(pendingIntentResponse.getResultData()); if (exception != null) { - android.util.Log.i(TAG, "Pending intent contains provider exception"); + Slog.d(TAG, "Pending intent contains provider exception"); return exception; } } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) { diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index d165756b3811..d02a8c1ee510 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -29,7 +29,6 @@ import android.credentials.ui.ProviderData; import android.credentials.ui.ProviderPendingIntentResponse; import android.os.ICancellationSignal; import android.os.RemoteException; -import android.util.Log; import android.util.Slog; import com.android.server.credentials.metrics.ProviderSessionMetric; @@ -253,7 +252,7 @@ public abstract class ProviderSession<T, R> @Nullable ComponentName expectedRemoteEntryProviderService) { // Check if the service is the one set by the OEM. If not silently reject this entry if (!mComponentName.equals(expectedRemoteEntryProviderService)) { - Log.i(TAG, "Remote entry being dropped as it is not from the service " + Slog.w(TAG, "Remote entry being dropped as it is not from the service " + "configured by the OEM."); return false; } @@ -270,15 +269,12 @@ public abstract class ProviderSession<T, R> return true; } } catch (SecurityException e) { - Log.i(TAG, "Error getting info for " - + mComponentName.flattenToString() + ": " + e.getMessage()); + Slog.e(TAG, "Error getting info for " + mComponentName.flattenToString(), e); return false; } catch (PackageManager.NameNotFoundException e) { - Log.i(TAG, "Error getting info for " - + mComponentName.flattenToString() + ": " + e.getMessage()); + Slog.i(TAG, "Error getting info for " + mComponentName.flattenToString(), e); return false; } - Log.i(TAG, "In enforceRemoteEntryRestrictions - remote entry checks fail"); return false; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 492d477fe23a..b1d613109e09 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -125,6 +125,7 @@ import com.android.server.compat.PlatformCompatNative; import com.android.server.connectivity.PacProxyService; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.coverage.CoverageService; +import com.android.server.cpu.CpuMonitorService; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.devicestate.DeviceStateManagerService; import com.android.server.display.DisplayManagerService; @@ -1405,6 +1406,15 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(RemoteProvisioningService.class); t.traceEnd(); + // TODO(b/277600174): Start CpuMonitorService on all builds and not just on debuggable + // builds once the Android JobScheduler starts using this service. + if (Build.IS_DEBUGGABLE || Build.IS_ENG) { + // Service for CPU monitor. + t.traceBegin("CpuMonitorService"); + mSystemServiceManager.startService(CpuMonitorService.class); + t.traceEnd(); + } + t.traceEnd(); // startCoreServices } diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp index 986fb71afa2d..e704ebf32270 100644 --- a/services/tests/voiceinteractiontests/Android.bp +++ b/services/tests/voiceinteractiontests/Android.bp @@ -40,6 +40,7 @@ android_test { "platform-test-annotations", "services.core", "services.voiceinteraction", + "services.soundtrigger", "servicestests-core-utils", "servicestests-utils-mockito-extended", "truth-prebuilt", diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp index 7332d2d8b0f6..de8d1440e6ac 100644 --- a/services/voiceinteraction/Android.bp +++ b/services/voiceinteraction/Android.bp @@ -9,11 +9,60 @@ package { filegroup { name: "services.voiceinteraction-sources", - srcs: ["java/**/*.java"], + srcs: ["java/com/android/server/voiceinteraction/*.java"], path: "java", visibility: ["//frameworks/base/services"], } +filegroup { + name: "services.soundtrigger_middleware-sources", + srcs: ["java/com/android/server/soundtrigger_middleware/*.java"], + path: "java", + visibility: ["//visibility:private"], +} + +filegroup { + name: "services.soundtrigger_service-sources", + srcs: ["java/com/android/server/soundtrigger/*.java"], + path: "java", + visibility: ["//visibility:private"], +} + +filegroup { + name: "services.soundtrigger-sources", + srcs: [ + ":services.soundtrigger_service-sources", + ":services.soundtrigger_middleware-sources", + ], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.soundtrigger_middleware", + defaults: ["platform_service_defaults"], + srcs: [":services.soundtrigger_middleware-sources"], + libs: [ + "services.core", + ], + static_libs: [ + "android.hardware.soundtrigger-V2.3-java", + ], + visibility: ["//visibility/base/services/tests/voiceinteraction"], +} + +java_library_static { + name: "services.soundtrigger", + defaults: ["platform_service_defaults"], + srcs: [":services.soundtrigger_service-sources"], + libs: [ + "services.core", + ], + static_libs: [ + "services.soundtrigger_middleware", + ], +} + java_library_static { name: "services.voiceinteraction", defaults: ["platform_service_defaults"], diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index a54e3560e5a3..203a3e74d9da 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -88,6 +88,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.ISoundTriggerService; import com.android.internal.app.ISoundTriggerSession; +import com.android.server.SoundTriggerInternal; import com.android.server.SystemService; import com.android.server.utils.EventLogger; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index dd0fa0ba6adf..1d7b966bab51 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -99,11 +99,11 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.LocalServices; +import com.android.server.SoundTriggerInternal; import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.LegacyPermissionManagerInternal; -import com.android.server.soundtrigger.SoundTriggerInternal; import com.android.server.utils.Slogf; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.ActivityTaskManagerInternal; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e39af5aa3327..9dd2a61671ec 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -2682,71 +2682,76 @@ public class TelecomManager { } /** - * Reports a new call with the specified {@link CallAttributes} to the telecom service. This - * method can be used to report both incoming and outgoing calls. By reporting the call, the - * system is aware of the call and can provide updates on services (ex. Another device wants to - * disconnect the call) or events (ex. a new Bluetooth route became available). - * + * Add a call to the Android system service Telecom. This allows the system to start tracking an + * incoming or outgoing call with the specified {@link CallAttributes}. Once the call is ready + * to be disconnected, use the {@link CallControl#disconnect(DisconnectCause, Executor, + * OutcomeReceiver)} which is provided by the {@code pendingControl#onResult(CallControl)}. * <p> - * The difference between this API call and {@link TelecomManager#placeCall(Uri, Bundle)} or - * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)} is that this API - * will asynchronously provide an update on whether the new call was added successfully via - * an {@link OutcomeReceiver}. Additionally, callbacks will run on the executor thread that was - * passed in. - * * <p> - * Note: Only packages that register with + * <p> + * <b>Call Lifecycle</b>: Your app is given foreground execution priority as long as you have a + * valid call and are posting a {@link android.app.Notification.CallStyle} notification. + * When your application is given foreground execution priority, your app is treated as a + * foreground service. Foreground execution priority will prevent the + * {@link android.app.ActivityManager} from killing your application when it is placed the + * background. Foreground execution priority is removed from your app when all of your app's + * calls terminate or your app no longer posts a valid notification. + * <p> + * <p> + * <p> + * <b>Note</b>: Only packages that register with * {@link PhoneAccount#CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS} * can utilize this API. {@link PhoneAccount}s that set the capabilities * {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}, * {@link PhoneAccount#CAPABILITY_CALL_PROVIDER}, * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} * are not supported and will cause an exception to be thrown. - * * <p> - * Usage example: + * <p> + * <p> + * <b>Usage example:</b> * <pre> - * - * // An app should first define their own construct of a Call that overrides all the - * // {@link CallControlCallback}s and {@link CallEventCallback}s - * private class MyVoipCall { - * public String callId = ""; - * - * public CallControlCallEventCallback handshakes = new - * CallControlCallEventCallback() { - * // override/ implement all {@link CallControlCallback}s + * // Its up to your app on how you want to wrap the objects. One such implementation can be: + * class MyVoipCall { + * ... + * public CallControlCallEventCallback handshakes = new CallControlCallback() { + * ... * } - * public CallEventCallback events = new - * CallEventCallback() { - * // override/ implement all {@link CallEventCallback}s - * } - * public MyVoipCall(String id){ - * callId = id; - * } * - * PhoneAccountHandle handle = new PhoneAccountHandle( - * new ComponentName("com.example.voip.app", - * "com.example.voip.app.NewCallActivity"), "123"); + * public CallEventCallback events = new CallEventCallback() { + * ... + * } * - * CallAttributes callAttributes = new CallAttributes.Builder(handle, - * CallAttributes.DIRECTION_OUTGOING, - * "John Smith", Uri.fromParts("tel", "123", null)) - * .build(); + * public MyVoipCall(String id){ + * ... + * } + * } * * MyVoipCall myFirstOutgoingCall = new MyVoipCall("1"); * - * telecomManager.addCall(callAttributes, Runnable::run, new OutcomeReceiver() { + * telecomManager.addCall(callAttributes, + * Runnable::run, + * new OutcomeReceiver() { * public void onResult(CallControl callControl) { - * // The call has been added successfully + * // The call has been added successfully. For demonstration + * // purposes, the call is disconnected immediately ... + * callControl.disconnect( + * new DisconnectCause(DisconnectCause.LOCAL) ) * } - * }, myFirstOutgoingCall.handshakes, myFirstOutgoingCall.events); + * }, + * myFirstOutgoingCall.handshakes, + * myFirstOutgoingCall.events); * </pre> * - * @param callAttributes attributes of the new call (incoming or outgoing, address, etc. ) - * @param executor thread to run background CallEventCallback updates on - * @param pendingControl OutcomeReceiver that receives the result of addCall transaction - * @param handshakes object that overrides {@link CallControlCallback}s - * @param events object that overrides {@link CallEventCallback}s + * @param callAttributes attributes of the new call (incoming or outgoing, address, etc.) + * @param executor execution context to run {@link CallControlCallback} updates on + * @param pendingControl Receives the result of addCall transaction. Upon success, a + * CallControl object is provided which can be used to do things like + * disconnect the call that was added. + * @param handshakes callback that receives <b>actionable</b> updates that originate from + * Telecom. + * @param events callback that receives <b>non</b>-actionable updates that originate + * from Telecom. */ @RequiresPermission(android.Manifest.permission.MANAGE_OWN_CALLS) @SuppressLint("SamShouldBeLast") |