From 83c2ca59025604d7940806a3d26bf4614a4a2041 Mon Sep 17 00:00:00 2001 From: samrobinson Date: Thu, 27 Feb 2020 16:06:22 +0000 Subject: [PATCH] Add `WifiLock` management to `SimpleExoPlayer`. Issue:#6914 PiperOrigin-RevId: 297598910 --- RELEASENOTES.md | 4 +- .../java/com/google/android/exoplayer2/C.java | 31 ++++++ .../android/exoplayer2/SimpleExoPlayer.java | 70 +++++++++++--- .../android/exoplayer2/WakeLockManager.java | 28 +++--- .../android/exoplayer2/WifiLockManager.java | 95 +++++++++++++++++++ 5 files changed, 203 insertions(+), 25 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/WifiLockManager.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 077397c4a6a..ce0c9c5eb11 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,7 +1,9 @@ # Release notes # -### 2.11.4 (2020-04-01) ### +### 2.11.4 (2020-04-08) ### +* Add optional automatic `WifiLock` handling to `SimpleExoPlayer` + ([#6914](https://github.com/google/ExoPlayer/issues/6914)). * Text: * Catch and log exceptions in `TextRenderer` rather than re-throwing. This allows playback to continue even if subtitle decoding fails diff --git a/library/core/src/main/java/com/google/android/exoplayer2/C.java b/library/core/src/main/java/com/google/android/exoplayer2/C.java index e926e90d22e..43cedf985be 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/C.java @@ -1019,6 +1019,37 @@ private C() {} /** Network type for other connections which are not Wifi or cellular (e.g. VPN, Bluetooth). */ public static final int NETWORK_TYPE_OTHER = 8; + /** + * Mode specifying whether the player should hold a WakeLock and a WifiLock. One of {@link + * #WAKE_MODE_NONE}, {@link #WAKE_MODE_LOCAL} and {@link #WAKE_MODE_NETWORK}. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({WAKE_MODE_NONE, WAKE_MODE_LOCAL, WAKE_MODE_NETWORK}) + public @interface WakeMode {} + /** + * A wake mode that will not cause the player to hold any locks. + * + *

This is suitable for applications that do not play media with the screen off. + */ + public static final int WAKE_MODE_NONE = 0; + /** + * A wake mode that will cause the player to hold a {@link android.os.PowerManager.WakeLock} + * during playback. + * + *

This is suitable for applications that play media with the screen off and do not load media + * over wifi. + */ + public static final int WAKE_MODE_LOCAL = 1; + /** + * A wake mode that will cause the player to hold a {@link android.os.PowerManager.WakeLock} and a + * {@link android.net.wifi.WifiManager.WifiLock} during playback. + * + *

This is suitable for applications that play media with the screen off and may load media + * over wifi. + */ + public static final int WAKE_MODE_NETWORK = 2; + /** * Track role flags. Possible flag values are {@link #ROLE_FLAG_MAIN}, {@link * #ROLE_FLAG_ALTERNATE}, {@link #ROLE_FLAG_SUPPLEMENTARY}, {@link #ROLE_FLAG_COMMENTARY}, {@link diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index dec9bd76d81..a3c82d68cf4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -325,6 +325,7 @@ public SimpleExoPlayer build() { private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioFocusManager audioFocusManager; private final WakeLockManager wakeLockManager; + private final WifiLockManager wifiLockManager; @Nullable private Format videoFormat; @Nullable private Format audioFormat; @@ -460,6 +461,7 @@ protected SimpleExoPlayer( new AudioBecomingNoisyManager(context, eventHandler, componentListener); audioFocusManager = new AudioFocusManager(context, eventHandler, componentListener); wakeLockManager = new WakeLockManager(context); + wifiLockManager = new WifiLockManager(context); } @Override @@ -1294,6 +1296,7 @@ public void release() { audioBecomingNoisyManager.setEnabled(false); audioFocusManager.handleStop(); wakeLockManager.setStayAwake(false); + wifiLockManager.setStayAwake(false); player.release(); removeSurfaceCallbacks(); if (surface != null) { @@ -1432,9 +1435,45 @@ public long getContentBufferedPosition() { * * @param handleWakeLock Whether the player should use a {@link android.os.PowerManager.WakeLock} * to ensure the device stays awake for playback, even when the screen is off. + * @deprecated Use {@link #setWakeMode(int)} instead. */ + @Deprecated public void setHandleWakeLock(boolean handleWakeLock) { - wakeLockManager.setEnabled(handleWakeLock); + setWakeMode(handleWakeLock ? C.WAKE_MODE_LOCAL : C.WAKE_MODE_NONE); + } + + /** + * Sets how the player should keep the device awake for playback when the screen is off. + * + *

Enabling this feature requires the {@link android.Manifest.permission#WAKE_LOCK} permission. + * It should be used together with a foreground {@link android.app.Service} for use cases where + * playback occurs and the screen is off (e.g. background audio playback). It is not useful when + * the screen will be kept on during playback (e.g. foreground video playback). + * + *

When enabled, the locks ({@link android.os.PowerManager.WakeLock} / {@link + * android.net.wifi.WifiManager.WifiLock}) will be held whenever the player is in the {@link + * #STATE_READY} or {@link #STATE_BUFFERING} states with {@code playWhenReady = true}. The locks + * held depends on the specified {@link C.WakeMode}. + * + * @param wakeMode The {@link C.WakeMode} option to keep the device awake during playback. + */ + public void setWakeMode(@C.WakeMode int wakeMode) { + switch (wakeMode) { + case C.WAKE_MODE_NONE: + wakeLockManager.setEnabled(false); + wifiLockManager.setEnabled(false); + break; + case C.WAKE_MODE_LOCAL: + wakeLockManager.setEnabled(true); + wifiLockManager.setEnabled(false); + break; + case C.WAKE_MODE_NETWORK: + wakeLockManager.setEnabled(true); + wifiLockManager.setEnabled(true); + break; + default: + break; + } } // Internal methods. @@ -1537,6 +1576,24 @@ private void verifyApplicationThread() { } } + private void updateWakeAndWifiLock() { + @State int playbackState = getPlaybackState(); + switch (playbackState) { + case Player.STATE_READY: + case Player.STATE_BUFFERING: + wakeLockManager.setStayAwake(getPlayWhenReady()); + wifiLockManager.setStayAwake(getPlayWhenReady()); + break; + case Player.STATE_ENDED: + case Player.STATE_IDLE: + wakeLockManager.setStayAwake(false); + wifiLockManager.setStayAwake(false); + break; + default: + throw new IllegalStateException(); + } + } + private final class ComponentListener implements VideoRendererEventListener, AudioRendererEventListener, @@ -1781,16 +1838,7 @@ public void onLoadingChanged(boolean isLoading) { @Override public void onPlayerStateChanged(boolean playWhenReady, @State int playbackState) { - switch (playbackState) { - case Player.STATE_READY: - case Player.STATE_BUFFERING: - wakeLockManager.setStayAwake(playWhenReady); - break; - case Player.STATE_ENDED: - case Player.STATE_IDLE: - wakeLockManager.setStayAwake(false); - break; - } + updateWakeAndWifiLock(); } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/WakeLockManager.java b/library/core/src/main/java/com/google/android/exoplayer2/WakeLockManager.java index 1e718136e87..bd385b0849d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/WakeLockManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/WakeLockManager.java @@ -39,7 +39,8 @@ private boolean stayAwake; public WakeLockManager(Context context) { - powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + powerManager = + (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE); } /** @@ -48,15 +49,15 @@ public WakeLockManager(Context context) { *

By default, wake lock handling is not enabled. Enabling this will acquire the wake lock if * necessary. Disabling this will release the wake lock if it is held. * - * @param enabled True if the player should handle a {@link WakeLock}, false otherwise. Please - * note that enabling this requires the {@link android.Manifest.permission#WAKE_LOCK} - * permission. + *

Enabling {@link WakeLock} requires the {@link android.Manifest.permission#WAKE_LOCK}. + * + * @param enabled True if the player should handle a {@link WakeLock}, false otherwise. */ public void setEnabled(boolean enabled) { if (enabled) { if (wakeLock == null) { if (powerManager == null) { - Log.w(TAG, "PowerManager was null, therefore the WakeLock was not created."); + Log.w(TAG, "PowerManager is null, therefore not creating the WakeLock."); return; } wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG); @@ -86,15 +87,16 @@ public void setStayAwake(boolean stayAwake) { // reasonable timeout that would not affect the user. @SuppressLint("WakelockTimeout") private void updateWakeLock() { - // Needed for the library nullness check. If enabled is true, the wakelock will not be null. - if (wakeLock != null) { - if (enabled && stayAwake) { - if (!wakeLock.isHeld()) { - wakeLock.acquire(); - } - } else if (wakeLock.isHeld()) { - wakeLock.release(); + if (wakeLock == null) { + return; + } + + if (enabled && stayAwake) { + if (!wakeLock.isHeld()) { + wakeLock.acquire(); } + } else if (wakeLock.isHeld()) { + wakeLock.release(); } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/WifiLockManager.java b/library/core/src/main/java/com/google/android/exoplayer2/WifiLockManager.java new file mode 100644 index 00000000000..f37ec6ab8b8 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/WifiLockManager.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import android.content.Context; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.WifiLock; +import androidx.annotation.Nullable; +import com.google.android.exoplayer2.util.Log; + +/** + * Handles a {@link WifiLock} + * + *

The handling of wifi locks requires the {@link android.Manifest.permission#WAKE_LOCK} + * permission. + */ +/* package */ final class WifiLockManager { + + private static final String TAG = "WifiLockManager"; + private static final String WIFI_LOCK_TAG = "ExoPlayer:WifiLockManager"; + + @Nullable private final WifiManager wifiManager; + @Nullable private WifiLock wifiLock; + private boolean enabled; + private boolean stayAwake; + + public WifiLockManager(Context context) { + wifiManager = + (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + } + + /** + * Sets whether to enable the usage of a {@link WifiLock}. + * + *

By default, wifi lock handling is not enabled. Enabling will acquire the wifi lock if + * necessary. Disabling will release the wifi lock if held. + * + *

Enabling {@link WifiLock} requires the {@link android.Manifest.permission#WAKE_LOCK}. + * + * @param enabled True if the player should handle a {@link WifiLock}. + */ + public void setEnabled(boolean enabled) { + if (enabled && wifiLock == null) { + if (wifiManager == null) { + Log.w(TAG, "WifiManager is null, therefore not creating the WifiLock."); + return; + } + wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, WIFI_LOCK_TAG); + } + + this.enabled = enabled; + updateWifiLock(); + } + + /** + * Sets whether to acquire or release the {@link WifiLock}. + * + *

The wifi lock will not be acquired unless handling has been enabled through {@link + * #setEnabled(boolean)}. + * + * @param stayAwake True if the player should acquire the {@link WifiLock}. False if it should + * release. + */ + public void setStayAwake(boolean stayAwake) { + this.stayAwake = stayAwake; + updateWifiLock(); + } + + private void updateWifiLock() { + if (wifiLock == null) { + return; + } + + if (enabled && stayAwake) { + if (!wifiLock.isHeld()) { + wifiLock.acquire(); + } + } else if (wifiLock.isHeld()) { + wifiLock.release(); + } + } +}