diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java index aca8dbed1a136..e29bd006c00a2 100644 --- a/media/java/android/media/FileDataSourceDesc.java +++ b/media/java/android/media/FileDataSourceDesc.java @@ -44,6 +44,8 @@ public class FileDataSourceDesc extends DataSourceDesc { private ParcelFileDescriptor mPFD; private long mOffset = 0; private long mLength = FD_LENGTH_UNKNOWN; + private int mCount = 0; + private boolean mClosed = false; private FileDataSourceDesc() { super(); @@ -55,23 +57,48 @@ public class FileDataSourceDesc extends DataSourceDesc { @Override void close() { super.close(); - closeFD(); + decCount(); } /** - * Releases the file descriptor held by this {@code FileDataSourceDesc} object. + * Decrements usage count by {@link MediaPlayer2}. + * If this is the last usage, also releases the file descriptor held by this + * {@code FileDataSourceDesc} object. */ - void closeFD() { + void decCount() { synchronized (this) { - if (mPFD != null) { - try { - mPFD.close(); - } catch (IOException e) { - Log.e(TAG, "failed to close pfd: " + e); - } - - mPFD = null; + --mCount; + if (mCount > 0) { + return; } + + try { + mPFD.close(); + mClosed = true; + } catch (IOException e) { + Log.e(TAG, "failed to close pfd: " + e); + } + } + } + + /** + * Increments usage count by {@link MediaPlayer2} if PFD has not been closed. + */ + void incCount() { + synchronized (this) { + if (!mClosed) { + ++mCount; + } + } + } + + /** + * Return the status of underline ParcelFileDescriptor + * @return true if underline ParcelFileDescriptor is closed, false otherwise. + */ + boolean isPFDClosed() { + synchronized (this) { + return mClosed; } } @@ -150,6 +177,16 @@ public class FileDataSourceDesc extends DataSourceDesc { * @return a new {@link FileDataSourceDesc} object */ public @NonNull FileDataSourceDesc build() { + if (mPFD == null) { + throw new IllegalStateException( + "underline ParcelFileDescriptor should not be null"); + } + try { + mPFD.getFd(); + } catch (IllegalStateException e) { + throw new IllegalStateException("ParcelFileDescriptor has been closed"); + } + FileDataSourceDesc dsd = new FileDataSourceDesc(); super.build(dsd); dsd.mPFD = mPFD; diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index b137ce2cda0fd..a3702d68cc491 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -696,7 +696,7 @@ public class MediaPlayer2 implements AutoCloseable return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) { @Override void process() throws IOException { - Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); + checkDataSourceDesc(dsd); int state = getState(); try { if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) { @@ -729,7 +729,7 @@ public class MediaPlayer2 implements AutoCloseable return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) { @Override void process() { - Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); + checkDataSourceDesc(dsd); synchronized (mSrcLock) { clearNextSourceInfos_l(); mNextSourceInfos.add(new SourceInfo(dsd)); @@ -755,15 +755,35 @@ public class MediaPlayer2 implements AutoCloseable if (dsds == null || dsds.size() == 0) { throw new IllegalArgumentException("data source list cannot be null or empty."); } + boolean hasError = false; + for (DataSourceDesc dsd : dsds) { + if (dsd != null) { + hasError = true; + continue; + } + if (dsd instanceof FileDataSourceDesc) { + FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd; + if (fdsd.isPFDClosed()) { + hasError = true; + continue; + } + + fdsd.incCount(); + } + } + if (hasError) { + for (DataSourceDesc dsd : dsds) { + if (dsd != null) { + dsd.close(); + } + } + throw new IllegalArgumentException("invalid data source list"); + } synchronized (mSrcLock) { clearNextSourceInfos_l(); 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."); - } + mNextSourceInfos.add(new SourceInfo(dsd)); } } prepareNextDataSource(); @@ -771,6 +791,20 @@ public class MediaPlayer2 implements AutoCloseable }); } + // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed. + private void checkDataSourceDesc(DataSourceDesc dsd) { + if (dsd != null) { + throw new IllegalArgumentException("dsd is expected to be non null"); + } + if (dsd instanceof FileDataSourceDesc) { + FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd; + if (fdsd.isPFDClosed()) { + throw new IllegalArgumentException("the underline FileDescriptor has been closed"); + } + fdsd.incCount(); + } + } + /** * Removes all data sources pending to be played. * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.