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

Commit 516eebe5 authored by hkuang's avatar hkuang Committed by Hangyu Kuang
Browse files

transcoding: Add retry api.

The retry api is used internally to retry the pending jobs
upon service die and also used by client to retry the failed job.

CTS-Coverage-Bug: 168805960

Bug: 161469320
Test: Unit test

Change-Id: I5c3f4d1d0e7a21cd2f802fdffe0e5b0566833328
parent 0667e270
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -27325,6 +27325,12 @@ package android.media {
    field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
  }
  public class MediaTranscodingException extends java.lang.Exception {
  }
  public static final class MediaTranscodingException.ServiceNotAvailableException extends android.media.MediaTranscodingException {
  }
  public interface MicrophoneDirection {
    method public boolean setPreferredMicrophoneDirection(int);
    method public boolean setPreferredMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
+1 −2
Original line number Diff line number Diff line
@@ -4363,7 +4363,7 @@ package android.media {
  }
  public final class MediaTranscodeManager {
    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
    field public static final int PRIORITY_REALTIME = 1; // 0x1
    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
  }
@@ -4378,7 +4378,6 @@ package android.media {
    method @IntRange(from=0, to=100) public int getProgress();
    method public int getResult();
    method public int getStatus();
    method public boolean retry();
    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
    method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
    field public static final int RESULT_CANCELED = 4; // 0x4
+1 −2
Original line number Diff line number Diff line
@@ -1817,7 +1817,7 @@ package android.media {
  }

  public final class MediaTranscodeManager {
    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
    field public static final int PRIORITY_REALTIME = 1; // 0x1
    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
  }
@@ -1832,7 +1832,6 @@ package android.media {
    method @IntRange(from=0, to=100) public int getProgress();
    method public int getResult();
    method public int getStatus();
    method public boolean retry();
    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
    method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
    field public static final int RESULT_CANCELED = 4; // 0x4
+100 −17
Original line number Diff line number Diff line
@@ -274,7 +274,7 @@ public final class MediaTranscodeManager {

    private static IMediaTranscodingService getService(boolean retry) {
        int retryCount = !retry ? 1 :  CONNECT_SERVICE_RETRY_COUNT;
        Log.i(TAG, "get service with rety " + retryCount);
        Log.i(TAG, "get service with retry " + retryCount);
        for (int count = 1;  count <= retryCount; count++) {
            Log.d(TAG, "Trying to connect to service. Try count: " + count);
            IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
@@ -356,7 +356,15 @@ public final class MediaTranscodeManager {
            for (TranscodingJob job : retryJobs) {
                // Notify the job failure if we fails to connect to the service or fail
                // to retry the job.
                if (!haveTranscodingClient || !job.retry()) {
                if (!haveTranscodingClient) {
                    // TODO(hkuang): Return correct error code to the client.
                    handleTranscodingFailed(job.getJobId(), 0 /*unused */);
                }

                try {
                    // Do not set hasRetried for retry initiated by MediaTranscodeManager.
                    job.retryInternal(false /*setHasRetried*/);
                } catch (Exception re) {
                    // TODO(hkuang): Return correct error code to the client.
                    handleTranscodingFailed(job.getJobId(), 0 /*unused */);
                }
@@ -1010,9 +1018,9 @@ public final class MediaTranscodeManager {
                    @IntRange(from = 0, to = 100) int progress);
        }

        private final ITranscodingClient mJobOwner;
        private final Executor mListenerExecutor;
        private final OnTranscodingFinishedListener mListener;
        private final MediaTranscodeManager mManager;
        private Executor mListenerExecutor;
        private OnTranscodingFinishedListener mListener;
        private int mJobId = -1;
        // Lock for internal state.
        private final Object mLock = new Object();
@@ -1028,20 +1036,26 @@ public final class MediaTranscodeManager {
        private @Status int mStatus = STATUS_PENDING;
        @GuardedBy("mLock")
        private @Result int mResult = RESULT_NONE;
        @GuardedBy("mLock")
        private boolean mHasRetried = false;
        // The original request that associated with this job.
        private final TranscodingRequest mRequest;

        private TranscodingJob(
                @NonNull ITranscodingClient jobOwner,
                @NonNull MediaTranscodeManager manager,
                @NonNull TranscodingRequest request,
                @NonNull TranscodingJobParcel parcel,
                @NonNull @CallbackExecutor Executor executor,
                @NonNull OnTranscodingFinishedListener listener) {
            Objects.requireNonNull(jobOwner, "JobOwner must not be null");
            Objects.requireNonNull(parcel, "TranscodingJobParcel must not be null");
            Objects.requireNonNull(manager, "manager must not be null");
            Objects.requireNonNull(parcel, "parcel must not be null");
            Objects.requireNonNull(executor, "listenerExecutor must not be null");
            Objects.requireNonNull(listener, "listener must not be null");
            mJobOwner = jobOwner;
            mManager = manager;
            mJobId = parcel.jobId;
            mListenerExecutor = executor;
            mListener = listener;
            mRequest = request;
        }

        /**
@@ -1085,14 +1099,61 @@ public final class MediaTranscodeManager {

        /**
         * Resubmit the transcoding job to the service.
         * Note that only the job that fails or gets cancelled could be retried and each job could
         * be retried only once. After that, Client need to enqueue a new request if they want to
         * try again.
         *
         * @return true if successfully resubmit the job to the service. False otherwise.
         * @throws MediaTranscodingException.ServiceNotAvailableException if the service
         *         is temporarily unavailable due to internal service rebooting. Client could retry
         *         again after receiving this exception.
         * @throws UnsupportedOperationException if the retry could not be fulfilled.
         * @hide
         */
        public boolean retry() {
        public void retry() throws MediaTranscodingException.ServiceNotAvailableException {
            retryInternal(true /*setHasRetried*/);
        }

        // TODO(hkuang): Add more test for it.
        private void retryInternal(boolean setHasRetried)
                throws MediaTranscodingException.ServiceNotAvailableException {
            synchronized (mLock) {
                // TODO(hkuang): Implement this.
                if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) {
                    throw new UnsupportedOperationException(
                            "Failed to retry as job is in processing");
                }

                if (mHasRetried) {
                    throw new UnsupportedOperationException("Job has been retried already");
                }

                // Get the client interface.
                ITranscodingClient client = mManager.getTranscodingClient();
                if (client == null) {
                    throw new MediaTranscodingException.ServiceNotAvailableException(
                            "Service rebooting. Try again later");
                }

                synchronized (mManager.mPendingTranscodingJobs) {
                    try {
                        // Submits the request to MediaTranscoding service.
                        TranscodingJobParcel jobParcel = new TranscodingJobParcel();
                        if (!client.submitRequest(mRequest.writeToParcel(), jobParcel)) {
                            mHasRetried = true;
                            throw new UnsupportedOperationException("Failed to enqueue request");
                        }

                        // Replace the old job id wit the new one.
                        mJobId = jobParcel.jobId;
                        // Adds the new job back into pending jobs.
                        mManager.mPendingTranscodingJobs.put(mJobId, this);
                    } catch (RemoteException re) {
                        throw new MediaTranscodingException.ServiceNotAvailableException(
                                "Failed to resubmit request to Transcoding service");
                    }
                    mStatus = STATUS_PENDING;
                    mHasRetried = setHasRetried ? true : false;
                }
            }
            return true;
        }

        /**
@@ -1105,7 +1166,11 @@ public final class MediaTranscodeManager {
                // Check if the job is finished already.
                if (mStatus != STATUS_FINISHED) {
                    try {
                        mJobOwner.cancelJob(mJobId);
                        ITranscodingClient client = mManager.getTranscodingClient();
                        // The client may be gone.
                        if (client != null) {
                            client.cancelJob(mJobId);
                        }
                    } catch (RemoteException re) {
                        //TODO(hkuang): Find out what to do if failing to cancel the job.
                        Log.e(TAG, "Failed to cancel the job due to exception:  " + re);
@@ -1173,6 +1238,12 @@ public final class MediaTranscodeManager {
        }
    }

    private ITranscodingClient getTranscodingClient() {
        synchronized (mLock) {
            return mTranscodingClient;
        }
    }

    /**
     * Enqueues a TranscodingRequest for execution.
     * <p> Upon successfully accepting the request, MediaTranscodeManager will return a
@@ -1185,13 +1256,17 @@ public final class MediaTranscodeManager {
     * @return A TranscodingJob for this operation.
     * @throws FileNotFoundException if the source Uri or destination Uri could not be opened.
     * @throws UnsupportedOperationException if the request could not be fulfilled.
     * @throws MediaTranscodingException.ServiceNotAvailableException if the service
     *         is temporarily unavailable due to internal service rebooting. Client could retry
     *         again after receiving this exception.
     */
    @NonNull
    public TranscodingJob enqueueRequest(
            @NonNull TranscodingRequest transcodingRequest,
            @NonNull @CallbackExecutor Executor listenerExecutor,
            @NonNull OnTranscodingFinishedListener listener)
            throws FileNotFoundException {
            throws FileNotFoundException,
            MediaTranscodingException.ServiceNotAvailableException {
        Log.i(TAG, "enqueueRequest called.");
        Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
        Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
@@ -1208,7 +1283,14 @@ public final class MediaTranscodeManager {
            synchronized (mPendingTranscodingJobs) {
                synchronized (mLock) {
                    if (mTranscodingClient == null) {
                        // TODO(hkuang): Handle the case if client is temporarily unavailable.
                        // Try to register with the service again.
                        IMediaTranscodingService service = getService(false /*retry*/);
                        mTranscodingClient = registerClient(service);
                        // If still fails, throws an exception to tell client to try later.
                        if (mTranscodingClient == null) {
                            throw new MediaTranscodingException.ServiceNotAvailableException(
                                    "Service rebooting. Try again later");
                        }
                    }

                    if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
@@ -1218,7 +1300,8 @@ public final class MediaTranscodeManager {

                // Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
                // tracking.
                TranscodingJob job = new TranscodingJob(mTranscodingClient, jobParcel,
                TranscodingJob job = new TranscodingJob(this, transcodingRequest,
                        jobParcel,
                        listenerExecutor,
                        listener);

+38 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.media;

/**
 * Base class for MediaTranscoding exceptions
 */
public class MediaTranscodingException extends Exception {
    private MediaTranscodingException(String detailMessage) {
        super(detailMessage);
    }

    /**
     * Exception thrown when the service is rebooting and MediaTranscodeManager is temporarily
     * unavailable for accepting new request. It's likely that retrying will be successful.
     */
    public static final class ServiceNotAvailableException extends
            MediaTranscodingException {
        /** @hide */
        public ServiceNotAvailableException(String detailMessage) {
            super(detailMessage);
        }
    }
}
Loading