blob: 409eef9dedf12a20eac8294bb9e2589b7b185acd [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.media;
import android.media.MediaPlayer;
import android.os.SystemClock;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
/**
* Class for listening to Android MediaServer crashes to throttle media decoding
* when needed.
*/
@JNINamespace("media")
public class MediaServerCrashListener implements MediaPlayer.OnErrorListener {
private static final String TAG = "crMediaCrashListener";
private static final long UNKNOWN_TIME = -1;
// Watchdog player. Used to listen to all media server crashes.
private MediaPlayer mPlayer;
// Protecting the creation/release of the watchdog player.
private final Object mLock = new Object();
// Approximate time necessary for the MediaServer to restart after a crash.
private static final int APPROX_MEDIA_SERVER_RESTART_TIME_IN_MS = 5000;
// The last time we reported a failure to create the watchdog as a server crash.
private long mLastReportedWatchdogCreationFailure = UNKNOWN_TIME;
private long mNativeMediaServerCrashListener;
@CalledByNative
private static MediaServerCrashListener create(long nativeMediaServerCrashListener) {
return new MediaServerCrashListener(nativeMediaServerCrashListener);
}
private MediaServerCrashListener(long nativeMediaServerCrashListener) {
mNativeMediaServerCrashListener = nativeMediaServerCrashListener;
}
@CalledByNative
public void releaseWatchdog() {
if (mPlayer == null) return;
mPlayer.release();
mPlayer = null;
}
@CalledByNative
public boolean startListening() {
if (mPlayer != null) return true;
try {
mPlayer = MediaPlayer.create(ContextUtils.getApplicationContext(), R.raw.empty);
} catch (IllegalStateException e) {
Log.e(TAG, "Exception while creating the watchdog player.", e);
} catch (RuntimeException e) {
Log.e(TAG, "Exception while creating the watchdog player.", e);
}
if (mPlayer != null) {
mPlayer.setOnErrorListener(MediaServerCrashListener.this);
// Reset the reported creation failure time on successful
// watchdog creation.
mLastReportedWatchdogCreationFailure = UNKNOWN_TIME;
return true;
}
long currentTime = SystemClock.elapsedRealtime();
// It takes ~5s for the MediaServer to restart. Do not report a
// failure to create a watchdog MediaPlayer as a crash more than
// once per 5s, to prevent a burst of calls to startListening() from
// artificially inflating the number of crashes.
if (mLastReportedWatchdogCreationFailure == UNKNOWN_TIME
|| (currentTime - mLastReportedWatchdogCreationFailure)
> APPROX_MEDIA_SERVER_RESTART_TIME_IN_MS) {
Log.e(TAG, "Unable to create watchdog player, treating it as server crash.");
MediaServerCrashListenerJni.get().onMediaServerCrashDetected(
mNativeMediaServerCrashListener, MediaServerCrashListener.this, false);
mLastReportedWatchdogCreationFailure = currentTime;
}
return false;
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
MediaServerCrashListenerJni.get().onMediaServerCrashDetected(
mNativeMediaServerCrashListener, MediaServerCrashListener.this, true);
releaseWatchdog();
}
return true;
}
@NativeMethods
interface Natives {
void onMediaServerCrashDetected(long nativeMediaServerCrashListener,
MediaServerCrashListener caller, boolean watchdogNeedsRelease);
}
}