Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(673)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/MediaThrottler.java

Issue 1372203002: Throttle media decoding after excessive Android media server crashes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: addressing comments Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/MediaThrottler.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/MediaThrottler.java b/content/public/android/java/src/org/chromium/content/browser/MediaThrottler.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c008c404221fdb3bc0e8f6e05acab7ff6a9f624
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/MediaThrottler.java
@@ -0,0 +1,199 @@
+// Copyright 2015 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.content.browser;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+
+import org.chromium.base.Log;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.content.R;
+
+/**
+ * Class for listening to Android MediaServer Crashes to throttle media decoding
+ * when needed.
+ */
+@JNINamespace("content")
+class MediaThrottler implements MediaPlayer.OnErrorListener {
+ private static final String TAG = "cr_MediaThrottler";
+ private static final long UNKNOWN_LAST_SERVER_CRASH_TIME = -1;
+
+ // Number of active decode requests.
+ private int mRequestCount;
+
+ // Application context.
+ private final Context mContext;
+
+ // Watch dog player. Used to listen to all media server crashes.
+ private MediaPlayer mPlayer;
+
+ // The last media server crash time since Chrome lauches.
+ private long mLastCrashTime = UNKNOWN_LAST_SERVER_CRASH_TIME;
+
+ // Server crash count since last reset() call.
+ private int mServerCrashCount;
+
+ // Object for synchronized access to memeber variables.
+ private final Object mLock = new Object();
+
+ // Handler for posting delayed tasks.
+ private Handler mHandler;
+
+ // Intervals between media server crashes that are considered normal.
Raymond Toy 2015/09/30 22:37:54 Why are crashes considered "normal"? And why 60 s
qinmin 2015/09/30 22:56:04 This value is based on the discussion with the sec
Raymond Toy 2015/10/01 21:58:20 I think just saying anything larger than 5 sec or
qinmin 2015/10/01 22:53:34 Done.
+ private static final long SERVER_CRASH_INTERVAL_THRESHOLD_IN_MILLIS = 60000;
+
+ // Delay to keep the watch dog player alive When there are no decoding
+ // requests. This is introduced to avoid recreating the watch dog over and
+ // over if a burst of small decoding requests arrive.
+ private static final int RELEASE_WATCH_DOG_PLAYER_DELAY_IN_MILLIS = 5000;
+
+ // When |mServerCrashCount| reaches this threshold, throttling will start.
+ private static final int SERVER_CRASH_COUNT_THRESHOLD_FOR_THROTTLING = 4;
Raymond Toy 2015/09/30 22:37:54 An explanation on how you decided 4 was enough wou
qinmin 2015/09/30 22:56:04 same as above. This value is just introduced to ki
Raymond Toy 2015/10/01 21:58:20 As above, just a short comment saying what you jus
qinmin 2015/10/01 22:53:34 Done.
+
+ /**
+ * A background task to release the watch dog player.
+ */
+ private class ReleaseWatchDogTask extends AsyncTask<Void, Void, Void> {
+ @Override
+ protected Void doInBackground(Void... voids) {
+ synchronized (mLock) {
+ if (mRequestCount == 0 && mPlayer != null) {
+ mPlayer.release();
+ mPlayer = null;
+ }
+ }
+ return null;
+ }
+ }
+
+ private final Runnable mDelayedReleaseRunnable = new Runnable() {
+ @Override
+ public void run() {
+ new ReleaseWatchDogTask().execute();
+ }
+ };
+
+ @CalledByNative
+ private static MediaThrottler create(Context context) {
+ return new MediaThrottler(context);
+ }
+
+ private MediaThrottler(Context context) {
+ mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ /**
+ * A background task to start the watch dog player.
+ */
+ private class StartWatchDogTask extends AsyncTask<Void, Void, Void> {
+ @Override
+ protected Void doInBackground(Void... voids) {
+ synchronized (mLock) {
+ if (mPlayer != null || mRequestCount == 0) return null;
+ mPlayer = MediaPlayer.create(mContext, R.raw.empty);
+ if (mPlayer == null) {
+ Log.e(TAG, "Unable to create watch dog player, treat it as server crash.");
+ onMediaServerCrash();
+ } else {
+ mPlayer.setOnErrorListener(MediaThrottler.this);
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Called to request the permission to decode media data.
+ *
+ * @return true if the request is permitted, or false otherwise.
+ */
+ @CalledByNative
+ private boolean requestDecoderResources() {
+ synchronized (mLock) {
+ long currentTime = SystemClock.elapsedRealtime();
+ if (mLastCrashTime != UNKNOWN_LAST_SERVER_CRASH_TIME
+ && (currentTime - mLastCrashTime < SERVER_CRASH_INTERVAL_THRESHOLD_IN_MILLIS)
+ && mServerCrashCount >= SERVER_CRASH_COUNT_THRESHOLD_FOR_THROTTLING) {
+ Log.e(TAG, "Request to decode media data denied due to throttling.");
+ return false;
+ }
+ mRequestCount++;
+ if (mRequestCount == 1) {
+ mHandler.removeCallbacks(mDelayedReleaseRunnable);
Ted C 2015/10/01 02:19:57 What happens if ReleaseWatchDogTask had already be
qinmin 2015/10/01 18:14:02 If ReleaseWatchDogTask had already been executed,
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ new StartWatchDogTask().execute();
Ted C 2015/10/01 02:19:57 Why post the async task? As opposed to the runnab
qinmin 2015/10/01 18:14:02 Creating the watch dog player will need to initial
Ted C 2015/10/02 17:23:38 Not blocking the UI thread is good, so an AsyncTas
qinmin 2015/10/02 17:31:55 the issue is that async task has to be executed on
Ted C 2015/10/02 18:34:20 Ah ha! Gotcha!
+ }
+ });
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called to signal that a decode request has been completed.
+ */
+ @CalledByNative
+ private void onDecodeRequestFinished() {
+ synchronized (mLock) {
+ mRequestCount--;
+ if (mRequestCount == 0) {
+ // Don't release the watch dog immediately, there could be a
+ // number of small requests coming together.
+ prepareToStopWatchDog();
+ }
+ }
+ }
+
+ /**
+ * Posts a delayed task to stop the watch dog player.
+ */
+ private void prepareToStopWatchDog() {
+ mHandler.postDelayed(mDelayedReleaseRunnable, RELEASE_WATCH_DOG_PLAYER_DELAY_IN_MILLIS);
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
+ synchronized (mLock) {
+ onMediaServerCrash();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called when media server crashes.
+ */
+ private void onMediaServerCrash() {
+ long currentTime = SystemClock.elapsedRealtime();
Ted C 2015/10/01 02:19:57 do you need to lock here? I guess you are already
qinmin 2015/10/01 18:14:02 Done.
+ if (mLastCrashTime != UNKNOWN_LAST_SERVER_CRASH_TIME
+ && (currentTime - mLastCrashTime < SERVER_CRASH_INTERVAL_THRESHOLD_IN_MILLIS)) {
+ mServerCrashCount++;
+ } else {
+ mServerCrashCount = 1;
+ }
+ mLastCrashTime = currentTime;
+ }
+
+ /**
+ * Resets the MediaThrottler to its initial state so that subsequent requests
+ * will not be throttled.
+ */
+ @CalledByNative
+ private void reset() {
+ synchronized (mLock) {
+ mServerCrashCount = 0;
Ted C 2015/10/01 02:19:57 should you cancel mDelayedReleaseRunnable?
qinmin 2015/10/01 18:14:02 No need. If mDelayedReleaseRunnable is already sch
+ mLastCrashTime = UNKNOWN_LAST_SERVER_CRASH_TIME;
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698