Search code examples
javaandroidandroid-serviceandroid-mediaplayerprotocolexception

ProtocolException in MediaPlayer streaming Service (MediaHTTPConnection: readAt 20709376 / 32768 => java.net.ProtocolException)


I've made an Android app that main feature is to play audio stream from my server. But for some reason music stops playing after about 20 minutes and the app is throwing ProtocolException (logs from app on Android Studio logcat image). Android Studio logcat

This error is even more weird because it occurs only on some devices. Error occurs on multiple Xiaomi devices (all with Android 10) and Samsung Galaxy S9 (also with Android 10) but on Samsung Galaxy S10 (Android 10) and Huawai tablet that error doesn't occur and music is being played as long as user does not stop it.

This is my PlayerService class code that is responsible for running MediaPlayer as a service:

public class PlayerService extends Service {

private static final String CHANNEL_ID = "PlayerServiceChannel";
private static final int SERVICE_ID = 1;
private MediaPlayer player;
private Notification notification;
private JsonObjectHandler jsonObjectHandler;
private int streamIndex = 9999;
private Messenger messenger;
private PendingIntent pendingIntent;
private NotificationManager notificationManager;
private PendingIntent closeApp;
private WifiManager.WifiLock wifiLock;

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    try {
        JSONObject jsonObject = new JSONObject(Objects.requireNonNull(intent.getStringExtra("JsonInfo")));
        jsonObjectHandler = new JsonObjectHandler(jsonObject);
    } catch (JSONException e) {
        jsonObjectHandler = null;
    }
    return START_REDELIVER_INTENT;
}

@Override
public void onCreate() {
    super.onCreate();
    wifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE))
            .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "playerServiceWiFiLock");
    HandlerThread thread = new HandlerThread("ServiceStartArgument", Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();
    Looper mServiceLooper = thread.getLooper();
    ServiceHandler mServiceHandler = new ServiceHandler(mServiceLooper);
    messenger = new Messenger(mServiceHandler);
    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:

                case TelephonyManager.CALL_STATE_OFFHOOK:
                    stopForeground(true);
                    stopSelf();
                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    break;
            }
            super.onCallStateChanged(state, incomingNumber);
        }
    };

    TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    if (mgr != null) {
        mgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    createNotificationChannel();

    Intent notificationIntent = new Intent(this, LoginActivity.class);

    pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            stopSelf();
            unregisterReceiver(this);
            System.exit(0);
        }
    };

    IntentFilter intentFilter = new IntentFilter("android.intent.CLOSE_APP");
    registerReceiver(broadcastReceiver, intentFilter);

    Intent intentClose = new Intent("android.intent.CLOSE_APP");

    closeApp = PendingIntent.getBroadcast(this, 0, intentClose, 0);

    updateNotification(streamIndex);

    startForeground(SERVICE_ID, notification);
}

private NotificationCompat.Builder createNotification(int streamIndex) {
    String defaultValue;
    if (jsonObjectHandler == null || streamIndex == 9999) {
        defaultValue = "";
    } else {
        defaultValue =
                jsonObjectHandler.getPlaylistButtons().get(streamIndex).getButtonName();
    }
    return new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ikonastream48)
            .setContentIntent(pendingIntent)
            .setContentTitle(getString(R.string.app_name))
            .setContentText(defaultValue)
            .setDefaults(0)
            .setSound(null)
            .addAction(R.drawable.ic_close_black, getString(R.string.close), closeApp);
}

private void updateNotification(int streamIndex) {
    notification = createNotification(streamIndex).build();
    notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    assert notificationManager != null;
    notificationManager.notify(SERVICE_ID, notification);
}

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID, "Example Service Channel", NotificationManager.IMPORTANCE_DEFAULT);

        serviceChannel.setSound(null, null);

        notificationManager = getSystemService(NotificationManager.class);
        if (notificationManager != null) {
            notificationManager.createNotificationChannel(serviceChannel);
        }
    }
}

private void stopServicePlayer() {
    if (player != null) {
        player.stop();
        player.reset();
        player.release();
        player = null;
    }
    stopForeground(true);
}

@Override
public void onDestroy() {
    super.onDestroy();
    stopServicePlayer();
    if (wifiLock.isHeld()) wifiLock.release();
}

private void sendMessageBroadcast(Intent intent) {
    LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    if (streamIndex != 9999) {
        Intent intent1 = new Intent("streamIndex");
        intent1.putExtra("INDEX", streamIndex);
        sendMessageBroadcast(intent1);
    }
    return messenger.getBinder();
}

private final class ServiceHandler extends Handler {

    ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.what == 0) {
            player = new MediaPlayer();
            player.setOnErrorListener((mp, what, extra) -> false);
            player.setAudioAttributes(new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .build());
            player.setAudioStreamType(AudioManager.STREAM_MUSIC);
            player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
            streamIndex = msg.arg1;
            updateNotification(streamIndex);
            try {
                player.setOnPreparedListener(mediaPlayer -> {
                    mediaPlayer.start();
                    Intent intent = new Intent("streamIndex");
                    intent.putExtra("INDEX", streamIndex);
                    sendMessageBroadcast(intent);
                });
                player.setDataSource(jsonObjectHandler
                        .getPlaylistButtons()
                        .get(streamIndex)
                        .getButtonStreamAddress());
            } catch (IOException e) {
                e.printStackTrace();
            }
            player.prepareAsync();
            if (!wifiLock.isHeld()) wifiLock.acquire();
        } else if (msg.what == 1) {
            Intent intent2 = new Intent("streamIndex");
            intent2.putExtra("INDEX", streamIndex);
            sendMessageBroadcast(intent2);
        }
    }
}
}

Thank you for all responses in advance!

P.S. The code was written few years ago (at the beginning of my programming journey), so I'm aware that it looks bad and need to be rewritten in better way.


Solution

  • The problem was on the server side. When the stream was switched from CBR to the VBR in the Lame encoder, the problem stop occurring.