From e51f6556a91ce2e5f412aac546b994531cf305f0 Mon Sep 17 00:00:00 2001 From: Matt Casey Date: Fri, 3 Jun 2022 02:25:11 +0000 Subject: [PATCH] Use MediaPlayer in a background thread. The intitial sound loading is slow so needs to be async. Bug: 231494899 Test: Manual verification including logs for timing, also requesting a lot of screenshots very fast to ensure it doesn't fall over. Change-Id: I011093e5a96912e52a16e957c44f1d899022ae13 --- .../screenshot/ScreenshotController.java | 73 ++++++++++++++----- .../screenshot/TakeScreenshotService.java | 3 +- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 8ce422ac93f80..5b6e5ce95b148 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -83,6 +83,8 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import android.window.WindowContext; +import androidx.concurrent.futures.CallbackToFutureAdapter; + import com.android.internal.app.ChooserActivity; import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; @@ -254,7 +256,7 @@ public class ScreenshotController { private final WindowManager mWindowManager; private final WindowManager.LayoutParams mWindowLayoutParams; private final AccessibilityManager mAccessibilityManager; - private final MediaPlayer mCameraSound; + private final ListenableFuture mCameraSound; private final ScrollCaptureClient mScrollCaptureClient; private final PhoneWindow mWindow; private final DisplayManager mDisplayManager; @@ -338,13 +340,7 @@ public class ScreenshotController { reloadAssets(); // Setup the Camera shutter sound - mCameraSound = MediaPlayer.create(mContext, - Uri.fromFile(new File(mContext.getResources().getString( - com.android.internal.R.string.config_cameraShutterSound))), null, - new AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .build(), AudioSystem.newAudioSessionId()); + mCameraSound = loadCameraSound(); mCopyBroadcastReceiver = new BroadcastReceiver() { @Override @@ -442,16 +438,32 @@ public class ScreenshotController { return mScreenshotView.isPendingSharedTransition(); } + // Any cleanup needed when the service is being destroyed. + void onDestroy() { + removeWindow(); + releaseMediaPlayer(); + releaseContext(); + mBgExecutor.shutdownNow(); + } + /** * Release the constructed window context. */ - void releaseContext() { + private void releaseContext() { mContext.unregisterReceiver(mCopyBroadcastReceiver); mContext.release(); - if (mCameraSound != null) { - mCameraSound.release(); + } + + private void releaseMediaPlayer() { + // Note that this may block if the sound is still being loaded (very unlikely) but we can't + // reliably release in the background because the service is being destroyed. + try { + MediaPlayer player = mCameraSound.get(); + if (player != null) { + player.release(); + } + } catch (InterruptedException | ExecutionException e) { } - mBgExecutor.shutdownNow(); } /** @@ -822,15 +834,42 @@ public class ScreenshotController { } } + private ListenableFuture loadCameraSound() { + // The media player creation is slow and needs on the background thread. + return CallbackToFutureAdapter.getFuture((completer) -> { + mBgExecutor.execute(() -> { + MediaPlayer player = MediaPlayer.create(mContext, + Uri.fromFile(new File(mContext.getResources().getString( + com.android.internal.R.string.config_cameraShutterSound))), null, + new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build(), AudioSystem.newAudioSessionId()); + completer.set(player); + }); + return "ScreenshotController#loadCameraSound"; + }); + } + + private void playCameraSound() { + mCameraSound.addListener(() -> { + try { + MediaPlayer player = mCameraSound.get(); + if (player != null) { + player.start(); + } + } catch (InterruptedException | ExecutionException e) { + } + }, mBgExecutor); + } + /** * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on * failure). */ private void saveScreenshotAndToast(Consumer finisher) { // Play the shutter sound to notify that we've taken a screenshot - if (mCameraSound != null) { - mCameraSound.start(); - } + playCameraSound(); saveScreenshotInWorkerThread( /* onComplete */ finisher, @@ -864,9 +903,7 @@ public class ScreenshotController { mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); // Play the shutter sound to notify that we've taken a screenshot - if (mCameraSound != null) { - mCameraSound.start(); - } + playCameraSound(); if (DEBUG_ANIM) { Log.d(TAG, "starting post-screenshot animation"); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 90e7631b4b03a..32d82037efb94 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -153,8 +153,7 @@ public class TakeScreenshotService extends Service { public void onDestroy() { super.onDestroy(); if (mScreenshot != null) { - mScreenshot.removeWindow(); - mScreenshot.releaseContext(); + mScreenshot.onDestroy(); mScreenshot = null; } if (DEBUG_SERVICE) {