Skip to content

Commit

Permalink
Merge branch 'fix-media-session-rejected'
Browse files Browse the repository at this point in the history
  • Loading branch information
valldrac committed Sep 19, 2023
2 parents 4048263 + 80d4003 commit 7b00428
Showing 1 changed file with 78 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.thoughtcrime.securesms.components.voice;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.NonNull;
Expand All @@ -29,7 +32,6 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

import org.checkerframework.checker.units.qual.A;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.MessageTable;
Expand All @@ -42,6 +44,7 @@
import org.thoughtcrime.securesms.service.KeyCachingService;

import java.util.Collections;
import java.util.List;

/**
* Android Service responsible for playback of voice notes.
Expand All @@ -54,30 +57,31 @@ public class VoiceNotePlaybackService extends MediaSessionService {

private static final String TAG = Log.tag(VoiceNotePlaybackService.class);
private static final String SESSION_ID = "VoiceNotePlayback";
private static final String EMPTY_ROOT_ID = "empty-root-id";
private static final int LOAD_MORE_THRESHOLD = 2;

private MediaSession mediaSession;
private VoiceNotePlayer player;
private VoiceNotePlayerEventListener playerEventListener;
private KeyClearedReceiver keyClearedReceiver;
private VoiceNotePlayerCallback voiceNotePlayerCallback;

@Override
public void onCreate() {
super.onCreate();
player = new VoiceNotePlayer(this);
playerEventListener = new VoiceNotePlayerEventListener();
player.addListener(playerEventListener);

if (KeyCachingService.isLocked()) {
Log.d(TAG, "Unbinding VoiceNotePlaybackService. App is locked.");
voiceNotePlayerCallback = new VoiceNotePlayerCallback(this, player);
mediaSession = buildMediaSession(false);

if (mediaSession == null) {
Log.e(TAG, "Unable to create media session at all, stopping service to avoid crash.");
stopSelf();
return;
}

player = new VoiceNotePlayer(this);
player.addListener(new VoiceNotePlayerEventListener());

voiceNotePlayerCallback = new VoiceNotePlayerCallback(this, player);
mediaSession = new MediaSession.Builder(this, player).setCallback(voiceNotePlayerCallback).setId(SESSION_ID).build();
keyClearedReceiver = new KeyClearedReceiver(this, mediaSession.getToken());
keyClearedReceiver = new KeyClearedReceiver(this, mediaSession.getToken());

setMediaNotificationProvider(new VoiceNoteMediaNotificationProvider(this));
setListener(new MediaSessionServiceListener());
Expand All @@ -93,16 +97,15 @@ public void onTaskRemoved(Intent rootIntent) {

@Override
public void onDestroy() {
player.removeListener(playerEventListener);
if (mediaSession != null) {
keyClearedReceiver.unregister();
player.release();
mediaSession.release();
mediaSession = null;
clearListener();
}
clearListener();
super.onDestroy();
if (keyClearedReceiver != null) {
keyClearedReceiver.unregister();
}
}

@Nullable
Expand All @@ -112,6 +115,7 @@ public MediaSession onGetSession(@NonNull MediaSession.ControllerInfo controller
}

private class VoiceNotePlayerEventListener implements Player.Listener {
private int previousPlaybackState = player.getPlaybackState();

@Override
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
Expand All @@ -124,6 +128,7 @@ public void onPlaybackStateChanged(int playbackState) {
}

private void onPlaybackStateChanged(boolean playWhenReady, int playbackState) {
Log.d(TAG, "playWhenReady: " + playWhenReady + "\nplaybackState: " + playbackState);
switch (playbackState) {
case Player.STATE_BUFFERING:
case Player.STATE_READY:
Expand All @@ -134,8 +139,14 @@ private void onPlaybackStateChanged(boolean playWhenReady, int playbackState) {
sendViewedReceiptForCurrentWindowIndex();
}
break;
case Player.STATE_ENDED:
if (previousPlaybackState == Player.STATE_READY) {
player.clearMediaItems();
}
break;
default:
}
previousPlaybackState = playbackState;
}

@Override
Expand Down Expand Up @@ -191,6 +202,58 @@ public void onAudioAttributesChanged(AudioAttributes audioAttributes) {
}
}

/**
* Some devices, such as the ASUS Zenfone 8, erroneously report multiple broadcast receivers for {@value Intent#ACTION_MEDIA_BUTTON} in the package manager.
* This triggers a failure within the {@link MediaSession} initialization and throws an {@link IllegalStateException}.
* This method will catch that exception and attempt to disable the duplicated broadcast receiver in the hopes of getting the package manager to
* report only 1, avoiding the error.
* If that doesn't work, it returns null, signaling the {@link MediaSession} cannot be built on this device.
*
* @return the built MediaSession, or null if the session cannot be built.
*/
private @Nullable MediaSession buildMediaSession(boolean isRetry) {
if (KeyCachingService.isLocked()) {
Log.i(TAG, "Refuse to create media session when app is locked.");
return null;
}
try {
return new MediaSession.Builder(this, player).setCallback(voiceNotePlayerCallback).setId(SESSION_ID).build();
} catch (IllegalStateException e) {

if (isRetry) {
Log.e(TAG, "Unable to create media session, even after retry.", e);
return null;
}

Log.w(TAG, "Unable to create media session with default parameters.", e);
PackageManager pm = this.getPackageManager();
Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
queryIntent.setPackage(this.getPackageName());
final List<ResolveInfo> mediaButtonReceivers = pm.queryBroadcastReceivers(queryIntent, /* flags= */ 0);

Log.d(TAG, "Found " + mediaButtonReceivers.size() + " BroadcastReceivers for " + Intent.ACTION_MEDIA_BUTTON);

boolean found = false;

if (mediaButtonReceivers.size() > 1) {
for (ResolveInfo receiverInfo : mediaButtonReceivers) {

final ActivityInfo activityInfo = receiverInfo.activityInfo;

if (!found && activityInfo.packageName.contains("androidx.media.session")) {
found = true;
} else {
pm.setComponentEnabledSetting(new ComponentName(activityInfo.packageName, activityInfo.name), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}

return buildMediaSession(true);
} else {
return null;
}
}
}

private @Nullable PlaybackParameters getPlaybackParametersForWindowPosition(int currentWindowIndex) {
if (isAudioMessage(currentWindowIndex)) {
return player.getPlaybackParameters();
Expand Down

0 comments on commit 7b00428

Please sign in to comment.