Merge "Revert "Move RingtoneTest to media/tests/ringtone"" into main
diff --git a/media/java/android/media/RingtoneV1.java b/media/java/android/media/RingtoneV1.java
index b761afa..3c54d4a 100644
--- a/media/java/android/media/RingtoneV1.java
+++ b/media/java/android/media/RingtoneV1.java
@@ -16,14 +16,15 @@
package android.media;
-import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.media.audiofx.HapticGenerator;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
import android.os.VibrationEffect;
@@ -61,7 +62,6 @@
private final Context mContext;
private final AudioManager mAudioManager;
- private final Ringtone.Injectables mInjectables;
private VolumeShaper.Configuration mVolumeShaperConfig;
private VolumeShaper mVolumeShaper;
@@ -74,10 +74,12 @@
private final IRingtonePlayer mRemotePlayer;
private final Binder mRemoteToken;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private MediaPlayer mLocalPlayer;
private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
private HapticGenerator mHapticGenerator;
+ @UnsupportedAppUsage
private Uri mUri;
private String mTitle;
@@ -92,15 +94,10 @@
private boolean mHapticGeneratorEnabled = false;
private final Object mPlaybackSettingsLock = new Object();
- /** @hide */
+ /** {@hide} */
+ @UnsupportedAppUsage
public RingtoneV1(Context context, boolean allowRemote) {
- this(context, new Ringtone.Injectables(), allowRemote);
- }
-
- /** @hide */
- RingtoneV1(Context context, @NonNull Ringtone.Injectables injectables, boolean allowRemote) {
mContext = context;
- mInjectables = injectables;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAllowRemote = allowRemote;
mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
@@ -203,7 +200,7 @@
}
destroyLocalPlayer();
// try opening uri locally before delegating to remote player
- mLocalPlayer = mInjectables.newMediaPlayer();
+ mLocalPlayer = new MediaPlayer();
try {
mLocalPlayer.setDataSource(mContext, mUri);
mLocalPlayer.setAudioAttributes(mAudioAttributes);
@@ -243,7 +240,19 @@
*/
public boolean hasHapticChannels() {
// FIXME: support remote player, or internalize haptic channels support and remove entirely.
- return mInjectables.hasHapticChannels(mLocalPlayer);
+ try {
+ android.os.Trace.beginSection("Ringtone.hasHapticChannels");
+ if (mLocalPlayer != null) {
+ for(MediaPlayer.TrackInfo trackInfo : mLocalPlayer.getTrackInfo()) {
+ if (trackInfo.hasHapticChannels()) {
+ return true;
+ }
+ }
+ }
+ } finally {
+ android.os.Trace.endSection();
+ }
+ return false;
}
/**
@@ -325,7 +334,7 @@
* @see android.media.audiofx.HapticGenerator#isAvailable()
*/
public boolean setHapticGeneratorEnabled(boolean enabled) {
- if (!mInjectables.isHapticGeneratorAvailable()) {
+ if (!HapticGenerator.isAvailable()) {
return false;
}
synchronized (mPlaybackSettingsLock) {
@@ -353,7 +362,7 @@
mLocalPlayer.setVolume(mVolume);
mLocalPlayer.setLooping(mIsLooping);
if (mHapticGenerator == null && mHapticGeneratorEnabled) {
- mHapticGenerator = mInjectables.createHapticGenerator(mLocalPlayer);
+ mHapticGenerator = HapticGenerator.create(mLocalPlayer.getAudioSessionId());
}
if (mHapticGenerator != null) {
mHapticGenerator.setEnabled(mHapticGeneratorEnabled);
@@ -388,6 +397,7 @@
*
* @hide
*/
+ @UnsupportedAppUsage
public void setUri(Uri uri) {
setUri(uri, null);
}
@@ -415,6 +425,7 @@
}
/** {@hide} */
+ @UnsupportedAppUsage
public Uri getUri() {
return mUri;
}
@@ -545,7 +556,7 @@
Log.e(TAG, "Could not load fallback ringtone");
return false;
}
- mLocalPlayer = mInjectables.newMediaPlayer();
+ mLocalPlayer = new MediaPlayer();
if (afd.getDeclaredLength() < 0) {
mLocalPlayer.setDataSource(afd.getFileDescriptor());
} else {
@@ -583,12 +594,12 @@
}
public boolean isLocalOnly() {
- return !mAllowRemote;
+ return mAllowRemote;
}
public boolean isUsingRemotePlayer() {
// V2 testing api, but this is the v1 approximation.
- return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null) && (mUri != null);
+ return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null);
}
class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
diff --git a/media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
similarity index 69%
rename from media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java
rename to media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
index 2c8daba..3c0c684 100644
--- a/media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
@@ -14,22 +14,20 @@
* limitations under the License.
*/
-package com.android.media;
+package com.android.mediaframeworktest.unit;
import static android.media.Ringtone.MEDIA_SOUND;
import static android.media.Ringtone.MEDIA_SOUND_AND_VIBRATION;
import static android.media.Ringtone.MEDIA_VIBRATION;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerFallbackSetup;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerSetup;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerStarted;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerStopped;
-
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -55,29 +53,34 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.testing.TestableContext;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.framework.base.media.ringtone.tests.R;
-import com.android.media.testing.RingtoneInjectablesTrackingTestRule;
+import com.android.mediaframeworktest.R;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.io.FileNotFoundException;
+import java.util.ArrayDeque;
+import java.util.Map;
+import java.util.Queue;
-/**
- * Test behavior of {@link Ringtone} when it's created via {@link Ringtone.Builder}.
- */
@RunWith(AndroidJUnit4.class)
-public class RingtoneBuilderTest {
+public class RingtoneTest {
private static final Uri SOUND_URI = Uri.parse("content://fake-sound-uri");
@@ -90,8 +93,11 @@
private static final VibrationEffect VIBRATION_EFFECT =
VibrationEffect.createWaveform(new long[] { 0, 100, 50, 100}, -1);
+ private static final VibrationEffect VIBRATION_EFFECT_REPEATING =
+ VibrationEffect.createWaveform(new long[] { 0, 100, 50, 100, 50}, 1);
- @Rule public final RingtoneInjectablesTrackingTestRule
+ @Rule
+ public final RingtoneInjectablesTrackingTestRule
mMediaPlayerRule = new RingtoneInjectablesTrackingTestRule();
@Captor private ArgumentCaptor<IBinder> mIBinderCaptor;
@@ -116,7 +122,6 @@
mContext = spy(testContext);
}
-
@Test
public void testRingtone_fullLifecycleUsingLocalMediaPlayer() throws Exception {
MediaPlayer mockMediaPlayer = mMediaPlayerRule.expectLocalMediaPlayer();
@@ -137,14 +142,14 @@
assertThat(ringtone.isLocalOnly()).isFalse();
// Prepare
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES);
verify(mockMediaPlayer).setVolume(1.0f);
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Verify dynamic controls.
ringtone.setVolume(0.8f);
@@ -160,7 +165,7 @@
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
// This test is intended to strictly verify all interactions with MediaPlayer in a local
// playback case. This shouldn't be necessary in other tests that have the same basic
@@ -194,16 +199,16 @@
assertThat(ringtone.getAudioAttributes()).isEqualTo(audioAttributes);
// Prepare
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, audioAttributes);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, audioAttributes);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verifyZeroInteractions(mMockRemotePlayer);
verifyZeroInteractions(mMockVibrator);
@@ -215,8 +220,8 @@
setupFileNotFound(mockMediaPlayer, SOUND_URI);
Ringtone ringtone =
newBuilder(MEDIA_SOUND, RINGTONE_ATTRIBUTES)
- .setUri(SOUND_URI)
- .build();
+ .setUri(SOUND_URI)
+ .build();
assertThat(ringtone).isNotNull();
assertThat(ringtone.isUsingRemotePlayer()).isTrue();
@@ -279,7 +284,7 @@
// Prepare
// Uses attributes with haptic channels enabled, but will use the effect when there aren't
// any present.
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).setVolume(1.0f);
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
@@ -287,7 +292,7 @@
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
verify(mMockVibrator).vibrate(VIBRATION_EFFECT, RINGTONE_VIB_ATTRIBUTES);
// Verify dynamic controls.
@@ -305,7 +310,7 @@
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verify(mMockVibrator).cancel(VibrationAttributes.USAGE_RINGTONE);
// This test is intended to strictly verify all interactions with MediaPlayer in a local
@@ -383,7 +388,7 @@
// Prepare
// Uses attributes with haptic channels enabled, but will abandon the MediaPlayer when it
// knows there aren't any.
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).setVolume(0.0f); // Vibration-only: sound muted.
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
@@ -438,7 +443,7 @@
// Prepare
// Uses attributes with haptic channels enabled, but will use the effect when there aren't
// any present.
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).setVolume(0.0f); // Vibration-only: sound muted.
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
@@ -446,7 +451,7 @@
// Play
ringtone.play();
// Vibrator.vibrate isn't called because the vibration comes from the sound.
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Verify dynamic controls (no-op without sound)
ringtone.setVolume(0.8f);
@@ -461,7 +466,7 @@
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
// This test is intended to strictly verify all interactions with MediaPlayer in a local
// playback case. This shouldn't be necessary in other tests that have the same basic
@@ -491,17 +496,17 @@
// Prepare
// The attributes here have haptic channels enabled (unlike above)
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
when(mockMediaPlayer.isPlaying()).thenReturn(true);
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verifyZeroInteractions(mMockRemotePlayer);
// Nothing after the initial hasVibrator - it uses audio-coupled.
@@ -531,7 +536,7 @@
// Prepare
// The attributes here have haptic channels enabled (unlike above)
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).prepare();
// Play
@@ -554,7 +559,7 @@
@Test
public void testRingtone_nullMediaOnBuilderUsesFallback() throws Exception {
AssetFileDescriptor testResourceFd =
- mContext.getResources().openRawResourceFd(R.raw.test_sound_file);
+ mContext.getResources().openRawResourceFd(R.raw.shortmp3);
// Ensure it will flow as expected.
assertThat(testResourceFd).isNotNull();
assertThat(testResourceFd.getDeclaredLength()).isAtLeast(0);
@@ -570,18 +575,18 @@
// Delegates straight to fallback in local player.
// Prepare
- verifyPlayerFallbackSetup(mockMediaPlayer, testResourceFd, RINGTONE_ATTRIBUTES);
+ verifyLocalPlayerFallbackSetup(mockMediaPlayer, testResourceFd, RINGTONE_ATTRIBUTES);
verify(mockMediaPlayer).setVolume(1.0f);
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verifyNoMoreInteractions(mockMediaPlayer);
verifyNoMoreInteractions(mMockRemotePlayer);
@@ -610,10 +615,24 @@
verifyNoMoreInteractions(mMockRemotePlayer);
}
+ @Test
+ public void testRingtone_noMediaSetOnBuilderFallbackFailsAndNoRemote() throws Exception {
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.raw.fallbackring, null);
+ Ringtone ringtone = newBuilder(MEDIA_SOUND, RINGTONE_ATTRIBUTES)
+ .setUri(null)
+ .setLocalOnly()
+ .build();
+ // Local player fallback fails as the resource isn't found (no media player creation is
+ // attempted), and since there is no local player, the ringtone ends up having nothing to
+ // do.
+ assertThat(ringtone).isNull();
+ }
+
private Ringtone.Builder newBuilder(@Ringtone.RingtoneMedia int ringtoneMedia,
AudioAttributes audioAttributes) {
return new Ringtone.Builder(mContext, ringtoneMedia, audioAttributes)
- .setInjectables(mMediaPlayerRule.getRingtoneTestInjectables());
+ .setInjectables(mMediaPlayerRule.injectables);
}
private static AudioAttributes audioAttributes(int audioUsage) {
@@ -628,4 +647,194 @@
doThrow(new FileNotFoundException("Fake file not found"))
.when(mockMediaPlayer).setDataSource(any(Context.class), eq(uri));
}
+
+ private void verifyLocalPlayerSetup(MediaPlayer mockPlayer, Uri expectedUri,
+ AudioAttributes expectedAudioAttributes) throws Exception {
+ verify(mockPlayer).setDataSource(mContext, expectedUri);
+ verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
+ verify(mockPlayer).setPreferredDevice(null);
+ verify(mockPlayer).prepare();
+ }
+
+ private void verifyLocalPlayerFallbackSetup(MediaPlayer mockPlayer, AssetFileDescriptor afd,
+ AudioAttributes expectedAudioAttributes) throws Exception {
+ // This is very specific but it's a simple way to test that the test resource matches.
+ if (afd.getDeclaredLength() < 0) {
+ verify(mockPlayer).setDataSource(afd.getFileDescriptor());
+ } else {
+ verify(mockPlayer).setDataSource(afd.getFileDescriptor(),
+ afd.getStartOffset(),
+ afd.getDeclaredLength());
+ }
+ verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
+ verify(mockPlayer).setPreferredDevice(null);
+ verify(mockPlayer).prepare();
+ }
+
+ private void verifyLocalPlay(MediaPlayer mockMediaPlayer) {
+ verify(mockMediaPlayer).setOnCompletionListener(any());
+ verify(mockMediaPlayer).start();
+ }
+
+ private void verifyLocalStop(MediaPlayer mockMediaPlayer) {
+ verify(mockMediaPlayer).stop();
+ verify(mockMediaPlayer).setOnCompletionListener(isNull());
+ verify(mockMediaPlayer).reset();
+ verify(mockMediaPlayer).release();
+ }
+
+ /**
+ * This rule ensures that all expected media player creations from the factory do actually
+ * occur. The reason for this level of control is that creating a media player is fairly
+ * expensive and blocking, so we do want unit tests of this class to "declare" interactions
+ * of all created media players.
+ *
+ * This needs to be a TestRule so that the teardown assertions can be skipped if the test has
+ * failed (and media player assertions may just be a distracting side effect). Otherwise, the
+ * teardown failures hide the real test ones.
+ */
+ public static class RingtoneInjectablesTrackingTestRule implements TestRule {
+ public Ringtone.Injectables injectables = new TestInjectables();
+ public boolean hapticGeneratorAvailable = true;
+
+ // Queue of (local) media players, in order of expected creation. Enqueue using
+ // expectNewMediaPlayer(), dequeued by the media player factory passed to Ringtone.
+ // This queue is asserted to be empty at the end of the test.
+ private Queue<MediaPlayer> mMockMediaPlayerQueue = new ArrayDeque<>();
+
+ // Similar to media players, but for haptic generator, which also needs releasing.
+ private Map<MediaPlayer, HapticGenerator> mMockHapticGeneratorMap = new ArrayMap<>();
+
+ // Media players with haptic channels.
+ private ArraySet<MediaPlayer> mHapticChannels = new ArraySet<>();
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ base.evaluate();
+ // Only assert if the test didn't fail (base.evaluate() would throw).
+ assertWithMessage("Test setup an expectLocalMediaPlayer but it wasn't consumed")
+ .that(mMockMediaPlayerQueue).isEmpty();
+ // Only assert if the test didn't fail (base.evaluate() would throw).
+ assertWithMessage(
+ "Test setup an expectLocalHapticGenerator but it wasn't consumed")
+ .that(mMockHapticGeneratorMap).isEmpty();
+ }
+ };
+ }
+
+ private TestMediaPlayer expectLocalMediaPlayer() {
+ TestMediaPlayer mockMediaPlayer = Mockito.mock(TestMediaPlayer.class);
+ // Delegate to simulated methods. This means they can be verified but also reflect
+ // realistic transitions from the TestMediaPlayer.
+ doCallRealMethod().when(mockMediaPlayer).start();
+ doCallRealMethod().when(mockMediaPlayer).stop();
+ doCallRealMethod().when(mockMediaPlayer).setLooping(anyBoolean());
+ when(mockMediaPlayer.isLooping()).thenCallRealMethod();
+ when(mockMediaPlayer.isLooping()).thenCallRealMethod();
+ mMockMediaPlayerQueue.add(mockMediaPlayer);
+ return mockMediaPlayer;
+ }
+
+ private HapticGenerator expectHapticGenerator(MediaPlayer mockMediaPlayer) {
+ HapticGenerator mockHapticGenerator = Mockito.mock(HapticGenerator.class);
+ // A test should never want this.
+ assertWithMessage("Can't expect a second haptic generator created "
+ + "for one media player")
+ .that(mMockHapticGeneratorMap.put(mockMediaPlayer, mockHapticGenerator))
+ .isNull();
+ return mockHapticGenerator;
+ }
+
+ private void setHasHapticChannels(MediaPlayer mp, boolean hasHapticChannels) {
+ if (hasHapticChannels) {
+ mHapticChannels.add(mp);
+ } else {
+ mHapticChannels.remove(mp);
+ }
+ }
+
+ private class TestInjectables extends Ringtone.Injectables {
+ @Override
+ public MediaPlayer newMediaPlayer() {
+ assertWithMessage(
+ "Unexpected MediaPlayer creation. Bug or need expectNewMediaPlayer")
+ .that(mMockMediaPlayerQueue)
+ .isNotEmpty();
+ return mMockMediaPlayerQueue.remove();
+ }
+
+ @Override
+ public boolean isHapticGeneratorAvailable() {
+ return hapticGeneratorAvailable;
+ }
+
+ @Override
+ public HapticGenerator createHapticGenerator(MediaPlayer mediaPlayer) {
+ HapticGenerator mockHapticGenerator = mMockHapticGeneratorMap.remove(mediaPlayer);
+ assertWithMessage("Unexpected HapticGenerator creation. "
+ + "Bug or need expectHapticGenerator")
+ .that(mockHapticGenerator)
+ .isNotNull();
+ return mockHapticGenerator;
+ }
+
+ @Override
+ public boolean isHapticPlaybackSupported() {
+ return true;
+ }
+
+ @Override
+ public boolean hasHapticChannels(MediaPlayer mp) {
+ return mHapticChannels.contains(mp);
+ }
+ }
+ }
+
+ /**
+ * MediaPlayer relies on a native backend and so its necessary to intercept calls from
+ * fake usage hitting them.
+ *
+ * Mocks don't work directly on native calls, but if they're overridden then it does work.
+ * Some basic state faking is also done to make the mocks more realistic.
+ */
+ private static class TestMediaPlayer extends MediaPlayer {
+ private boolean mIsPlaying = false;
+ private boolean mIsLooping = false;
+
+ @Override
+ public void start() {
+ mIsPlaying = true;
+ }
+
+ @Override
+ public void stop() {
+ mIsPlaying = false;
+ }
+
+ @Override
+ public void setLooping(boolean value) {
+ mIsLooping = value;
+ }
+
+ @Override
+ public boolean isLooping() {
+ return mIsLooping;
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return mIsPlaying;
+ }
+
+ void simulatePlayingFinished() {
+ if (!mIsPlaying) {
+ throw new IllegalStateException(
+ "Attempted to pretend playing finished when not playing");
+ }
+ mIsPlaying = false;
+ }
+ }
}
diff --git a/media/tests/ringtone/Android.bp b/media/tests/ringtone/Android.bp
index 8d1e5e3..55b98c4 100644
--- a/media/tests/ringtone/Android.bp
+++ b/media/tests/ringtone/Android.bp
@@ -9,24 +9,15 @@
srcs: ["src/**/*.java"],
libs: [
- "android.test.base",
- "android.test.mock",
"android.test.runner",
+ "android.test.base",
],
static_libs: [
- "androidx.test.ext.junit",
- "androidx.test.ext.truth",
"androidx.test.rules",
- "frameworks-base-testutils",
- "mockito-target-inline-minus-junit4",
- "testables",
"testng",
- ],
-
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
+ "androidx.test.ext.truth",
+ "frameworks-base-testutils",
],
test_suites: [
diff --git a/media/tests/ringtone/OWNERS b/media/tests/ringtone/OWNERS
deleted file mode 100644
index 93b44f4..0000000
--- a/media/tests/ringtone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 345036
-
-include /services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java b/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java
deleted file mode 100644
index e97e117..0000000
--- a/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 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.media.testing;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.AudioAttributes;
-import android.media.MediaPlayer;
-import android.net.Uri;
-
-/**
- * Helper class with assertion methods on mock {@link MediaPlayer} instances.
- */
-public final class MediaPlayerTestHelper {
-
- /** Verify this local media player mock instance was started. */
- public static void verifyPlayerStarted(MediaPlayer mockMediaPlayer) {
- verify(mockMediaPlayer).setOnCompletionListener(any());
- verify(mockMediaPlayer).start();
- }
-
- /** Verify this local media player mock instance was stopped and released. */
- public static void verifyPlayerStopped(MediaPlayer mockMediaPlayer) {
- verify(mockMediaPlayer).stop();
- verify(mockMediaPlayer).setOnCompletionListener(isNull());
- verify(mockMediaPlayer).reset();
- verify(mockMediaPlayer).release();
- }
-
- /** Verify this local media player mock instance was setup with given attributes. */
- public static void verifyPlayerSetup(Context context, MediaPlayer mockPlayer,
- Uri expectedUri, AudioAttributes expectedAudioAttributes) throws Exception {
- verify(mockPlayer).setDataSource(context, expectedUri);
- verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
- verify(mockPlayer).setPreferredDevice(null);
- verify(mockPlayer).prepare();
- }
-
- /** Verify this local media player mock instance was setup with given fallback attributes. */
- public static void verifyPlayerFallbackSetup(MediaPlayer mockPlayer,
- AssetFileDescriptor afd, AudioAttributes expectedAudioAttributes) throws Exception {
- // This is very specific but it's a simple way to test that the test resource matches.
- if (afd.getDeclaredLength() < 0) {
- verify(mockPlayer).setDataSource(afd.getFileDescriptor());
- } else {
- verify(mockPlayer).setDataSource(afd.getFileDescriptor(),
- afd.getStartOffset(),
- afd.getDeclaredLength());
- }
- verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
- verify(mockPlayer).setPreferredDevice(null);
- verify(mockPlayer).prepare();
- }
-
- private MediaPlayerTestHelper() {
- }
-}
diff --git a/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java b/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java
deleted file mode 100644
index 25752ce..0000000
--- a/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright 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.media.testing;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.when;
-
-import android.media.MediaPlayer;
-import android.media.Ringtone;
-import android.media.audiofx.HapticGenerator;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.Mockito;
-
-import java.util.ArrayDeque;
-import java.util.Map;
-import java.util.Queue;
-
-/**
- * This rule ensures that all expected media player creations from the factory do actually
- * occur. The reason for this level of control is that creating a media player is fairly
- * expensive and blocking, so we do want unit tests of this class to "declare" interactions
- * of all created media players.
- * <p>
- * This needs to be a TestRule so that the teardown assertions can be skipped if the test has
- * failed (and media player assertions may just be a distracting side effect). Otherwise, the
- * teardown failures hide the real test ones.
- */
-public class RingtoneInjectablesTrackingTestRule implements TestRule {
-
- private final Ringtone.Injectables mRingtoneTestInjectables = new TestInjectables();
-
- // Queue of (local) media players, in order of expected creation. Enqueue using
- // expectNewMediaPlayer(), dequeued by the media player factory passed to Ringtone.
- // This queue is asserted to be empty at the end of the test.
- private final Queue<MediaPlayer> mMockMediaPlayerQueue = new ArrayDeque<>();
-
- // Similar to media players, but for haptic generator, which also needs releasing.
- private final Map<MediaPlayer, HapticGenerator> mMockHapticGeneratorMap = new ArrayMap<>();
-
- // Media players with haptic channels.
- private final ArraySet<MediaPlayer> mHapticChannels = new ArraySet<>();
-
- private boolean mHapticGeneratorAvailable = true;
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- base.evaluate();
- // Only assert if the test didn't fail (base.evaluate() would throw).
- assertWithMessage("Test setup an expectLocalMediaPlayer but it wasn't consumed")
- .that(mMockMediaPlayerQueue).isEmpty();
- // Only assert if the test didn't fail (base.evaluate() would throw).
- assertWithMessage(
- "Test setup an expectLocalHapticGenerator but it wasn't consumed")
- .that(mMockHapticGeneratorMap).isEmpty();
- }
- };
- }
-
- /** The {@link Ringtone.Injectables} to be used for creating a testable {@link Ringtone}. */
- public Ringtone.Injectables getRingtoneTestInjectables() {
- return mRingtoneTestInjectables;
- }
-
- /**
- * Create a test {@link MediaPlayer} that will be provided to the {@link Ringtone} instance
- * created with {@link #getRingtoneTestInjectables()}.
- *
- * <p>If a media player is not created during the test execution after this method is called
- * then the test will fail. It will also fail if the ringtone attempts to create one without
- * this method being called first.
- */
- public TestMediaPlayer expectLocalMediaPlayer() {
- TestMediaPlayer mockMediaPlayer = Mockito.mock(TestMediaPlayer.class);
- // Delegate to simulated methods. This means they can be verified but also reflect
- // realistic transitions from the TestMediaPlayer.
- doCallRealMethod().when(mockMediaPlayer).start();
- doCallRealMethod().when(mockMediaPlayer).stop();
- doCallRealMethod().when(mockMediaPlayer).setLooping(anyBoolean());
- when(mockMediaPlayer.isLooping()).thenCallRealMethod();
- mMockMediaPlayerQueue.add(mockMediaPlayer);
- return mockMediaPlayer;
- }
-
- /**
- * Create a test {@link HapticGenerator} that will be provided to the {@link Ringtone} instance
- * created with {@link #getRingtoneTestInjectables()}.
- *
- * <p>If a haptic generator is not created during the test execution after this method is called
- * then the test will fail. It will also fail if the ringtone attempts to create one without
- * this method being called first.
- */
- public HapticGenerator expectHapticGenerator(MediaPlayer mediaPlayer) {
- HapticGenerator mockHapticGenerator = Mockito.mock(HapticGenerator.class);
- // A test should never want this.
- assertWithMessage("Can't expect a second haptic generator created "
- + "for one media player")
- .that(mMockHapticGeneratorMap.put(mediaPlayer, mockHapticGenerator))
- .isNull();
- return mockHapticGenerator;
- }
-
- /**
- * Configures the {@link MediaPlayer} to always return given flag when
- * {@link Ringtone.Injectables#hasHapticChannels(MediaPlayer)} is called.
- */
- public void setHasHapticChannels(MediaPlayer mp, boolean hasHapticChannels) {
- if (hasHapticChannels) {
- mHapticChannels.add(mp);
- } else {
- mHapticChannels.remove(mp);
- }
- }
-
- /** Test implementation of {@link Ringtone.Injectables} that uses the test rule setup. */
- private class TestInjectables extends Ringtone.Injectables {
- @Override
- public MediaPlayer newMediaPlayer() {
- assertWithMessage(
- "Unexpected MediaPlayer creation. Bug or need expectNewMediaPlayer")
- .that(mMockMediaPlayerQueue)
- .isNotEmpty();
- return mMockMediaPlayerQueue.remove();
- }
-
- @Override
- public boolean isHapticGeneratorAvailable() {
- return mHapticGeneratorAvailable;
- }
-
- @Override
- public HapticGenerator createHapticGenerator(MediaPlayer mediaPlayer) {
- HapticGenerator mockHapticGenerator = mMockHapticGeneratorMap.remove(mediaPlayer);
- assertWithMessage("Unexpected HapticGenerator creation. "
- + "Bug or need expectHapticGenerator")
- .that(mockHapticGenerator)
- .isNotNull();
- return mockHapticGenerator;
- }
-
- @Override
- public boolean isHapticPlaybackSupported() {
- return true;
- }
-
- @Override
- public boolean hasHapticChannels(MediaPlayer mp) {
- return mHapticChannels.contains(mp);
- }
- }
-
- /**
- * MediaPlayer relies on a native backend and so its necessary to intercept calls from
- * fake usage hitting them.
- * <p>
- * Mocks don't work directly on native calls, but if they're overridden then it does work.
- * Some basic state faking is also done to make the mocks more realistic.
- */
- public static class TestMediaPlayer extends MediaPlayer {
- private boolean mIsPlaying = false;
- private boolean mIsLooping = false;
-
- @Override
- public void start() {
- mIsPlaying = true;
- }
-
- @Override
- public void stop() {
- mIsPlaying = false;
- }
-
- @Override
- public void setLooping(boolean value) {
- mIsLooping = value;
- }
-
- @Override
- public boolean isLooping() {
- return mIsLooping;
- }
-
- @Override
- public boolean isPlaying() {
- return mIsPlaying;
- }
-
- /**
- * Updates {@link #isPlaying()} result to false, if it's set to true.
- *
- * @throws IllegalStateException is {@link #isPlaying()} is already false
- */
- public void simulatePlayingFinished() {
- if (!mIsPlaying) {
- throw new IllegalStateException(
- "Attempted to pretend playing finished when not playing");
- }
- mIsPlaying = false;
- }
- }
-}