PageRenderTime 42ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/content/public/android/java/src/org/chromium/content/browser/MediaThrottler.java

https://gitlab.com/0072016/Facebook-SDK-
Java | 224 lines | 146 code | 26 blank | 52 comment | 28 complexity | 78d0627a45e5ac03a04e2d5bb7de8e90 MD5 | raw file
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. package org.chromium.content.browser;
  5. import android.content.Context;
  6. import android.media.MediaPlayer;
  7. import android.os.AsyncTask;
  8. import android.os.Handler;
  9. import android.os.Looper;
  10. import android.os.SystemClock;
  11. import org.chromium.base.Log;
  12. import org.chromium.base.annotations.CalledByNative;
  13. import org.chromium.base.annotations.JNINamespace;
  14. import org.chromium.base.metrics.RecordHistogram;
  15. import org.chromium.content.R;
  16. /**
  17. * Class for listening to Android MediaServer Crashes to throttle media decoding
  18. * when needed.
  19. */
  20. @JNINamespace("content")
  21. class MediaThrottler implements MediaPlayer.OnErrorListener {
  22. private static final String TAG = "cr_MediaThrottler";
  23. private static final long UNKNOWN_LAST_SERVER_CRASH_TIME = -1;
  24. // Number of active decode requests.
  25. private int mRequestCount;
  26. // Application context.
  27. private final Context mContext;
  28. // Watch dog player. Used to listen to all media server crashes.
  29. private MediaPlayer mPlayer;
  30. // The last media server crash time since Chrome lauches.
  31. private long mLastCrashTime = UNKNOWN_LAST_SERVER_CRASH_TIME;
  32. // Server crash count since last reset() call.
  33. private int mServerCrashCount;
  34. // Object for synchronized access to memeber variables.
  35. private final Object mLock = new Object();
  36. // Handler for posting delayed tasks.
  37. private Handler mHandler;
  38. // Intervals between media server crashes that are considered normal. It
  39. // takes about 5 seconds to restart the media server. So this value has to
  40. // be larger than 5 seconds.
  41. private static final long SERVER_CRASH_INTERVAL_THRESHOLD_IN_MILLIS = 60000;
  42. // Delay to keep the watch dog player alive When there are no decoding
  43. // requests. This is introduced to avoid recreating the watch dog over and
  44. // over if a burst of small decoding requests arrive.
  45. private static final int RELEASE_WATCH_DOG_PLAYER_DELAY_IN_MILLIS = 5000;
  46. // When |mServerCrashCount| reaches this threshold, throttling will start.
  47. // This is to prevent a page from loading a malformed video over and over
  48. // to crash the media server excessively.
  49. private static final int SERVER_CRASH_COUNT_THRESHOLD_FOR_THROTTLING = 4;
  50. /**
  51. * A background task to release the watch dog player.
  52. */
  53. private class ReleaseWatchDogTask extends AsyncTask<Void, Void, Void> {
  54. @Override
  55. protected Void doInBackground(Void... voids) {
  56. synchronized (mLock) {
  57. if (mRequestCount == 0 && mPlayer != null) {
  58. mPlayer.release();
  59. mPlayer = null;
  60. }
  61. }
  62. return null;
  63. }
  64. }
  65. private final Runnable mDelayedReleaseRunnable = new Runnable() {
  66. @Override
  67. public void run() {
  68. new ReleaseWatchDogTask().execute();
  69. }
  70. };
  71. @CalledByNative
  72. private static MediaThrottler create(Context context) {
  73. return new MediaThrottler(context);
  74. }
  75. private MediaThrottler(Context context) {
  76. mContext = context;
  77. mHandler = new Handler(Looper.getMainLooper());
  78. }
  79. /**
  80. * A background task to start the watch dog player.
  81. */
  82. private class StartWatchDogTask extends AsyncTask<Void, Void, Void> {
  83. @Override
  84. protected Void doInBackground(Void... voids) {
  85. synchronized (mLock) {
  86. if (mPlayer != null || mRequestCount == 0) return null;
  87. try {
  88. mPlayer = MediaPlayer.create(mContext, R.raw.empty);
  89. } catch (IllegalStateException e) {
  90. Log.e(TAG, "Exception happens while creating the watch dog player.", e);
  91. } catch (RuntimeException e) {
  92. Log.e(TAG, "Exception happens while creating the watch dog player.", e);
  93. }
  94. if (mPlayer == null) {
  95. Log.e(TAG, "Unable to create watch dog player, treat it as server crash.");
  96. onMediaServerCrash();
  97. } else {
  98. mPlayer.setOnErrorListener(MediaThrottler.this);
  99. }
  100. }
  101. return null;
  102. }
  103. }
  104. /**
  105. * Called to request the permission to decode media data.
  106. *
  107. * @return true if the request is permitted, or false otherwise.
  108. */
  109. @CalledByNative
  110. private boolean requestDecoderResources() {
  111. synchronized (mLock) {
  112. long currentTime = SystemClock.elapsedRealtime();
  113. if (mLastCrashTime != UNKNOWN_LAST_SERVER_CRASH_TIME
  114. && (currentTime - mLastCrashTime < SERVER_CRASH_INTERVAL_THRESHOLD_IN_MILLIS)
  115. && mServerCrashCount >= SERVER_CRASH_COUNT_THRESHOLD_FOR_THROTTLING) {
  116. Log.e(TAG, "Request to decode media data denied due to throttling.");
  117. return false;
  118. }
  119. mRequestCount++;
  120. if (mRequestCount == 1 || mPlayer == null) {
  121. mHandler.removeCallbacks(mDelayedReleaseRunnable);
  122. mHandler.post(new Runnable() {
  123. @Override
  124. public void run() {
  125. new StartWatchDogTask().execute();
  126. }
  127. });
  128. }
  129. }
  130. return true;
  131. }
  132. /**
  133. * Called to signal that a decode request has been completed.
  134. */
  135. @CalledByNative
  136. private void onDecodeRequestFinished() {
  137. synchronized (mLock) {
  138. mRequestCount--;
  139. if (mRequestCount == 0) {
  140. // Don't release the watch dog immediately, there could be a
  141. // number of small requests coming together.
  142. prepareToStopWatchDog();
  143. }
  144. }
  145. }
  146. /**
  147. * Posts a delayed task to stop the watch dog player.
  148. */
  149. private void prepareToStopWatchDog() {
  150. mHandler.postDelayed(mDelayedReleaseRunnable, RELEASE_WATCH_DOG_PLAYER_DELAY_IN_MILLIS);
  151. }
  152. @Override
  153. public boolean onError(MediaPlayer mp, int what, int extra) {
  154. if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
  155. synchronized (mLock) {
  156. onMediaServerCrash();
  157. }
  158. }
  159. return true;
  160. }
  161. /**
  162. * Called when media server crashes.
  163. */
  164. private void onMediaServerCrash() {
  165. assert Thread.holdsLock(mLock);
  166. long currentTime = SystemClock.elapsedRealtime();
  167. if (mLastCrashTime != UNKNOWN_LAST_SERVER_CRASH_TIME
  168. && (currentTime - mLastCrashTime < SERVER_CRASH_INTERVAL_THRESHOLD_IN_MILLIS)) {
  169. mServerCrashCount++;
  170. } else {
  171. recordNumMediaServerCrashes();
  172. mServerCrashCount = 1;
  173. }
  174. mLastCrashTime = currentTime;
  175. }
  176. /**
  177. * Resets the MediaThrottler to its initial state so that subsequent requests
  178. * will not be throttled.
  179. */
  180. @CalledByNative
  181. private void reset() {
  182. synchronized (mLock) {
  183. recordNumMediaServerCrashes();
  184. mServerCrashCount = 0;
  185. mLastCrashTime = UNKNOWN_LAST_SERVER_CRASH_TIME;
  186. }
  187. }
  188. /**
  189. * Records the number of consecutive media server crashes in UMA.
  190. */
  191. private void recordNumMediaServerCrashes() {
  192. assert Thread.holdsLock(mLock);
  193. if (mServerCrashCount > 0) {
  194. RecordHistogram.recordCountHistogram(
  195. "Media.Android.NumMediaServerCrashes", mServerCrashCount);
  196. }
  197. }
  198. }