Manage wakelock when playback suppression is being handled.
After this change, a WakeLock of PowerManager#PARTIAL_WAKE_LOCK level would be acquired when the media is paused due to playback attempt without suitable output. This WakeLock will be release either when the suitable media output has been connected or the set timeout to do so has expired. PiperOrigin-RevId: 661570346
This commit is contained in:
parent
65a471e3db
commit
a76ff16179
@ -25,6 +25,8 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.provider.Settings;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -53,9 +55,10 @@ import java.util.List;
|
||||
* href="https://developer.android.com/media/routing#output-switcher">Media Output Switcher</a> if
|
||||
* it is available on the device, or otherwise the Bluetooth settings screen.
|
||||
*
|
||||
* <p>This implementation also pauses playback when launching the system dialog. The underlying
|
||||
* {@link Player} implementation (e.g. ExoPlayer) is expected to resume playback automatically when
|
||||
* a suitable audio device is connected by the user.
|
||||
* <p>This implementation also pauses playback before opening the system dialog. If the user
|
||||
* connects a suitable media output within the specified timeout, playback resumes automatically.
|
||||
* During this timeout, a power wakelock of the {@link PowerManager#PARTIAL_WAKE_LOCK} level is
|
||||
* obtained to prevent the system from freezing the app.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class WearUnsuitableOutputPlaybackSuppressionResolverListener
|
||||
@ -94,6 +97,9 @@ public final class WearUnsuitableOutputPlaybackSuppressionResolverListener
|
||||
*/
|
||||
private static final int FILTER_TYPE_AUDIO = 1;
|
||||
|
||||
private static final String WAKE_LOCK_TAG =
|
||||
"WearUnsuitableOutputPlaybackSuppressionResolverListener:WakeLock";
|
||||
|
||||
/**
|
||||
* The default timeout for auto-resume of suppressed playback when the playback suppression reason
|
||||
* as {@link Player#PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT} is removed, in
|
||||
@ -106,6 +112,8 @@ public final class WearUnsuitableOutputPlaybackSuppressionResolverListener
|
||||
private final long autoResumeTimeoutAfterUnsuitableOutputSuppressionMs;
|
||||
private final Clock clock;
|
||||
|
||||
@Nullable private final WakeLock wakeLock;
|
||||
|
||||
private long unsuitableOutputPlaybackSuppressionStartRealtimeMs;
|
||||
|
||||
/**
|
||||
@ -144,6 +152,14 @@ public final class WearUnsuitableOutputPlaybackSuppressionResolverListener
|
||||
autoResumeTimeoutAfterUnsuitableOutputSuppressionMs = autoResumeTimeoutMs;
|
||||
this.clock = clock;
|
||||
unsuitableOutputPlaybackSuppressionStartRealtimeMs = C.TIME_UNSET;
|
||||
WakeLock wakeLock = null;
|
||||
PowerManager powerManager =
|
||||
(PowerManager) applicationContext.getSystemService(Context.POWER_SERVICE);
|
||||
if (powerManager != null) {
|
||||
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
|
||||
wakeLock.setReferenceCounted(false);
|
||||
}
|
||||
this.wakeLock = wakeLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -158,6 +174,9 @@ public final class WearUnsuitableOutputPlaybackSuppressionResolverListener
|
||||
== Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT) {
|
||||
player.pause();
|
||||
unsuitableOutputPlaybackSuppressionStartRealtimeMs = clock.elapsedRealtime();
|
||||
if (wakeLock != null && !wakeLock.isHeld()) {
|
||||
wakeLock.acquire(autoResumeTimeoutAfterUnsuitableOutputSuppressionMs);
|
||||
}
|
||||
if (events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
|
||||
launchSystemMediaOutputSwitcherUi(applicationContext);
|
||||
}
|
||||
@ -168,6 +187,9 @@ public final class WearUnsuitableOutputPlaybackSuppressionResolverListener
|
||||
< autoResumeTimeoutAfterUnsuitableOutputSuppressionMs)) {
|
||||
unsuitableOutputPlaybackSuppressionStartRealtimeMs = C.TIME_UNSET;
|
||||
player.play();
|
||||
if (wakeLock != null) {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,7 @@ import org.robolectric.shadows.AudioDeviceInfoBuilder;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
import org.robolectric.shadows.ShadowAudioManager;
|
||||
import org.robolectric.shadows.ShadowPackageManager;
|
||||
import org.robolectric.shadows.ShadowPowerManager;
|
||||
|
||||
/** Tests for the {@link WearUnsuitableOutputPlaybackSuppressionResolverListener}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@ -683,6 +684,82 @@ public class WearUnsuitableOutputPlaybackSuppressionResolverListenerTest {
|
||||
assertThat(testPlayer.isPlaying()).isFalse();
|
||||
}
|
||||
|
||||
/** Test to ensure wake lock is acquired when playback is suppressed due to unsuitable output. */
|
||||
@Test
|
||||
public void playEventWithSuppressedPlaybackCondition_shouldAcquireWakeLock()
|
||||
throws TimeoutException {
|
||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
testPlayer.addListener(
|
||||
new WearUnsuitableOutputPlaybackSuppressionResolverListener(
|
||||
ApplicationProvider.getApplicationContext(), TEST_TIME_OUT_MS, fakeClock));
|
||||
testPlayer.setMediaItem(
|
||||
MediaItem.fromUri("asset:///media/mp4/sample_with_increasing_timestamps_360p.mp4"));
|
||||
testPlayer.prepare();
|
||||
|
||||
testPlayer.play();
|
||||
runUntilPlaybackState(testPlayer, Player.STATE_READY);
|
||||
|
||||
assertThat(ShadowPowerManager.getLatestWakeLock()).isNotNull();
|
||||
assertThat(ShadowPowerManager.getLatestWakeLock().isHeld()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that the wake lock acquired with playback suppression due to unsuitable output
|
||||
* is released after the set timeout.
|
||||
*/
|
||||
@Test
|
||||
public void playEventWithSuppressedPlaybackCondition_shouldReleaseAcquiredWakeLockAfterTimeout()
|
||||
throws TimeoutException {
|
||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
testPlayer.addListener(
|
||||
new WearUnsuitableOutputPlaybackSuppressionResolverListener(
|
||||
ApplicationProvider.getApplicationContext(), TEST_TIME_OUT_MS, fakeClock));
|
||||
testPlayer.setMediaItem(
|
||||
MediaItem.fromUri("asset:///media/mp4/sample_with_increasing_timestamps_360p.mp4"));
|
||||
testPlayer.prepare();
|
||||
testPlayer.play();
|
||||
runUntilPlaybackState(testPlayer, Player.STATE_READY);
|
||||
|
||||
fakeClock.advanceTime(TEST_TIME_OUT_MS * 2);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(ShadowPowerManager.getLatestWakeLock()).isNotNull();
|
||||
assertThat(ShadowPowerManager.getLatestWakeLock().isHeld()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that the wake lock acquired with playback suppression due to unsuitable output
|
||||
* is released after suitable output gets added.
|
||||
*/
|
||||
@Test
|
||||
public void playEventWithSuppressedPlaybackConditionRemoved_shouldReleaseAcquiredWakeLock()
|
||||
throws TimeoutException {
|
||||
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_WATCH, /* supported= */ true);
|
||||
setupConnectedAudioOutput(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||
FakeClock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
testPlayer.addListener(
|
||||
new WearUnsuitableOutputPlaybackSuppressionResolverListener(
|
||||
ApplicationProvider.getApplicationContext(), TEST_TIME_OUT_MS, fakeClock));
|
||||
testPlayer.setMediaItem(
|
||||
MediaItem.fromUri("asset:///media/mp4/sample_with_increasing_timestamps_360p.mp4"));
|
||||
testPlayer.prepare();
|
||||
|
||||
testPlayer.play();
|
||||
runUntilPlaybackState(testPlayer, Player.STATE_READY);
|
||||
|
||||
addConnectedAudioOutput(
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, /* notifyAudioDeviceCallbacks= */ true);
|
||||
runUntilPlayWhenReady(testPlayer, /* expectedPlayWhenReady= */ false);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(ShadowPowerManager.getLatestWakeLock()).isNotNull();
|
||||
assertThat(ShadowPowerManager.getLatestWakeLock().isHeld()).isFalse();
|
||||
}
|
||||
|
||||
private void registerFakeActivity(
|
||||
String fakeActionName, String fakePackageName, String fakeClassName, int applicationFlags) {
|
||||
ComponentName fakeComponentName = new ComponentName(fakePackageName, fakeClassName);
|
||||
|
Loading…
x
Reference in New Issue
Block a user