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

Commit 1bdf879d authored by Wei Jia's avatar Wei Jia
Browse files

DataSourceDesc: refactor to base class and subclasses

Test: cts
Bug: 112549021
Change-Id: Ic3ce737053a436b2ce9f82e90b12116a47dc4efd
parent e7be8115
Loading
Loading
Loading
Loading
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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;

import android.annotation.NonNull;

import com.android.internal.util.Preconditions;

/**
 * @hide
 * Structure for file data source descriptor.
 *
 * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)}
 * to set data source for playback.
 *
 * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
 *
 */
public class CallbackDataSourceDesc extends DataSourceDesc {
    private Media2DataSource mMedia2DataSource;

    private CallbackDataSourceDesc() {
    }

    /**
     * Return the Media2DataSource of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
     * @return the Media2DataSource of this data source
     */
    public Media2DataSource getMedia2DataSource() {
        return mMedia2DataSource;
    }

    /**
     * Builder class for {@link CallbackDataSourceDesc} objects.
     * <p> Here is an example where <code>Builder</code> is used to define the
     * {@link CallbackDataSourceDesc} to be used by a {@link MediaPlayer2} instance:
     *
     * <pre class="prettyprint">
     * CallbackDataSourceDesc newDSD = new CallbackDataSourceDesc.Builder()
     *         .setDataSource(media2DataSource)
     *         .setStartPosition(1000)
     *         .setEndPosition(15000)
     *         .build();
     * mediaplayer2.setDataSourceDesc(newDSD);
     * </pre>
     */
    public static class Builder extends BuilderBase<Builder> {
        private Media2DataSource mMedia2DataSource;

        /**
         * Constructs a new Builder with the defaults.
         */
        public Builder() {
            super();
        }

        /**
         * Constructs a new Builder from a given {@link CallbackDataSourceDesc} instance
         * @param dsd the {@link CallbackDataSourceDesc} object whose data will be reused
         * in the new Builder.
         */
        public Builder(CallbackDataSourceDesc dsd) {
            super(dsd);
            if (dsd == null) {
                return;  // use default
            }
            mMedia2DataSource = dsd.mMedia2DataSource;
        }

        /**
         * Combines all of the fields that have been set and return a new
         * {@link CallbackDataSourceDesc} object. <code>IllegalStateException</code> will be
         * thrown if there is conflict between fields.
         *
         * @return a new {@link CallbackDataSourceDesc} object
         */
        public @NonNull CallbackDataSourceDesc build() {
            CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
            super.build(dsd);
            dsd.mMedia2DataSource = mMedia2DataSource;

            return dsd;
        }

        /**
         * Sets the data source (Media2DataSource) to use.
         *
         * @param m2ds the Media2DataSource for the media to play
         * @return the same Builder instance.
         * @throws NullPointerException if m2ds is null.
         */
        public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) {
            Preconditions.checkNotNull(m2ds);
            mMedia2DataSource = m2ds;
            return this;
        }
    }
}
+39 −349
Original line number Diff line number Diff line
@@ -16,49 +16,21 @@

package android.media;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.util.Preconditions;

import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @hide
 * Structure for data source descriptor.
 * Base class of data source descriptor.
 *
 * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
 * to set data source for playback.
 *
 * <p>Users should use {@link Builder} to change {@link DataSourceDesc}.
 * <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
 *
 */
public final class DataSourceDesc {
    /* No data source has been set yet */
    public static final int TYPE_NONE     = 0;
    /* data source is type of MediaDataSource */
    public static final int TYPE_CALLBACK = 1;
    /* data source is type of FileDescriptor */
    public static final int TYPE_FD       = 2;
    /* data source is type of Uri */
    public static final int TYPE_URI      = 3;

public class DataSourceDesc {
    // intentionally less than long.MAX_VALUE
    public static final long LONG_MAX = 0x7ffffffffffffffL;

@@ -66,25 +38,13 @@ public final class DataSourceDesc {
    public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
    public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;


    private int mType = TYPE_NONE;

    private Media2DataSource mMedia2DataSource;

    private FileDescriptor mFD;
    private long mFDOffset = 0;
    private long mFDLength = LONG_MAX;

    private Uri mUri;
    private Map<String, String> mUriHeader;
    private List<HttpCookie> mUriCookies;
    private Context mUriContext;
    public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS;

    private String mMediaId;
    private long mStartPositionMs = 0;
    private long mEndPositionMs = LONG_MAX_TIME_MS;
    private long mEndPositionMs = POSITION_UNKNOWN;

    private DataSourceDesc() {
    DataSourceDesc() {
    }

    /**
@@ -105,194 +65,69 @@ public final class DataSourceDesc {

    /**
     * Return the position in milliseconds at which the playback will end.
     * -1 means ending at the end of source content.
     * {@link #POSITION_UNKNOWN} means ending at the end of source content.
     * @return the position in milliseconds at which the playback will end
     */
    public long getEndPosition() {
        return mEndPositionMs;
    }

    /**
     * Return the type of data source.
     * @return the type of data source
     */
    public int getType() {
        return mType;
    }

    /**
     * Return the Media2DataSource of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
     * @return the Media2DataSource of this data source
     */
    public Media2DataSource getMedia2DataSource() {
        return mMedia2DataSource;
    }

    /**
     * Return the FileDescriptor of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_FD}.
     * @return the FileDescriptor of this data source
     */
    public FileDescriptor getFileDescriptor() {
        return mFD;
    }

    /**
     * Return the offset associated with the FileDescriptor of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_FD} and it has
     * been set by the {@link Builder}.
     * @return the offset associated with the FileDescriptor of this data source
     */
    public long getFileDescriptorOffset() {
        return mFDOffset;
    }

    /**
     * Return the content length associated with the FileDescriptor of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_FD}.
     * -1 means same as the length of source content.
     * @return the content length associated with the FileDescriptor of this data source
     */
    public long getFileDescriptorLength() {
        return mFDLength;
    }

    /**
     * Return the Uri of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_URI}.
     * @return the Uri of this data source
     */
    public Uri getUri() {
        return mUri;
    }

    /**
     * Return the Uri headers of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_URI}.
     * @return the Uri headers of this data source
     */
    public Map<String, String> getUriHeaders() {
        if (mUriHeader == null) {
            return null;
        }
        return new HashMap<String, String>(mUriHeader);
    }

    /**
     * Return the Uri cookies of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_URI}.
     * @return the Uri cookies of this data source
     */
    public List<HttpCookie> getUriCookies() {
        if (mUriCookies == null) {
            return null;
        }
        return new ArrayList<HttpCookie>(mUriCookies);
    }

    /**
     * Return the Context used for resolving the Uri of this data source.
     * It's meaningful only when {@code getType} returns {@link #TYPE_URI}.
     * @return the Context used for resolving the Uri of this data source
     */
    public Context getUriContext() {
        return mUriContext;
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("DataSourceDesc{");
        sb.append("mMediaId=").append(mMediaId);
        sb.append(", mStartPositionMs=").append(mStartPositionMs);
        sb.append(", mEndPositionMs=").append(mEndPositionMs);
        sb.append('}');
        return sb.toString();
    }

    /**
     * Builder class for {@link DataSourceDesc} objects.
     * <p> Here is an example where <code>Builder</code> is used to define the
     * {@link DataSourceDesc} to be used by a {@link MediaPlayer2} instance:
     *
     * <pre class="prettyprint">
     * DataSourceDesc oldDSD = mediaplayer2.getDataSourceDesc();
     * DataSourceDesc newDSD = new DataSourceDesc.Builder(oldDSD)
     *         .setStartPosition(1000)
     *         .setEndPosition(15000)
     *         .build();
     * mediaplayer2.setDataSourceDesc(newDSD);
     * </pre>
     * Base class for Builders in the subclasses of {@link DataSourceDesc}.
     */
    public static class Builder {
        private int mType = TYPE_NONE;

        private Media2DataSource mMedia2DataSource;

        private FileDescriptor mFD;
        private long mFDOffset = 0;
        private long mFDLength = LONG_MAX;

        private Uri mUri;
        private Map<String, String> mUriHeader;
        private List<HttpCookie> mUriCookies;
        private Context mUriContext;

    protected static class BuilderBase<T extends BuilderBase> {
        private String mMediaId;
        private long mStartPositionMs = 0;
        private long mEndPositionMs = LONG_MAX_TIME_MS;
        private long mEndPositionMs = POSITION_UNKNOWN;

        /**
         * Constructs a new Builder with the defaults.
         * Constructs a new BuilderBase with the defaults.
         */
        public Builder() {
        BuilderBase() {
        }

        /**
         * Constructs a new Builder from a given {@link DataSourceDesc} instance
         * Constructs a new BuilderBase from a given {@link DataSourceDesc} instance
         * @param dsd the {@link DataSourceDesc} object whose data will be reused
         * in the new Builder.
         * in the new BuilderBase.
         */
        public Builder(DataSourceDesc dsd) {
            mType = dsd.mType;
            mMedia2DataSource = dsd.mMedia2DataSource;
            mFD = dsd.mFD;
            mFDOffset = dsd.mFDOffset;
            mFDLength = dsd.mFDLength;
            mUri = dsd.mUri;
            mUriHeader = dsd.mUriHeader;
            mUriCookies = dsd.mUriCookies;
            mUriContext = dsd.mUriContext;

        BuilderBase(DataSourceDesc dsd) {
            if (dsd == null) {
                return;
            }
            mMediaId = dsd.mMediaId;
            mStartPositionMs = dsd.mStartPositionMs;
            mEndPositionMs = dsd.mEndPositionMs;
        }

        /**
         * Combines all of the fields that have been set and return a new
         * {@link DataSourceDesc} object. <code>IllegalStateException</code> will be
         * thrown if there is conflict between fields.
         * Sets all fields that have been set in the {@link DataSourceDesc} object.
         * <code>IllegalStateException</code> will be thrown if there is conflict between fields.
         *
         * @return a new {@link DataSourceDesc} object
         * @param dsd an instance of subclass of {@link DataSourceDesc} whose data will be set
         * @return the same instance of subclass of {@link DataSourceDesc}
         */
        public DataSourceDesc build() {
            if (mType != TYPE_CALLBACK
                && mType != TYPE_FD
                && mType != TYPE_URI) {
                throw new IllegalStateException("Illegal type: " + mType);
            }
        void build(@NonNull DataSourceDesc dsd) {
            Preconditions.checkNotNull(dsd);

            if (mStartPositionMs > mEndPositionMs) {
                throw new IllegalStateException("Illegal start/end position: "
                    + mStartPositionMs + " : " + mEndPositionMs);
            }

            DataSourceDesc dsd = new DataSourceDesc();
            dsd.mType = mType;
            dsd.mMedia2DataSource = mMedia2DataSource;
            dsd.mFD = mFD;
            dsd.mFDOffset = mFDOffset;
            dsd.mFDLength = mFDLength;
            dsd.mUri = mUri;
            dsd.mUriHeader = mUriHeader;
            dsd.mUriCookies = mUriCookies;
            dsd.mUriContext = mUriContext;

            dsd.mMediaId = mMediaId;
            dsd.mStartPositionMs = mStartPositionMs;
            dsd.mEndPositionMs = mEndPositionMs;

            return dsd;
        }

        /**
@@ -301,9 +136,9 @@ public final class DataSourceDesc {
         * @param mediaId the media Id of this data source
         * @return the same Builder instance.
         */
        public Builder setMediaId(String mediaId) {
        public @NonNull T setMediaId(String mediaId) {
            mMediaId = mediaId;
            return this;
            return (T) this;
        }

        /**
@@ -314,12 +149,12 @@ public final class DataSourceDesc {
         * @return the same Builder instance.
         *
         */
        public Builder setStartPosition(long position) {
        public @NonNull T setStartPosition(long position) {
            if (position < 0) {
                position = 0;
            }
            mStartPositionMs = position;
            return this;
            return (T) this;
        }

        /**
@@ -329,157 +164,12 @@ public final class DataSourceDesc {
         * @param position the end position in milliseconds at which the playback will end
         * @return the same Builder instance.
         */
        public Builder setEndPosition(long position) {
        public @NonNull T setEndPosition(long position) {
            if (position < 0) {
                position = LONG_MAX_TIME_MS;
            }
            mEndPositionMs = position;
            return this;
        }

        /**
         * Sets the data source (Media2DataSource) to use.
         *
         * @param m2ds the Media2DataSource for the media you want to play
         * @return the same Builder instance.
         * @throws NullPointerException if m2ds is null.
         */
        public Builder setDataSource(Media2DataSource m2ds) {
            Preconditions.checkNotNull(m2ds);
            resetDataSource();
            mType = TYPE_CALLBACK;
            mMedia2DataSource = m2ds;
            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.
         *
         * @param fd the FileDescriptor for the file you want to play
         * @return the same Builder instance.
         * @throws NullPointerException if fd is null.
         */
        public Builder setDataSource(FileDescriptor fd) {
            Preconditions.checkNotNull(fd);
            resetDataSource();
            mType = TYPE_FD;
            mFD = fd;
            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.
         *
         * 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 you want 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.
         */
        public Builder setDataSource(FileDescriptor fd, long offset, long length) {
            Preconditions.checkNotNull(fd);
            if (offset < 0) {
                offset = 0;
            }
            if (length < 0) {
                length = LONG_MAX;
            }
            resetDataSource();
            mType = TYPE_FD;
            mFD = fd;
            mFDOffset = offset;
            mFDLength = length;
            return this;
        }

        /**
         * Sets the data source as a content Uri.
         *
         * @param context the Context to use when resolving the Uri
         * @param uri the Content URI of the data you want to play
         * @return the same Builder instance.
         * @throws NullPointerException if context or uri is null.
         */
        public Builder setDataSource(@NonNull Context context, @NonNull Uri uri) {
            Preconditions.checkNotNull(context, "context cannot be null");
            Preconditions.checkNotNull(uri, "uri cannot be null");
            resetDataSource();
            mType = TYPE_URI;
            mUri = uri;
            mUriContext = context;
            return this;
        }

        /**
         * Sets the data source as a content Uri.
         *
         * To provide cookies for the subsequent HTTP requests, you can install your own default
         * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you
         * can use this API to pass the cookies as a list of HttpCookie. If the app has not
         * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager
         * and populates its CookieStore with the provided cookies when this data source is passed
         * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler
         * is required to be of CookieManager type such that {@link MediaPlayer2} can update the
         * manager’s CookieStore.
         *
         *  <p><strong>Note</strong> that the cross domain redirection is allowed by default,
         * but that can be changed with key/value pairs through the headers parameter with
         * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
         * disallow or allow cross domain redirection.
         *
         * @param context the Context to use when resolving the Uri
         * @param uri the Content URI of the data you want to play
         * @param headers the headers to be sent together with the request for the data
         *                The headers must not include cookies. Instead, use the cookies param.
         * @param cookies the cookies to be sent together with the request
         * @return the same Builder instance.
         * @throws NullPointerException if context or uri is null.
         * @throws IllegalArgumentException if the cookie handler is not of CookieManager type
         *                                  when cookies are provided.
         */
        public Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
                @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
            Preconditions.checkNotNull(context, "context cannot be null");
            Preconditions.checkNotNull(uri);
            if (cookies != null) {
                CookieHandler cookieHandler = CookieHandler.getDefault();
                if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
                    throw new IllegalArgumentException(
                            "The cookie handler has to be of CookieManager type "
                            + "when cookies are provided.");
                }
            }

            resetDataSource();
            mType = TYPE_URI;
            mUri = uri;
            if (headers != null) {
                mUriHeader = new HashMap<String, String>(headers);
            }
            if (cookies != null) {
                mUriCookies = new ArrayList<HttpCookie>(cookies);
            }
            mUriContext = context;
            return this;
        }

        private void resetDataSource() {
            mType = TYPE_NONE;
            mMedia2DataSource = null;
            mFD = null;
            mFDOffset = 0;
            mFDLength = LONG_MAX;
            mUri = null;
            mUriHeader = null;
            mUriCookies = null;
            mUriContext = null;
            return (T) this;
        }
    }
}
+186 −0

File added.

Preview size limit exceeded, changes collapsed.

+28 −32
Original line number Diff line number Diff line
@@ -789,38 +789,34 @@ public class MediaPlayer2 implements AutoCloseable
            throws IOException {
        checkArgument(dsd != null, "the DataSourceDesc cannot be null");

        switch (dsd.getType()) {
            case DataSourceDesc.TYPE_CALLBACK:
        if (dsd instanceof CallbackDataSourceDesc) {
            CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd;
            handleDataSource(isCurrent,
                             srcId,
                                 dsd.getMedia2DataSource(),
                                 dsd.getStartPosition(),
                                 dsd.getEndPosition());
                break;

            case DataSourceDesc.TYPE_FD:
                             cbDSD.getMedia2DataSource(),
                             cbDSD.getStartPosition(),
                             cbDSD.getEndPosition());
        } else if (dsd instanceof FileDataSourceDesc) {
            FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
            handleDataSource(isCurrent,
                             srcId,
                                 dsd.getFileDescriptor(),
                                 dsd.getFileDescriptorOffset(),
                                 dsd.getFileDescriptorLength(),
                                 dsd.getStartPosition(),
                                 dsd.getEndPosition());
                break;

            case DataSourceDesc.TYPE_URI:
                             fileDSD.getFileDescriptor(),
                             fileDSD.getOffset(),
                             fileDSD.getLength(),
                             fileDSD.getStartPosition(),
                             fileDSD.getEndPosition());
        } else if (dsd instanceof UriDataSourceDesc) {
            UriDataSourceDesc uriDSD = (UriDataSourceDesc) dsd;
            handleDataSource(isCurrent,
                             srcId,
                                 dsd.getUriContext(),
                                 dsd.getUri(),
                                 dsd.getUriHeaders(),
                                 dsd.getUriCookies(),
                                 dsd.getStartPosition(),
                                 dsd.getEndPosition());
                break;

            default:
                break;
                             uriDSD.getContext(),
                             uriDSD.getUri(),
                             uriDSD.getHeaders(),
                             uriDSD.getCookies(),
                             uriDSD.getStartPosition(),
                             uriDSD.getEndPosition());
        } else {
            throw new IllegalArgumentException("Unsupported DataSourceDesc. " + dsd.toString());
        }
    }

+228 −0

File added.

Preview size limit exceeded, changes collapsed.