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

Commit da21c3d3 authored by Wei Jia's avatar Wei Jia
Browse files

MediaPlayer2: use ParcelFileDescriptor

Test: cts
Bug: 112549021
Change-Id: Ic1644e82558cc47fcba1152d8a1c7c10b6f7ef8c
parent 328d4426
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@ import android.annotation.NonNull;

/**
 * @hide
 * Structure for file data source descriptor.
 * Structure of data source descriptor for sources using callback.
 *
 * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)}
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
 * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
 * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
 * to set data source for playback.
 *
 * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
+17 −2
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import android.annotation.NonNull;
 * @hide
 * Base class of data source descriptor.
 *
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
 * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
 * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
 * to set data source for playback.
 *
 * <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
@@ -30,7 +32,7 @@ import android.annotation.NonNull;
 */
public class DataSourceDesc {
    // intentionally less than long.MAX_VALUE
    public static final long LONG_MAX = 0x7ffffffffffffffL;
    static final long LONG_MAX = 0x7ffffffffffffffL;

    // keep consistent with native code
    public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
@@ -45,6 +47,19 @@ public class DataSourceDesc {
    DataSourceDesc() {
    }

    /**
     * Releases the resources held by this {@code DataSourceDesc} object.
     */
    void close() {
    }

    // Have to declare protected for finalize() since it is protected
    // in the base class Object.
    @Override
    protected void finalize() throws Throwable {
        close();
    }

    /**
     * Return the media Id of data source.
     * @return the media Id of data source
+74 −33
Original line number Diff line number Diff line
@@ -17,20 +17,26 @@
package android.media;

import android.annotation.NonNull;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.IOException;

/**
 * @hide
 * Structure for data source descriptor.
 * Structure of data source descriptor for sources using file descriptor.
 *
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
 * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
 * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
 * to set data source for playback.
 *
 * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
 *
 */
public class FileDataSourceDesc extends DataSourceDesc {
    private static final String TAG = "FileDataSourceDesc";

    /**
     * Used when the length of file descriptor is unknown.
     *
@@ -38,34 +44,61 @@ public class FileDataSourceDesc extends DataSourceDesc {
     */
    public static final long FD_LENGTH_UNKNOWN = LONG_MAX;

    private FileDescriptor mFD;
    private ParcelFileDescriptor mPFD;
    private long mOffset = 0;
    private long mLength = FD_LENGTH_UNKNOWN;

    private FileDataSourceDesc() {
        super();
    }

    /**
     * Releases the resources held by this {@code FileDataSourceDesc} object.
     */
    @Override
    void close() {
        super.close();
        closeFD();
    }

    /**
     * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
     */
    void closeFD() {
        synchronized (this) {
            if (mPFD != null) {
                try {
                    mPFD.close();
                } catch (IOException e) {
                    Log.e(TAG, "failed to close pfd: " + e);
                }

                mPFD = null;
            }
        }
    }

    /**
     * Return the FileDescriptor of this data source.
     * @return the FileDescriptor of this data source
     * Return the ParcelFileDescriptor of this data source.
     * @return the ParcelFileDescriptor of this data source
     */
    public FileDescriptor getFileDescriptor() {
        return mFD;
    public ParcelFileDescriptor getParcelFileDescriptor() {
        return mPFD;
    }

    /**
     * Return the offset associated with the FileDescriptor of this data source.
     * Return the offset associated with the ParcelFileDescriptor of this data source.
     * It's meaningful only when it has been set by the {@link Builder}.
     * @return the offset associated with the FileDescriptor of this data source
     * @return the offset associated with the ParcelFileDescriptor of this data source
     */
    public long getOffset() {
        return mOffset;
    }

    /**
     * Return the content length associated with the FileDescriptor of this data source.
     * Return the content length associated with the ParcelFileDescriptor of this data source.
     * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
     * @return the content length associated with the FileDescriptor of this data source
     * @return the content length associated with the ParcelFileDescriptor of this data source
     */
    public long getLength() {
        return mLength;
@@ -78,7 +111,7 @@ public class FileDataSourceDesc extends DataSourceDesc {
     *
     * <pre class="prettyprint">
     * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder()
     *         .setDataSource(fd, 0, srcLength)
     *         .setDataSource(pfd, 0, srcLength)
     *         .setStartPosition(1000)
     *         .setEndPosition(15000)
     *         .build();
@@ -86,7 +119,7 @@ public class FileDataSourceDesc extends DataSourceDesc {
     * </pre>
     */
    public static class Builder extends BuilderBase<Builder> {
        private FileDescriptor mFD;
        private ParcelFileDescriptor mPFD;
        private long mOffset = 0;
        private long mLength = FD_LENGTH_UNKNOWN;

@@ -107,7 +140,7 @@ public class FileDataSourceDesc extends DataSourceDesc {
            if (dsd == null) {
                return;  // use default
            }
            mFD = dsd.mFD;
            mPFD = dsd.mPFD;
            mOffset = dsd.mOffset;
            mLength = dsd.mLength;
        }
@@ -122,7 +155,7 @@ public class FileDataSourceDesc extends DataSourceDesc {
        public @NonNull FileDataSourceDesc build() {
            FileDataSourceDesc dsd = new FileDataSourceDesc();
            super.build(dsd);
            dsd.mFD = mFD;
            dsd.mPFD = mPFD;
            dsd.mOffset = mOffset;
            dsd.mLength = mLength;

@@ -130,38 +163,46 @@ public class FileDataSourceDesc extends DataSourceDesc {
        }

        /**
         * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
         * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
         * to close the file descriptor after the source has been used.
         * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
         * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
         * created by this builder is passed to {@link MediaPlayer2} via
         * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
         * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
         * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
         * close the ParcelFileDescriptor.
         *
         * @param fd the FileDescriptor for the file to play
         * @param pfd the ParcelFileDescriptor for the file to play
         * @return the same Builder instance.
         * @throws NullPointerException if fd is null.
         * @throws NullPointerException if pfd is null.
         */
        public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) {
            Media2Utils.checkArgument(fd != null, "fd cannot be null.");
        public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
            Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
            resetDataSource();
            mFD = fd;
            mPFD = pfd;
            return this;
        }

        /**
         * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
         * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
         * to close the file descriptor after the source has been used.
         * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
         * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
         * created by this builder is passed to {@link MediaPlayer2} via
         * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
         * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
         * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
         * close the ParcelFileDescriptor.
         *
         * Any negative number for offset is treated as 0.
         * Any negative number for length is treated as maximum length of the data source.
         *
         * @param fd the FileDescriptor for the file to play
         * @param pfd the ParcelFileDescriptor for the file to play
         * @param offset the offset into the file where the data to be played starts, in bytes
         * @param length the length in bytes of the data to be played
         * @return the same Builder instance.
         * @throws NullPointerException if fd is null.
         * @throws NullPointerException if pfd is null.
         */
        public @NonNull Builder setDataSource(
                @NonNull FileDescriptor fd, long offset, long length) {
            Media2Utils.checkArgument(fd != null, "fd cannot be null.");
                @NonNull ParcelFileDescriptor pfd, long offset, long length) {
            Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
            if (offset < 0) {
                offset = 0;
            }
@@ -169,14 +210,14 @@ public class FileDataSourceDesc extends DataSourceDesc {
                length = FD_LENGTH_UNKNOWN;
            }
            resetDataSource();
            mFD = fd;
            mPFD = pfd;
            mOffset = offset;
            mLength = length;
            return this;
        }

        private void resetDataSource() {
            mFD = null;
            mPFD = null;
            mOffset = 0;
            mLength = FD_LENGTH_UNKNOWN;
        }
+75 −28
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.util.Log;
@@ -412,6 +413,9 @@ public class MediaPlayer2 implements AutoCloseable
            mHandlerThread = null;
        }

        setCurrentSourceInfo(null);
        clearNextSourceInfos();

        // Modular DRM clean up
        mOnDrmConfigHelper = null;
        synchronized (mDrmEventCbLock) {
@@ -457,10 +461,8 @@ public class MediaPlayer2 implements AutoCloseable
        synchronized (mDrmEventCbLock) {
            mDrmEventCallbackRecords.clear();
        }
        synchronized (mSrcLock) {
            mCurrentSourceInfo = null;
            mNextSourceInfos.clear();
        }
        setCurrentSourceInfo(null);
        clearNextSourceInfos();

        synchronized (mTaskLock) {
            mPendingTasks.clear();
@@ -685,6 +687,8 @@ public class MediaPlayer2 implements AutoCloseable

    /**
     * Sets the data source as described by a DataSourceDesc.
     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
     * in the {@link FileDataSourceDesc} will be closed by the player.
     *
     * @param dsd the descriptor of data source you want to play
     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -696,14 +700,18 @@ public class MediaPlayer2 implements AutoCloseable
            void process() throws IOException {
                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                int state = getState();
                try {
                    if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
                        throw new IllegalStateException("called in wrong state " + state);
                    }

                    synchronized (mSrcLock) {
                    mCurrentSourceInfo = new SourceInfo(dsd);
                        setCurrentSourceInfo(new SourceInfo(dsd));
                        handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
                    }
                } finally {
                    dsd.close();
                }
            }
        });
    }
@@ -711,6 +719,8 @@ public class MediaPlayer2 implements AutoCloseable
    /**
     * Sets a single data source as described by a DataSourceDesc which will be played
     * after current data source is finished.
     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
     * in the {@link FileDataSourceDesc} will be closed by the player.
     *
     * @param dsd the descriptor of data source you want to play after current one
     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -722,7 +732,7 @@ public class MediaPlayer2 implements AutoCloseable
            void process() {
                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                synchronized (mSrcLock) {
                    mNextSourceInfos.clear();
                    clearNextSourceInfos();
                    mNextSourceInfos.add(new SourceInfo(dsd));
                }
                prepareNextDataSource();
@@ -732,6 +742,8 @@ public class MediaPlayer2 implements AutoCloseable

    /**
     * Sets a list of data sources to be played sequentially after current data source is done.
     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
     * in the {@link FileDataSourceDesc} will be closed by the player.
     *
     * @param dsds the list of data sources you want to play after current one
     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -744,17 +756,15 @@ public class MediaPlayer2 implements AutoCloseable
                if (dsds == null || dsds.size() == 0) {
                    throw new IllegalArgumentException("data source list cannot be null or empty.");
                }
                for (DataSourceDesc dsd : dsds) {
                    if (dsd == null) {
                        throw new IllegalArgumentException(
                                "DataSourceDesc in the source list cannot be null.");
                    }
                }

                synchronized (mSrcLock) {
                    mNextSourceInfos.clear();
                    clearNextSourceInfos();
                    for (DataSourceDesc dsd : dsds) {
                        if (dsd != null) {
                            mNextSourceInfos.add(new SourceInfo(dsd));
                        } else {
                            Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
                        }
                    }
                }
                prepareNextDataSource();
@@ -771,7 +781,7 @@ public class MediaPlayer2 implements AutoCloseable
        return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
            @Override
            void process() {
                mNextSourceInfos.clear();
                clearNextSourceInfos();
            }
        });
    }
@@ -802,7 +812,7 @@ public class MediaPlayer2 implements AutoCloseable
            FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
            handleDataSource(isCurrent,
                             srcId,
                             fileDSD.getFileDescriptor(),
                             fileDSD.getParcelFileDescriptor(),
                             fileDSD.getOffset(),
                             fileDSD.getLength(),
                             fileDSD.getStartPosition(),
@@ -886,7 +896,7 @@ public class MediaPlayer2 implements AutoCloseable
            if (afd.getDeclaredLength() < 0) {
                handleDataSource(isCurrent,
                        srcId,
                        afd.getFileDescriptor(),
                        ParcelFileDescriptor.dup(afd.getFileDescriptor()),
                        0,
                        DataSourceDesc.LONG_MAX,
                        startPos,
@@ -894,7 +904,7 @@ public class MediaPlayer2 implements AutoCloseable
            } else {
                handleDataSource(isCurrent,
                        srcId,
                        afd.getFileDescriptor(),
                        ParcelFileDescriptor.dup(afd.getFileDescriptor()),
                        afd.getStartOffset(),
                        afd.getDeclaredLength(),
                        startPos,
@@ -960,7 +970,8 @@ public class MediaPlayer2 implements AutoCloseable
        if (file.exists()) {
            FileInputStream is = new FileInputStream(file);
            FileDescriptor fd = is.getFD();
            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
            handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd),
                    0, DataSourceDesc.LONG_MAX, startPos, endPos);
            is.close();
        } else {
            throw new IOException("handleDataSource failed.");
@@ -984,9 +995,10 @@ public class MediaPlayer2 implements AutoCloseable
     */
    private void handleDataSource(
            boolean isCurrent, long srcId,
            FileDescriptor fd, long offset, long length,
            ParcelFileDescriptor pfd, long offset, long length,
            long startPos, long endPos) throws IOException {
        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
        nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length,
                startPos, endPos);
    }

    private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
@@ -1037,7 +1049,10 @@ public class MediaPlayer2 implements AutoCloseable
                        MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
                mTaskHandler.handleMessage(msg, nextSource.mId);

                mNextSourceInfos.poll();
                SourceInfo nextSourceInfo = mNextSourceInfos.poll();
                if (nextSource != null) {
                    nextSourceInfo.close();
                }
                return prepareNextDataSource();
            }
        }
@@ -1058,7 +1073,7 @@ public class MediaPlayer2 implements AutoCloseable
                SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
                    // Switch to next source only when it has been prepared.
                    mCurrentSourceInfo = mNextSourceInfos.poll();
                    setCurrentSourceInfo(mNextSourceInfos.poll());

                    long srcId = mCurrentSourceInfo.mId;
                    try {
@@ -4490,6 +4505,7 @@ public class MediaPlayer2 implements AutoCloseable
        final DataSourceDesc mDSD;
        final long mId = mSrcIdGenerator.getAndIncrement();
        AtomicInteger mBufferedPercentage = new AtomicInteger(0);
        boolean mClosed = false;

        // m*AsNextSource (below) only applies to pending data sources in the playlist;
        // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
@@ -4501,6 +4517,17 @@ public class MediaPlayer2 implements AutoCloseable
            this.mDSD = dsd;
        }

        void close() {
            synchronized (this) {
                if (!mClosed) {
                    if (mDSD != null) {
                        mDSD.close();
                    }
                    mClosed = true;
                }
            }
        }

        @Override
        public String toString() {
            return String.format("%s(%d)", SourceInfo.class.getName(), mId);
@@ -4531,6 +4558,26 @@ public class MediaPlayer2 implements AutoCloseable
        return nextSourceInfo != null && nextSourceInfo.mId == srcId;
    }

    private void setCurrentSourceInfo(SourceInfo newSourceInfo) {
        synchronized (mSrcLock) {
            if (mCurrentSourceInfo != null) {
                mCurrentSourceInfo.close();
            }
            mCurrentSourceInfo = newSourceInfo;
        }
    }

    private void clearNextSourceInfos() {
        synchronized (mSrcLock) {
            for (SourceInfo sourceInfo : mNextSourceInfos) {
                if (sourceInfo != null) {
                    sourceInfo.close();
                }
            }
            mNextSourceInfos.clear();
        }
    }

    public static final class MetricsConstants {
        private MetricsConstants() {}

+4 −2
Original line number Diff line number Diff line
@@ -31,9 +31,11 @@ import java.util.Map;

/**
 * @hide
 * Structure for data source descriptor.
 * Structure of data source descriptor for sources using URI.
 *
 * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)}
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
 * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
 * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
 * to set data source for playback.
 *
 * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.