Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddceab18 authored by Anton Potapov's avatar Anton Potapov
Browse files

Implement ScreenRecordingService to record provided capture target.

This is a thin service that will be running in the user process on hsum
enabled devices. It doesn't have the burden of the whole System UI
dagger graph meaning it's significantly cheaper to start.

It should delegate the majority of its functionality back to the System
UI using aidl interfaces.

Flag: EXEMPT BUG FIX
Bug: 368579013
Test: passes presubmits
Change-Id: I5b018bc546f17f6ae207549a8ad8413ba57fa28d
parent cfd57ff8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -534,6 +534,8 @@
            android:singleUser="true"
            android:permission="android.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE" />

        <service android:name=".screenrecord.service.ScreenRecordingService" />

        <service android:name=".screenrecord.RecordingService"
                 android:foregroundServiceType="systemExempted"/>

+19 −0
Original line number Diff line number Diff line
/**
 * Copyright (c) 2025, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.mediaprojection;

parcelable MediaProjectionCaptureTarget;
 No newline at end of file
+6 −2
Original line number Diff line number Diff line
@@ -216,8 +216,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList
                        mAudioSource,
                        captureTarget,
                        displayId,
                        this,
                        mScreenRecordingStartTimeStore
                        this
                );

                if (startRecording()) {
@@ -619,6 +618,11 @@ public class RecordingService extends Service implements ScreenMediaRecorderList
        onStartCommand(stopIntent, 0, 0);
    }

    @Override
    public void onStarted() {
        mScreenRecordingStartTimeStore.markStartTime();
    }

    @Override
    public void onStopped(int userId, @StopReason int stopReason) {
        if (mController.isRecording()) {
+17 −16
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ import android.view.Surface;

import com.android.internal.R;
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import com.android.systemui.recordissue.ScreenRecordingStartTimeStore;

import java.io.Closeable;
import java.io.File;
@@ -81,7 +80,6 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
    private static final long MAX_FILESIZE_BYTES = 5000000000L;
    private static final String TAG = "ScreenMediaRecorder";


    private File mTempVideoFile;
    private File mTempAudioFile;
    private MediaProjection mMediaProjection;
@@ -89,11 +87,9 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
    private VirtualDisplay mVirtualDisplay;
    private MediaRecorder mMediaRecorder;
    private int mUid;
    private ScreenRecordingMuxer mMuxer;
    private ScreenInternalAudioRecorder mAudio;
    private ScreenRecordingAudioSource mAudioSource;
    private final MediaProjectionCaptureTarget mCaptureRegion;
    private final ScreenRecordingStartTimeStore mScreenRecordingStartTimeStore;
    private final Handler mHandler;
    private final int mDisplayId;

@@ -107,8 +103,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
            ScreenRecordingAudioSource audioSource,
            MediaProjectionCaptureTarget captureRegion,
            int displayId,
            ScreenMediaRecorderListener listener,
            ScreenRecordingStartTimeStore screenRecordingStartTimeStore) {
            ScreenMediaRecorderListener listener) {
        mContext = context;
        mHandler = handler;
        mUid = uid;
@@ -116,7 +111,6 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
        mListener = listener;
        mAudioSource = audioSource;
        mDisplayId = displayId;
        mScreenRecordingStartTimeStore = screenRecordingStartTimeStore;
    }

    private void prepare() throws IOException, RemoteException, RuntimeException {
@@ -290,18 +284,18 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
    /**
    * Start screen recording
    */
    void start() throws IOException, RemoteException, RuntimeException {
    public void start() throws IOException, RemoteException, RuntimeException {
        Log.d(TAG, "start recording");
        prepare();
        mMediaRecorder.start();
        mScreenRecordingStartTimeStore.markStartTime();
        mListener.onStarted();
        recordInternalAudio();
    }

    /**
     * End screen recording, throws an exception if stopping recording failed
     */
    void end(@StopReason int stopReason) throws IOException {
    public void end(@StopReason int stopReason) throws IOException {
        Closer closer = new Closer();

        // MediaRecorder might throw RuntimeException if stopped immediately after starting
@@ -353,7 +347,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
    /**
     * Store recorded video
     */
    protected SavedRecording save() throws IOException, IllegalStateException {
    public SavedRecording save() throws IOException, IllegalStateException {
        String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
                .format(new Date());

@@ -374,11 +368,12 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
                Log.d(TAG, "muxing recording");
                File file = File.createTempFile("temp", ".mp4",
                        mContext.getCacheDir());
                mMuxer = new ScreenRecordingMuxer(MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4,
                ScreenRecordingMuxer muxer = new ScreenRecordingMuxer(
                        MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4,
                        file.getAbsolutePath(),
                        mTempVideoFile.getAbsolutePath(),
                        mTempAudioFile.getAbsolutePath());
                mMuxer.mux();
                muxer.mux();
                mTempVideoFile.delete();
                mTempVideoFile = file;
            } catch (IOException e) {
@@ -415,7 +410,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
    /**
     * Release the resources without saving the data
     */
    protected void release() {
    public void release() {
        if (mTempVideoFile != null) {
            mTempVideoFile.delete();
        }
@@ -432,7 +427,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
        private Uri mUri;
        private Icon mThumbnailIcon;

        protected SavedRecording(Uri uri, File file, Size thumbnailSize) {
        public SavedRecording(Uri uri, File file, Size thumbnailSize) {
            mUri = uri;
            try {
                Bitmap thumbnailBitmap = ThumbnailUtils.createVideoThumbnail(
@@ -452,7 +447,13 @@ public class ScreenMediaRecorder extends MediaProjection.Callback {
        }
    }

    interface ScreenMediaRecorderListener {
    public interface ScreenMediaRecorderListener {

        /**
         * Called when the recording actually starts
         */
        void onStarted();

        /**
         * Called to indicate an info or a warning during recording.
         * See {@link MediaRecorder.OnInfoListener} for the full description.
+9 −3
Original line number Diff line number Diff line
@@ -16,11 +16,17 @@

package com.android.systemui.screenrecord.service;

import com.android.systemui.screenrecord.service.IScreenRecordingServiceCallback;
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;

/** Interface implemented by ScreenRecordingService */
/** Interface implemented by ScreenshotCrossProfileService */
interface IScreenRecordingService {

    void setCallback(IScreenRecordingServiceCallback callback);
    void setCallback(com.android.systemui.screenrecord.service.IScreenRecordingServiceCallback callback);
    void stopRecording(int reason);
    void startRecording(
        in MediaProjectionCaptureTarget captureTarget,
        int audioSource,
        int displayId,
        boolean shouldShowTaps
    );
}
 No newline at end of file
Loading