summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp7
-rw-r--r--core/api/current.txt1
-rw-r--r--core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java30
-rw-r--r--core/java/android/os/PowerManager.java11
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java16
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--core/res/res/values/public-final.xml5
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java3
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt118
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt36
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt269
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java17
-rw-r--r--services/core/java/com/android/server/power/feature/PowerManagerFlags.java12
-rw-r--r--services/core/java/com/android/server/power/feature/power_flags.aconfig11
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java41
21 files changed, 388 insertions, 255 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 834398e5c2c2..ac756ea1d624 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1573,6 +1573,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "power_flags_lib_host",
+ aconfig_declarations: "power_flags",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Content
aconfig_declarations {
name: "android.content.flags-aconfig",
diff --git a/core/api/current.txt b/core/api/current.txt
index 21929658cbb9..dd606774b770 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1207,7 +1207,6 @@ package android {
field public static final int minResizeHeight = 16843670; // 0x1010396
field public static final int minResizeWidth = 16843669; // 0x1010395
field public static final int minSdkVersion = 16843276; // 0x101020c
- field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int minSdkVersionFull = 16844461; // 0x10106ad
field public static final int minWidth = 16843071; // 0x101013f
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index d2d3a6840acc..c7403c0ea98c 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -339,36 +339,6 @@ public class FrameworkParsingPackageUtils {
}
/**
- * Check if a package is compatible with this platform with regards to its
- * its minSdkVersionFull.
- *
- * @param minSdkVersionFullString A string representation of a major.minor version,
- * e.g. "12.34"
- * @param platformMinSdkVersionFull The major and minor version of the platform, i.e. the value
- * of Build.VERSION.SDK_INT_FULL
- * @param input A ParseInput object to report success or failure
- */
- public static ParseResult<Void> verifyMinSdkVersionFull(@NonNull String minSdkVersionFullString,
- int platformMinSdkVersionFull, @NonNull ParseInput input) {
- int minSdkVersionFull;
- try {
- minSdkVersionFull = Build.parseFullVersion(minSdkVersionFullString);
- } catch (IllegalStateException e) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- e.getMessage());
- }
- if (minSdkVersionFull <= platformMinSdkVersionFull) {
- return input.success(null);
- }
- return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
- "Requires newer sdk version "
- + Build.fullVersionToString(minSdkVersionFull)
- + " (current version is "
- + Build.fullVersionToString(platformMinSdkVersionFull)
- + ")");
- }
-
- /**
* Computes the targetSdkVersion to use at runtime. If the package is not compatible with this
* platform, populates {@code outError[0]} with an error message.
* <p>
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e769abec7dd9..5129af6be442 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -4213,6 +4213,17 @@ public final class PowerManager {
else mFlags &= ~UNIMPORTANT_FOR_LOGGING;
}
+ /** @hide */
+ public void updateUids(int[] uids) {
+ synchronized (mToken) {
+ try {
+ mService.updateWakeLockUids(mToken, uids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
@Override
public String toString() {
synchronized (mToken) {
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 5c08dc6be1a0..db60e12e50b1 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -29,7 +29,6 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_
import static android.os.Build.VERSION_CODES.DONUT;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.sdk.Flags.majorMinorVersioningScheme;
import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
@@ -1690,21 +1689,6 @@ public class ParsingPackageUtils {
targetCode = minCode;
}
- if (majorMinorVersioningScheme()) {
- val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersionFull);
- if (val != null) {
- if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- String minSdkVersionFullString = val.string.toString();
- ParseResult<Void> minSdkVersionFullResult =
- FrameworkParsingPackageUtils.verifyMinSdkVersionFull(
- minSdkVersionFullString, Build.VERSION.SDK_INT_FULL, input);
- if (minSdkVersionFullResult.isError()) {
- return input.error(minSdkVersionFullResult);
- }
- }
- }
- }
-
if (isApkInApex) {
val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_maxSdkVersion);
if (val != null) {
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8c6fd1dfc47e..3edc5c108083 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2572,10 +2572,6 @@
against a development branch, in which case it will only work against
the development builds. -->
<attr name="minSdkVersion" format="integer|string" />
- <!-- This is the minimum SDK major and minor version (e.g. "36.1") that
- the application requires. Verified independently of minSdkVersion.
- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
- <attr name="minSdkVersionFull" format="string" />
<!-- This is the SDK version number that the application is targeting.
It is able to run on older versions (down to minSdkVersion), but
was explicitly tested to work with the version specified here.
diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml
index d8e89318a134..af1e5123096d 100644
--- a/core/res/res/values/public-final.xml
+++ b/core/res/res/values/public-final.xml
@@ -3953,8 +3953,7 @@
<public name="pageSizeCompat" />
<!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
<public name="wantsRoleHolderPriority"/>
- <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
- <public name="minSdkVersionFull"/>
+ <public name="removed_"/>
<public name="removed_" />
<public name="removed_" />
<public name="removed_" />
@@ -3980,8 +3979,6 @@
<public type="attr" name="pageSizeCompat" id="0x010106ab" />
<!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) -->
<public type="attr" name="wantsRoleHolderPriority" id="0x010106ac" />
- <!-- @FlaggedApi(android.sdk.Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) -->
- <public type="attr" name="minSdkVersionFull" id="0x010106ad" />
<staging-public-group-final type="string" first-id="0x01b40000">
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index da62be7f142f..deec52d1c19e 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -208,8 +208,7 @@ public class DesktopModeStatus {
/**
* Return {@code true} if the current device supports desktop mode.
*/
- @VisibleForTesting
- public static boolean isDesktopModeSupported(@NonNull Context context) {
+ private static boolean isDesktopModeSupported(@NonNull Context context) {
return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index c8d3430bf54b..f03bd3d9a2a7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -73,6 +73,9 @@ import com.android.wm.shell.shared.ShellTransitions
import com.android.wm.shell.shared.TransitionUtil
import java.util.concurrent.Executor
import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeoutOrNull
private const val TAG = "ActivityTransitionAnimator"
@@ -241,7 +244,7 @@ constructor(
override fun onTransitionAnimationProgress(linearProgress: Float) {
LinkedHashSet(listeners).forEach {
- it.onTransitionAnimationProgress(linearProgress)
+ it.onTransitionAnimationProgress(linearProgress)
}
}
@@ -494,15 +497,19 @@ constructor(
/**
* Create a new animation [Runner] controlled by the [Controller] that [controllerFactory] can
- * create based on [forLaunch].
+ * create based on [forLaunch] and within the given [scope].
*
* This method must only be used for long-lived registrations. Otherwise, use
* [createEphemeralRunner].
*/
@VisibleForTesting
- fun createLongLivedRunner(controllerFactory: ControllerFactory, forLaunch: Boolean): Runner {
+ fun createLongLivedRunner(
+ controllerFactory: ControllerFactory,
+ scope: CoroutineScope,
+ forLaunch: Boolean,
+ ): Runner {
assertLongLivedReturnAnimations()
- return Runner(callback!!, transitionAnimator, lifecycleListener) {
+ return Runner(scope, callback!!, transitionAnimator, lifecycleListener) {
controllerFactory.createController(forLaunch)
}
}
@@ -564,7 +571,7 @@ constructor(
* Creates a [Controller] for launching or returning from the activity linked to [cookie]
* and [component].
*/
- abstract fun createController(forLaunch: Boolean): Controller
+ abstract suspend fun createController(forLaunch: Boolean): Controller
}
/**
@@ -691,9 +698,14 @@ constructor(
* animations.
*
* The [Controller]s created by [controllerFactory] will only be used for transitions matching
- * the [cookie], or the [ComponentName] defined within it if the cookie matching fails.
+ * the [cookie], or the [ComponentName] defined within it if the cookie matching fails. These
+ * [Controller]s can only be created within [scope].
*/
- fun register(cookie: TransitionCookie, controllerFactory: ControllerFactory) {
+ fun register(
+ cookie: TransitionCookie,
+ controllerFactory: ControllerFactory,
+ scope: CoroutineScope,
+ ) {
assertLongLivedReturnAnimations()
if (transitionRegister == null) {
@@ -725,7 +737,7 @@ constructor(
}
val launchRemoteTransition =
RemoteTransition(
- OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = true)),
+ OriginTransition(createLongLivedRunner(controllerFactory, scope, forLaunch = true)),
"${cookie}_launchTransition",
)
transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true)
@@ -749,7 +761,9 @@ constructor(
}
val returnRemoteTransition =
RemoteTransition(
- OriginTransition(createLongLivedRunner(controllerFactory, forLaunch = false)),
+ OriginTransition(
+ createLongLivedRunner(controllerFactory, scope, forLaunch = false)
+ ),
"${cookie}_returnTransition",
)
transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true)
@@ -952,7 +966,9 @@ constructor(
* Reusable factory to generate single-use controllers. In case of an ephemeral [Runner],
* this must be null and [controller] must be defined instead.
*/
- private val controllerFactory: (() -> Controller)?,
+ private val controllerFactory: (suspend () -> Controller)?,
+ /** The scope to use when this runner is based on [controllerFactory]. */
+ private val scope: CoroutineScope? = null,
private val callback: Callback,
/** The animator to use to animate the window transition. */
private val transitionAnimator: TransitionAnimator,
@@ -973,13 +989,15 @@ constructor(
)
constructor(
+ scope: CoroutineScope,
callback: Callback,
transitionAnimator: TransitionAnimator,
listener: Listener? = null,
- controllerFactory: () -> Controller,
+ controllerFactory: suspend () -> Controller,
) : this(
controller = null,
controllerFactory = controllerFactory,
+ scope = scope,
callback = callback,
transitionAnimator = transitionAnimator,
listener = listener,
@@ -994,12 +1012,12 @@ constructor(
assert((controller != null).xor(controllerFactory != null))
delegate = null
- if (controller != null) {
+ controller?.let {
// Ephemeral launches bundle the runner with the launch request (instead of being
// registered ahead of time for later use). This means that there could be a timeout
// between creation and invocation, so the delegate needs to exist from the
// beginning in order to handle such timeout.
- createDelegate()
+ createDelegate(it)
}
}
@@ -1040,49 +1058,79 @@ constructor(
finishedCallback: IRemoteAnimationFinishedCallback?,
performAnimation: (AnimationDelegate) -> Unit,
) {
- maybeSetUp()
- val delegate = delegate
- mainExecutor.execute {
- if (delegate == null) {
- Log.i(TAG, "onAnimationStart called after completion")
- // Animation started too late and timed out already. We need to still
- // signal back that we're done with it.
- finishedCallback?.onAnimationFinished()
- } else {
- performAnimation(delegate)
+ val controller = controller
+ val controllerFactory = controllerFactory
+
+ if (controller != null) {
+ maybeSetUp(controller)
+ val success = startAnimation(performAnimation)
+ if (!success) finishedCallback?.onAnimationFinished()
+ } else if (controllerFactory != null) {
+ scope?.launch {
+ val success =
+ withTimeoutOrNull(TRANSITION_TIMEOUT) {
+ setUp(controllerFactory)
+ startAnimation(performAnimation)
+ } ?: false
+ if (!success) finishedCallback?.onAnimationFinished()
}
+ } else {
+ // This should never happen, as either the controller or factory should always be
+ // defined. This final call is for safety in case something goes wrong.
+ Log.wtf(TAG, "initAndRun with neither a controller nor factory")
+ finishedCallback?.onAnimationFinished()
+ }
+ }
+
+ /** Tries to start the animation on the main thread and returns whether it succeeded. */
+ @BinderThread
+ private fun startAnimation(performAnimation: (AnimationDelegate) -> Unit): Boolean {
+ val delegate = delegate
+ return if (delegate != null) {
+ mainExecutor.execute { performAnimation(delegate) }
+ true
+ } else {
+ // Animation started too late and timed out already.
+ Log.i(TAG, "startAnimation called after completion")
+ false
}
}
@BinderThread
override fun onAnimationCancelled() {
val delegate = delegate
- mainExecutor.execute {
- delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion")
- delegate?.onAnimationCancelled()
+ if (delegate != null) {
+ mainExecutor.execute { delegate.onAnimationCancelled() }
+ } else {
+ Log.wtf(TAG, "onAnimationCancelled called after completion")
}
}
+ /**
+ * Posts the default animation timeouts. Since this only applies to ephemeral launches, this
+ * method is a no-op if [controller] is not defined.
+ */
@VisibleForTesting
@UiThread
fun postTimeouts() {
- maybeSetUp()
+ controller?.let { maybeSetUp(it) }
delegate?.postTimeouts()
}
@AnyThread
- private fun maybeSetUp() {
- if (controllerFactory == null || delegate != null) return
- createDelegate()
+ private fun maybeSetUp(controller: Controller) {
+ if (delegate != null) return
+ createDelegate(controller)
}
@AnyThread
- private fun createDelegate() {
- var controller = controller
- val factory = controllerFactory
- if (controller == null && factory == null) return
+ private suspend fun setUp(createController: suspend () -> Controller) {
+ val controller = createController()
+ createDelegate(controller)
+ }
- controller = controller ?: factory!!.invoke()
+ @AnyThread
+ private fun createDelegate(controller: Controller) {
delegate =
AnimationDelegate(
mainExecutor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index b0b80a9419e2..52c41a07198d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -25,11 +25,14 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.Flags as SharedFlags
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,6 +51,7 @@ class ActivityStarterImplTest : SysuiTestCase() {
@Mock private lateinit var activityStarterInternal: ActivityStarterInternalImpl
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
private lateinit var underTest: ActivityStarterImpl
+ private val kosmos = testKosmos()
private val mainExecutor = FakeExecutor(FakeSystemClock())
@Before
@@ -69,12 +73,18 @@ class ActivityStarterImplTest : SysuiTestCase() {
@EnableSceneContainer
@Test
fun registerTransition_forwardsTheRequest() {
- val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
- val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
-
- underTest.registerTransition(cookie, controllerFactory)
-
- verify(activityStarterInternal).registerTransition(eq(cookie), eq(controllerFactory))
+ with(kosmos) {
+ testScope.runTest {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory =
+ mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+
+ underTest.registerTransition(cookie, controllerFactory, testScope)
+
+ verify(activityStarterInternal)
+ .registerTransition(eq(cookie), eq(controllerFactory), eq(testScope))
+ }
+ }
}
@DisableFlags(
@@ -83,12 +93,17 @@ class ActivityStarterImplTest : SysuiTestCase() {
)
@Test
fun registerTransition_doesNotForwardTheRequest_whenFlaggedOff() {
- val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
- val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+ with(kosmos) {
+ testScope.runTest {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory =
+ mock(ActivityTransitionAnimator.ControllerFactory::class.java)
- underTest.registerTransition(cookie, controllerFactory)
+ underTest.registerTransition(cookie, controllerFactory, testScope)
- verify(activityStarterInternal, never()).registerTransition(any(), any())
+ verify(activityStarterInternal, never()).registerTransition(any(), any(), any())
+ }
+ }
}
@EnableFlags(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 5406acf694ff..dfa5c9a26d79 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -42,6 +42,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSettingsInteracto
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
@@ -58,12 +59,14 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
@@ -109,6 +112,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
@Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor
@Mock private lateinit var communalSettingsInteractor: CommunalSettingsInteractor
private lateinit var underTest: LegacyActivityStarterInternalImpl
+ private val kosmos = testKosmos()
private val mainExecutor = FakeExecutor(FakeSystemClock())
private val shadeAnimationInteractor =
ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository())
@@ -157,13 +161,18 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
)
@Test
fun registerTransition_registers() {
- val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
- val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
- `when`(controllerFactory.cookie).thenReturn(cookie)
+ with(kosmos) {
+ testScope.runTest {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory =
+ mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+ `when`(controllerFactory.cookie).thenReturn(cookie)
- underTest.registerTransition(cookie, controllerFactory)
+ underTest.registerTransition(cookie, controllerFactory, testScope)
- verify(activityTransitionAnimator).register(eq(cookie), any())
+ verify(activityTransitionAnimator).register(eq(cookie), any(), eq(testScope))
+ }
+ }
}
@DisableFlags(
@@ -172,14 +181,19 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
)
@Test
fun registerTransition_throws_whenFlagsAreDisabled() {
- val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
- val controllerFactory = mock(ActivityTransitionAnimator.ControllerFactory::class.java)
+ with(kosmos) {
+ testScope.runTest {
+ val cookie = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val controllerFactory =
+ mock(ActivityTransitionAnimator.ControllerFactory::class.java)
- assertThrows(IllegalStateException::class.java) {
- underTest.registerTransition(cookie, controllerFactory)
- }
+ assertThrows(IllegalStateException::class.java) {
+ underTest.registerTransition(cookie, controllerFactory, testScope)
+ }
- verify(activityTransitionAnimator, never()).register(any(), any())
+ verify(activityTransitionAnimator, never()).register(any(), any(), any())
+ }
+ }
}
@EnableFlags(
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index ca98cbf20c3a..18891dba4b0d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -25,6 +25,8 @@ import android.view.View;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import kotlinx.coroutines.CoroutineScope;
+
/**
* An interface to start activities. This is used as a callback from the views to
* {@link PhoneStatusBar} to allow custom handling for starting the activity, i.e. dismissing the
@@ -37,11 +39,12 @@ public interface ActivityStarter {
/**
* Registers the given {@link ActivityTransitionAnimator.ControllerFactory} for launching and
* closing transitions matching the {@link ActivityTransitionAnimator.TransitionCookie} and the
- * {@link ComponentName} that it contains.
+ * {@link ComponentName} that it contains, within the given {@link CoroutineScope}.
*/
void registerTransition(
ActivityTransitionAnimator.TransitionCookie cookie,
- ActivityTransitionAnimator.ControllerFactory controllerFactory);
+ ActivityTransitionAnimator.ControllerFactory controllerFactory,
+ CoroutineScope scope);
/**
* Unregisters the {@link ActivityTransitionAnimator.ControllerFactory} previously registered
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 5a63c0cd84e6..bd1d7f755a74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.concurrency.DelayableExecutor
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
/** Handles start activity logic in SystemUI. */
@SysUISingleton
@@ -52,9 +53,10 @@ constructor(
override fun registerTransition(
cookie: ActivityTransitionAnimator.TransitionCookie,
controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ scope: CoroutineScope,
) {
if (!TransitionAnimator.longLivedReturnAnimationsEnabled()) return
- activityStarterInternal.registerTransition(cookie, controllerFactory)
+ activityStarterInternal.registerTransition(cookie, controllerFactory, scope)
}
override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
index 5e427fbf1f7e..015ec3052134 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -25,15 +25,17 @@ import android.view.View
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.plugins.ActivityStarter
+import kotlinx.coroutines.CoroutineScope
interface ActivityStarterInternal {
/**
* Registers the given [controllerFactory] for launching and closing transitions matching the
- * [cookie] and the [ComponentName] that it contains.
+ * [cookie] and the [ComponentName] that it contains, within the given [scope].
*/
fun registerTransition(
cookie: ActivityTransitionAnimator.TransitionCookie,
controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ scope: CoroutineScope,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index 7289c2ed5897..6e82d7f7401a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -66,6 +66,7 @@ import com.android.systemui.util.kotlin.getOrNull
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
/**
* Encapsulates the activity logic for activity starter when the SceneContainerFlag is enabled.
@@ -105,6 +106,7 @@ constructor(
override fun registerTransition(
cookie: ActivityTransitionAnimator.TransitionCookie,
controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ scope: CoroutineScope,
) {
check(TransitionAnimator.longLivedReturnAnimationsEnabled())
@@ -116,7 +118,7 @@ constructor(
controllerFactory.launchCujType,
controllerFactory.returnCujType,
) {
- override fun createController(
+ override suspend fun createController(
forLaunch: Boolean
): ActivityTransitionAnimator.Controller {
val baseController = controllerFactory.createController(forLaunch)
@@ -132,7 +134,7 @@ constructor(
}
}
- activityTransitionAnimator.register(cookie, factory)
+ activityTransitionAnimator.register(cookie, factory, scope)
}
override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index d7a29c36f2ce..76f67dc6c146 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -64,6 +64,7 @@ import com.android.systemui.util.kotlin.getOrNull
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
/** Encapsulates the activity logic for activity starter. */
@SysUISingleton
@@ -102,6 +103,7 @@ constructor(
override fun registerTransition(
cookie: ActivityTransitionAnimator.TransitionCookie,
controllerFactory: ActivityTransitionAnimator.ControllerFactory,
+ scope: CoroutineScope,
) {
check(TransitionAnimator.longLivedReturnAnimationsEnabled())
@@ -113,7 +115,7 @@ constructor(
controllerFactory.launchCujType,
controllerFactory.returnCujType,
) {
- override fun createController(
+ override suspend fun createController(
forLaunch: Boolean
): ActivityTransitionAnimator.Controller {
val baseController = controllerFactory.createController(forLaunch)
@@ -129,7 +131,7 @@ constructor(
}
}
- activityTransitionAnimator.register(cookie, factory)
+ activityTransitionAnimator.register(cookie, factory, scope)
}
override fun unregisterTransition(cookie: ActivityTransitionAnimator.TransitionCookie) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index fd751d9cc7c3..845be0252581 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -27,7 +27,10 @@ import android.window.WindowAnimationState
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.Flags
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.wm.shell.shared.ShellTransitions
import com.google.common.truth.Truth.assertThat
@@ -38,6 +41,9 @@ import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
import kotlin.concurrent.thread
import kotlin.test.assertEquals
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
@@ -54,10 +60,12 @@ import org.mockito.Mockito.`when`
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class ActivityTransitionAnimatorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private val transitionContainer = LinearLayout(mContext)
private val mainExecutor = context.mainExecutor
private val testTransitionAnimator = fakeTransitionAnimator(mainExecutor)
@@ -67,12 +75,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Spy private val controller = TestTransitionAnimatorController(transitionContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
- private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
+ private lateinit var underTest: ActivityTransitionAnimator
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setup() {
- activityTransitionAnimator =
+ underTest =
ActivityTransitionAnimator(
mainExecutor,
ActivityTransitionAnimator.TransitionRegister.fromShellTransitions(
@@ -82,17 +90,17 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
testTransitionAnimator,
disableWmTimeout = true,
)
- activityTransitionAnimator.callback = callback
- activityTransitionAnimator.addListener(listener)
+ underTest.callback = callback
+ underTest.addListener(listener)
}
@After
fun tearDown() {
- activityTransitionAnimator.removeListener(listener)
+ underTest.removeListener(listener)
}
private fun startIntentWithAnimation(
- animator: ActivityTransitionAnimator = this.activityTransitionAnimator,
+ animator: ActivityTransitionAnimator = underTest,
controller: ActivityTransitionAnimator.Controller? = this.controller,
animate: Boolean = true,
intentStarter: (RemoteAnimationAdapter?) -> Int,
@@ -157,7 +165,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
var animationAdapter: RemoteAnimationAdapter? = null
- startIntentWithAnimation(activityTransitionAnimator) { adapter ->
+ startIntentWithAnimation(underTest) { adapter ->
animationAdapter = adapter
ActivityManager.START_DELIVERED_TO_TOP
}
@@ -185,9 +193,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
fun registersReturnIffCookieIsPresent() {
`when`(callback.isOnKeyguard()).thenReturn(false)
- startIntentWithAnimation(activityTransitionAnimator, controller) { _ ->
- ActivityManager.START_DELIVERED_TO_TOP
- }
+ startIntentWithAnimation(underTest, controller) { ActivityManager.START_DELIVERED_TO_TOP }
waitForIdleSync()
assertTrue(testShellTransitions.remotes.isEmpty())
@@ -199,9 +205,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
get() = ActivityTransitionAnimator.TransitionCookie("testCookie")
}
- startIntentWithAnimation(activityTransitionAnimator, controller) { _ ->
- ActivityManager.START_DELIVERED_TO_TOP
- }
+ startIntentWithAnimation(underTest, controller) { ActivityManager.START_DELIVERED_TO_TOP }
waitForIdleSync()
assertEquals(1, testShellTransitions.remotes.size)
@@ -214,13 +218,15 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun registersLongLivedTransition() {
- var factory = controllerFactory()
- activityTransitionAnimator.register(factory.cookie, factory)
- assertEquals(2, testShellTransitions.remotes.size)
-
- factory = controllerFactory()
- activityTransitionAnimator.register(factory.cookie, factory)
- assertEquals(4, testShellTransitions.remotes.size)
+ kosmos.runTest {
+ var factory = controllerFactory()
+ underTest.register(factory.cookie, factory, testScope)
+ assertEquals(2, testShellTransitions.remotes.size)
+
+ factory = controllerFactory()
+ underTest.register(factory.cookie, factory, testScope)
+ assertEquals(4, testShellTransitions.remotes.size)
+ }
}
@EnableFlags(
@@ -229,49 +235,55 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun registersLongLivedTransitionOverridingPreviousRegistration() {
- val cookie = ActivityTransitionAnimator.TransitionCookie("test_cookie")
- var factory = controllerFactory(cookie)
- activityTransitionAnimator.register(cookie, factory)
- val transitions = testShellTransitions.remotes.values.toList()
-
- factory = controllerFactory(cookie)
- activityTransitionAnimator.register(cookie, factory)
- assertEquals(2, testShellTransitions.remotes.size)
- for (transition in transitions) {
- assertThat(testShellTransitions.remotes.values).doesNotContain(transition)
+ kosmos.runTest {
+ val cookie = ActivityTransitionAnimator.TransitionCookie("test_cookie")
+ var factory = controllerFactory(cookie)
+ underTest.register(cookie, factory, testScope)
+ val transitions = testShellTransitions.remotes.values.toList()
+
+ factory = controllerFactory(cookie)
+ underTest.register(cookie, factory, testScope)
+ assertEquals(2, testShellTransitions.remotes.size)
+ for (transition in transitions) {
+ assertThat(testShellTransitions.remotes.values).doesNotContain(transition)
+ }
}
}
@DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
@Test
fun doesNotRegisterLongLivedTransitionIfFlagIsDisabled() {
- val factory = controllerFactory(component = null)
- assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(factory.cookie, factory)
+ kosmos.runTest {
+ val factory = controllerFactory(component = null)
+ assertThrows(IllegalStateException::class.java) {
+ underTest.register(factory.cookie, factory, testScope)
+ }
}
}
@EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
@Test
fun doesNotRegisterLongLivedTransitionIfMissingRequiredProperties() {
- // No ComponentName
- var factory = controllerFactory(component = null)
- assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(factory.cookie, factory)
- }
+ kosmos.runTest {
+ // No ComponentName
+ var factory = controllerFactory(component = null)
+ assertThrows(IllegalStateException::class.java) {
+ underTest.register(factory.cookie, factory, testScope)
+ }
- // No TransitionRegister
- activityTransitionAnimator =
- ActivityTransitionAnimator(
- mainExecutor,
- transitionRegister = null,
- testTransitionAnimator,
- testTransitionAnimator,
- disableWmTimeout = true,
- )
- factory = controllerFactory()
- assertThrows(IllegalStateException::class.java) {
- activityTransitionAnimator.register(factory.cookie, factory)
+ // No TransitionRegister
+ val activityTransitionAnimator =
+ ActivityTransitionAnimator(
+ mainExecutor,
+ transitionRegister = null,
+ testTransitionAnimator,
+ testTransitionAnimator,
+ disableWmTimeout = true,
+ )
+ factory = controllerFactory()
+ assertThrows(IllegalStateException::class.java) {
+ activityTransitionAnimator.register(factory.cookie, factory, testScope)
+ }
}
}
@@ -281,27 +293,29 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun unregistersLongLivedTransition() {
- val cookies = arrayOfNulls<ActivityTransitionAnimator.TransitionCookie>(3)
+ kosmos.runTest {
+ val cookies = arrayOfNulls<ActivityTransitionAnimator.TransitionCookie>(3)
- for (index in 0 until 3) {
- cookies[index] = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
- val factory = controllerFactory(cookies[index]!!)
- activityTransitionAnimator.register(factory.cookie, factory)
- }
+ for (index in 0 until 3) {
+ cookies[index] = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
+ val factory = controllerFactory(cookies[index]!!)
+ underTest.register(factory.cookie, factory, testScope)
+ }
- activityTransitionAnimator.unregister(cookies[0]!!)
- assertEquals(4, testShellTransitions.remotes.size)
+ underTest.unregister(cookies[0]!!)
+ assertEquals(4, testShellTransitions.remotes.size)
- activityTransitionAnimator.unregister(cookies[2]!!)
- assertEquals(2, testShellTransitions.remotes.size)
+ underTest.unregister(cookies[2]!!)
+ assertEquals(2, testShellTransitions.remotes.size)
- activityTransitionAnimator.unregister(cookies[1]!!)
- assertThat(testShellTransitions.remotes).isEmpty()
+ underTest.unregister(cookies[1]!!)
+ assertThat(testShellTransitions.remotes).isEmpty()
+ }
}
@Test
fun doesNotStartIfAnimationIsCancelled() {
- val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ val runner = underTest.createEphemeralRunner(controller)
runner.onAnimationCancelled()
runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
@@ -315,7 +329,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun cancelsIfNoOpeningWindowIsFound() {
- val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ val runner = underTest.createEphemeralRunner(controller)
runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
@@ -328,7 +342,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun startsAnimationIfWindowIsOpening() {
- val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ val runner = underTest.createEphemeralRunner(controller)
runner.onAnimationStart(
TRANSIT_NONE,
arrayOf(fakeWindow()),
@@ -354,9 +368,11 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() {
- assertThrows(IllegalStateException::class.java) {
- val factory = controllerFactory()
- activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true)
+ kosmos.runTest {
+ assertThrows(IllegalStateException::class.java) {
+ val factory = controllerFactory()
+ underTest.createLongLivedRunner(factory, testScope, forLaunch = true)
+ }
}
}
@@ -365,44 +381,34 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
)
@Test
- fun runnerCreatesDelegateLazily_whenPostingTimeouts() {
- val factory = controllerFactory()
- val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true)
- assertNull(runner.delegate)
- runner.postTimeouts()
- assertNotNull(runner.delegate)
- }
-
- @EnableFlags(
- Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
- Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
- )
- @Test
fun runnerCreatesDelegateLazily_onAnimationStart() {
- val factory = controllerFactory()
- val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = true)
- assertNull(runner.delegate)
-
- var delegateInitialized = false
- activityTransitionAnimator.addListener(
- object : ActivityTransitionAnimator.Listener {
- override fun onTransitionAnimationStart() {
- // This is called iff the delegate was initialized, so it's a good proxy for
- // checking the initialization.
- delegateInitialized = true
+ kosmos.runTest {
+ val factory = controllerFactory()
+ val runner = underTest.createLongLivedRunner(factory, testScope, forLaunch = true)
+ assertNull(runner.delegate)
+
+ var delegateInitialized = false
+ underTest.addListener(
+ object : ActivityTransitionAnimator.Listener {
+ override fun onTransitionAnimationStart() {
+ // This is called iff the delegate was initialized, so it's a good proxy for
+ // checking the initialization.
+ delegateInitialized = true
+ }
}
- }
- )
- runner.onAnimationStart(
- TRANSIT_NONE,
- arrayOf(fakeWindow()),
- emptyArray(),
- emptyArray(),
- iCallback,
- )
+ )
+ runner.onAnimationStart(
+ TRANSIT_NONE,
+ arrayOf(fakeWindow()),
+ emptyArray(),
+ emptyArray(),
+ iCallback,
+ )
+ testScope.advanceUntilIdle()
+ waitForIdleSync()
- waitForIdleSync()
- assertTrue(delegateInitialized)
+ assertTrue(delegateInitialized)
+ }
}
@EnableFlags(
@@ -411,29 +417,32 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun runnerCreatesDelegateLazily_onAnimationTakeover() {
- val factory = controllerFactory()
- val runner = activityTransitionAnimator.createLongLivedRunner(factory, forLaunch = false)
- assertNull(runner.delegate)
-
- var delegateInitialized = false
- activityTransitionAnimator.addListener(
- object : ActivityTransitionAnimator.Listener {
- override fun onTransitionAnimationStart() {
- // This is called iff the delegate was initialized, so it's a good proxy for
- // checking the initialization.
- delegateInitialized = true
+ kosmos.runTest {
+ val factory = controllerFactory()
+ val runner = underTest.createLongLivedRunner(factory, testScope, forLaunch = false)
+ assertNull(runner.delegate)
+
+ var delegateInitialized = false
+ underTest.addListener(
+ object : ActivityTransitionAnimator.Listener {
+ override fun onTransitionAnimationStart() {
+ // This is called iff the delegate was initialized, so it's a good proxy for
+ // checking the initialization.
+ delegateInitialized = true
+ }
}
- }
- )
- runner.takeOverAnimation(
- arrayOf(fakeWindow(MODE_CLOSING)),
- arrayOf(WindowAnimationState()),
- SurfaceControl.Transaction(),
- iCallback,
- )
+ )
+ runner.takeOverAnimation(
+ arrayOf(fakeWindow(MODE_CLOSING)),
+ arrayOf(WindowAnimationState()),
+ SurfaceControl.Transaction(),
+ iCallback,
+ )
+ testScope.advanceUntilIdle()
+ waitForIdleSync()
- waitForIdleSync()
- assertTrue(delegateInitialized)
+ assertTrue(delegateInitialized)
+ }
}
@DisableFlags(
@@ -442,7 +451,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun animationTakeoverThrows_whenTheFlagsAreDisabled() {
- val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ val runner = underTest.createEphemeralRunner(controller)
assertThrows(IllegalStateException::class.java) {
runner.takeOverAnimation(
arrayOf(fakeWindow()),
@@ -459,7 +468,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
)
@Test
fun disposeRunner_delegateDereferenced() {
- val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ val runner = underTest.createEphemeralRunner(controller)
assertNotNull(runner.delegate)
runner.dispose()
waitForIdleSync()
@@ -469,13 +478,13 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
@Test
fun concurrentListenerModification_doesNotThrow() {
// Need a second listener to trigger the concurrent modification.
- activityTransitionAnimator.addListener(object : ActivityTransitionAnimator.Listener {})
+ underTest.addListener(object : ActivityTransitionAnimator.Listener {})
`when`(listener.onTransitionAnimationStart()).thenAnswer {
- activityTransitionAnimator.removeListener(listener)
+ underTest.removeListener(listener)
listener
}
- val runner = activityTransitionAnimator.createEphemeralRunner(controller)
+ val runner = underTest.createEphemeralRunner(controller)
runner.onAnimationStart(
TRANSIT_NONE,
arrayOf(fakeWindow()),
@@ -494,7 +503,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
component: ComponentName? = mock(ComponentName::class.java),
): ActivityTransitionAnimator.ControllerFactory {
return object : ActivityTransitionAnimator.ControllerFactory(cookie, component) {
- override fun createController(forLaunch: Boolean) =
+ override suspend fun createController(forLaunch: Boolean) =
object : DelegateTransitionAnimatorController(controller) {
override val isLaunching: Boolean
get() = forLaunch
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 090707db50a5..8fae875eb29b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -5979,10 +5979,19 @@ public final class PowerManagerService extends SystemService
if (uids != null) {
ws = new WorkSource();
- // XXX should WorkSource have a way to set uids as an int[] instead of adding them
- // one at a time?
- for (int uid : uids) {
- ws.add(uid);
+ if (mFeatureFlags.isWakelockAttributionViaWorkchainEnabled()) {
+ int callingUid = Binder.getCallingUid();
+ for (int uid : uids) {
+ WorkChain workChain = ws.createWorkChain();
+ workChain.addNode(uid, null);
+ workChain.addNode(callingUid, null);
+ }
+ } else {
+ // XXX should WorkSource have a way to set uids as an int[] instead of
+ // adding them one at a time?
+ for (int uid : uids) {
+ ws.add(uid);
+ }
}
}
updateWakeLockWorkSource(lock, ws, null);
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index 42b44013bea2..ebc50fd85f24 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -63,6 +63,10 @@ public class PowerManagerFlags {
private final FlagState mMoveWscLoggingToNotifier =
new FlagState(Flags.FLAG_MOVE_WSC_LOGGING_TO_NOTIFIER, Flags::moveWscLoggingToNotifier);
+ private final FlagState mWakelockAttributionViaWorkchain =
+ new FlagState(Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN,
+ Flags::wakelockAttributionViaWorkchain);
+
/** Returns whether early-screen-timeout-detector is enabled on not. */
public boolean isEarlyScreenTimeoutDetectorEnabled() {
return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -110,6 +114,13 @@ public class PowerManagerFlags {
}
/**
+ * @return Whether the wakelock attribution via workchain is enabled
+ */
+ public boolean isWakelockAttributionViaWorkchainEnabled() {
+ return mWakelockAttributionViaWorkchain.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -120,6 +131,7 @@ public class PowerManagerFlags {
pw.println(" " + mPerDisplayWakeByTouch);
pw.println(" " + mFrameworkWakelockInfo);
pw.println(" " + mMoveWscLoggingToNotifier);
+ pw.println(" " + mWakelockAttributionViaWorkchain);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index 613daf820e34..fefe195dc337 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -23,6 +23,17 @@ flag {
}
flag {
+ name: "wakelock_attribution_via_workchain"
+ namespace: "power"
+ description: "Enables the attribution of wakelocks via WorkChain for updateWakelockUids"
+ bug: "331304805"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "improve_wakelock_latency"
namespace: "power"
description: "Feature flag for tracking the optimizations to improve the latency of acquiring and releasing a wakelock."
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 6b138b986fe7..29a17e1c85ab 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -89,9 +89,13 @@ import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
+import android.os.WorkSource;
import android.os.test.TestLooper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+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.DeviceConfig;
import android.provider.Settings;
@@ -200,6 +204,9 @@ public class PowerManagerServiceTest {
@Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private PowerManagerService mService;
private ContextWrapper mContextSpy;
private BatteryReceiver mBatteryReceiver;
@@ -3045,6 +3052,40 @@ public class PowerManagerServiceTest {
}
/**
+ * Test IPowerManager.updateWakeLockUids() updates the workchain with the new uids
+ */
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN})
+ public void test_updateWakelockUids_updatesWorkchain() {
+ createService();
+ startSystem();
+ final String tag = "wakelock1";
+ final String packageName = "pkg.name";
+ final IBinder token = new Binder();
+ int flags = PowerManager.PARTIAL_WAKE_LOCK;
+ final IWakeLockCallback callback1 = Mockito.mock(IWakeLockCallback.class);
+ final IBinder callbackBinder1 = Mockito.mock(Binder.class);
+ when(callback1.asBinder()).thenReturn(callbackBinder1);
+ WorkSource oldWorksource = new WorkSource();
+ oldWorksource.createWorkChain().addNode(1000, null);
+ mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
+ oldWorksource, null /* historyTag */, Display.INVALID_DISPLAY, callback1);
+ verify(mNotifierMock).onWakeLockAcquired(anyInt(), eq(tag), eq(packageName),
+ anyInt(), anyInt(), eq(oldWorksource), any(), same(callback1));
+
+ WorkSource newWorksource = new WorkSource();
+ newWorksource.createWorkChain().addNode(1011, null)
+ .addNode(Binder.getCallingUid(), null);
+ newWorksource.createWorkChain().addNode(1012, null)
+ .addNode(Binder.getCallingUid(), null);
+ mService.getBinderServiceInstance().updateWakeLockUids(token, new int[]{1011, 1012});
+ verify(mNotifierMock).onWakeLockChanging(anyInt(), eq(tag), eq(packageName),
+ anyInt(), anyInt(), eq(oldWorksource), any(), any(),
+ anyInt(), eq(tag), eq(packageName), anyInt(), anyInt(), eq(newWorksource), any(),
+ any());
+ }
+
+ /**
* Test IPowerManager.updateWakeLockCallback() with a new IWakeLockCallback.
*/
@Test