0

[Chromecast] Bump minSdkVersion to 23.

Future releases will no longer support Android L.

Bug: internal b/183489162
Test: build and install mediashell
Change-Id: I8aa62a5b6822b32f136daa5ee3d918cadc7a2d29
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3016354
Auto-Submit: Thoren Paulson <thoren@google.com>
Commit-Queue: Daniel Nicoara <dnicoara@chromium.org>
Reviewed-by: Daniel Nicoara <dnicoara@chromium.org>
Reviewed-by: Luke Halliwell <halliwell@chromium.org>
Reviewed-by: Simeon Anfinrud <sanfin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#900488}
This commit is contained in:
Thoren Paulson
2021-07-12 15:22:13 +00:00
committed by Chromium LUCI CQ
parent b78f6b10f4
commit 9de6cd0594
7 changed files with 17 additions and 131 deletions

@ -678,7 +678,7 @@ if (is_android) {
android_manifest = "$root_gen_dir/cast_shell_manifest/AndroidManifest.xml"
android_manifest_dep = "//chromecast/browser/android:cast_shell_manifest"
min_sdk_version = 21
min_sdk_version = 23
target_sdk_version = 31
shared_libraries = [ "//chromecast/android:libcast_shell_android" ]

@ -8,7 +8,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.chromecast.shell">
<uses-sdk android:minSdkVersion="21"/>
<uses-sdk android:minSdkVersion="23"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>

@ -44,20 +44,18 @@ public class CastAudioFocusRequest {
}
private int getStreamType() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mAudioAttributes != null) {
switch (mAudioAttributes.getContentType()) {
case AudioAttributes.CONTENT_TYPE_MOVIE:
case AudioAttributes.CONTENT_TYPE_MUSIC:
return AudioManager.STREAM_MUSIC;
case AudioAttributes.CONTENT_TYPE_SONIFICATION:
return AudioManager.STREAM_ALARM;
case AudioAttributes.CONTENT_TYPE_SPEECH:
return AudioManager.STREAM_VOICE_CALL;
case AudioAttributes.CONTENT_TYPE_UNKNOWN:
default:
return AudioManager.STREAM_SYSTEM;
}
if (mAudioAttributes != null) {
switch (mAudioAttributes.getContentType()) {
case AudioAttributes.CONTENT_TYPE_MOVIE:
case AudioAttributes.CONTENT_TYPE_MUSIC:
return AudioManager.STREAM_MUSIC;
case AudioAttributes.CONTENT_TYPE_SONIFICATION:
return AudioManager.STREAM_ALARM;
case AudioAttributes.CONTENT_TYPE_SPEECH:
return AudioManager.STREAM_VOICE_CALL;
case AudioAttributes.CONTENT_TYPE_UNKNOWN:
default:
return AudioManager.STREAM_SYSTEM;
}
}
return 0;

@ -9,7 +9,6 @@ import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.audiopolicy.AudioPolicy;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import androidx.annotation.Nullable;
@ -88,35 +87,6 @@ public class CastAudioManager {
return audioFocusLossState;
}
// Only called on Lollipop and below, in an Activity's onPause() event.
// On Lollipop and below, setStreamMute() calls are cumulative and per-application, and if
// Activities don't unmute the streams that they mute, the stream remains muted to other
// applications, which are unable to unmute the stream themselves. Therefore, when an Activity
// is paused, it must unmute any streams it had muted.
// More context in b/19964892 and b/22204758.
@SuppressWarnings("deprecation")
public void releaseStreamMuteIfNecessary(int streamType) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
// On L, if we try to unmute a stream that is not muted, a warning Toast appears.
// Check the stream mute state to determine whether to unmute.
boolean isMuted = false;
try {
// isStreamMute() was only made public in M, but it can be accessed through
// reflection in L.
isMuted = (Boolean) mInternal.getClass()
.getMethod("isStreamMute", int.class)
.invoke(mInternal, streamType);
} catch (Exception e) {
Log.e(TAG, "Can not call AudioManager.isStreamMute().", e);
}
if (isMuted) {
// Note: this is a no-op on fixed-volume devices.
mInternal.setStreamMute(streamType, false);
}
}
}
public int getStreamMaxVolume(int streamType) {
return mInternal.getStreamMaxVolume(streamType);
}

@ -144,13 +144,6 @@ public class CastWebContentsActivity extends Activity {
mAudioManagerState.set(CastAudioManager.getAudioManager(this));
}));
// Clean up stream mute state on pause events.
mAudioManagerState.andThen(Observable.not(mResumedState))
.map(Both::getFirst)
.subscribe(Observers.onEnter((CastAudioManager audioManager) -> {
audioManager.releaseStreamMuteIfNecessary(AudioManager.STREAM_MUSIC);
}));
// Handle each new Intent.
Controller<CastWebContentsSurfaceHelper.StartParams> startParamsState = new Controller<>();
mGotIntentState.and(Observable.not(mIsFinishingState))

@ -4,13 +4,9 @@
package org.chromium.chromecast.shell;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.Build;
import android.util.SparseIntArray;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -184,65 +180,4 @@ public class CastAudioManagerTest {
.closed(CastAudioManager.AudioFocusLoss.TRANSIENT_CAN_DUCK)
.end();
}
// Simulate the AudioManager mute behavior on Android L. The isStreamMute() method is present,
// but can only be used through reflection. Mute requests are cumulative, so a stream only
// unmutes once a equal number of setStreamMute(t, true) setStreamMute(t, false) requests have
// been received.
private static class LollipopAudioManager extends AudioManager {
// Stores the number of total standing mute requests per stream.
private final SparseIntArray mMuteState = new SparseIntArray();
private boolean mCanCallStreamMute = true;
public void setCanCallStreamMute(boolean able) {
mCanCallStreamMute = able;
}
@Override
public boolean isStreamMute(int streamType) {
if (!mCanCallStreamMute) {
throw new RuntimeException("isStreamMute() disabled for testing");
}
return mMuteState.get(streamType, 0) > 0;
}
@Override
public void setStreamMute(int streamType, boolean muteState) {
int delta = muteState ? 1 : -1;
int currentMuteCount = mMuteState.get(streamType, 0);
int newMuteCount = currentMuteCount + delta;
assert newMuteCount >= 0;
mMuteState.put(streamType, newMuteCount);
}
}
@Test
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
public void testReleaseStreamMuteWithNoMute() {
AudioManager fakeAudioManager = new LollipopAudioManager();
CastAudioManager audioManager = new CastAudioManager(fakeAudioManager);
audioManager.releaseStreamMuteIfNecessary(AudioManager.STREAM_MUSIC);
assertFalse(fakeAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
}
@Test
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
public void testReleaseStreamMuteWithMute() {
AudioManager fakeAudioManager = new LollipopAudioManager();
CastAudioManager audioManager = new CastAudioManager(fakeAudioManager);
fakeAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
assertTrue(fakeAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
audioManager.releaseStreamMuteIfNecessary(AudioManager.STREAM_MUSIC);
assertFalse(fakeAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
}
@Test
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
public void testHandleExceptionFromIsStreamMute() {
LollipopAudioManager fakeAudioManager = new LollipopAudioManager();
fakeAudioManager.setCanCallStreamMute(false);
CastAudioManager audioManager = new CastAudioManager(fakeAudioManager);
// This should not crash even if isStreamMute() throws an exception.
audioManager.releaseStreamMuteIfNecessary(AudioManager.STREAM_MUSIC);
}
}

@ -11,7 +11,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Intent;
@ -36,7 +35,6 @@ import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowActivity;
import org.chromium.chromecast.base.Observable;
import org.chromium.content_public.browser.WebContents;
import org.chromium.testing.local.LocalRobolectricTestRunner;
@ -48,6 +46,9 @@ import org.chromium.testing.local.LocalRobolectricTestRunner;
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class CastWebContentsActivityTest {
/**
* ShadowActivity that allows us to intercept calls to setTurnScreenOn.
*/
@Implements(Activity.class)
public static class ExtendedShadowActivity extends ShadowActivity {
private boolean mTurnScreenOn;
@ -108,17 +109,6 @@ public class CastWebContentsActivityTest {
assertNull(intent);
}
@Test
public void testReleasesStreamMuteIfNecessaryOnPause() {
CastAudioManager mockAudioManager = mock(CastAudioManager.class);
when(mockAudioManager.requestAudioFocusWhen(anyObject()))
.thenReturn(mock(Observable.class));
mActivity.setAudioManagerForTesting(mockAudioManager);
mActivityLifecycle.create().start().resume();
mActivityLifecycle.pause();
verify(mockAudioManager).releaseStreamMuteIfNecessary(AudioManager.STREAM_MUSIC);
}
@Test
public void testDropsIntentWithoutUri() {
CastWebContentsSurfaceHelper surfaceHelper = mock(CastWebContentsSurfaceHelper.class);