diff options
Diffstat (limited to 'media/java')
-rw-r--r-- | media/java/android/media/IRingtonePlayer.aidl | 3 | ||||
-rw-r--r-- | media/java/android/media/MediaPlayer.java | 6 | ||||
-rw-r--r-- | media/java/android/media/MediaSync.java | 40 | ||||
-rw-r--r-- | media/java/android/media/Ringtone.java | 48 | ||||
-rw-r--r-- | media/java/android/media/RingtoneManager.java | 19 | ||||
-rw-r--r-- | media/java/android/media/tv/TvInputInfo.java | 3 | ||||
-rw-r--r-- | media/java/android/media/tv/TvInputService.java | 15 |
7 files changed, 100 insertions, 34 deletions
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl index aa5fde3..8091421 100644 --- a/media/java/android/media/IRingtonePlayer.aidl +++ b/media/java/android/media/IRingtonePlayer.aidl @@ -33,4 +33,7 @@ interface IRingtonePlayer { /** Used for Notification sound playback. */ void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa); void stopAsync(); + + /** Return the title of the media. */ + String getTitle(in Uri uri); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 6dd855d..a4d3485 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1017,16 +1017,14 @@ public class MediaPlayer implements SubtitleController.Listener setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength()); } return; - } catch (SecurityException ex) { - } catch (IOException ex) { + } catch (SecurityException | IOException ex) { + Log.w(TAG, "Couldn't open file on client side; trying server side: " + ex); } finally { if (fd != null) { fd.close(); } } - Log.d(TAG, "Couldn't open file on client side, trying server side"); - setDataSource(uri.toString(), headers); } diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java index b07931d..b37e02c 100644 --- a/media/java/android/media/MediaSync.java +++ b/media/java/android/media/MediaSync.java @@ -24,6 +24,7 @@ import android.media.PlaybackParams; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.util.Log; import android.view.Surface; import java.lang.annotation.Retention; @@ -82,7 +83,7 @@ import java.util.List; * codec.releaseOutputBuffer(bufferId, 1000 * info.presentationTime); * } else { * ByteBuffer audioByteBuffer = codec.getOutputBuffer(bufferId); - * sync.queueByteBuffer(audioByteBuffer, bufferId, info.size, info.presentationTime); + * sync.queueAudio(audioByteBuffer, bufferId, info.presentationTime); * } * // ... * } @@ -427,6 +428,11 @@ public final class MediaSync { /** * Flushes all buffers from the sync object. * <p> + * All pending unprocessed audio and video buffers are discarded. If an audio track was + * configured, it is flushed and stopped. If a video output surface was configured, the + * last frame queued to it is left on the frame. Queue a blank video frame to clear the + * surface, + * <p> * No callbacks are received for the flushed buffers. * * @throws IllegalStateException if the internal player engine has not been @@ -437,10 +443,19 @@ public final class MediaSync { mAudioBuffers.clear(); mCallbackHandler.removeCallbacksAndMessages(null); } - // TODO implement this for surface buffers. + if (mAudioTrack != null) { + mAudioTrack.pause(); + mAudioTrack.flush(); + // Call stop() to signal to the AudioSink to completely fill the + // internal buffer before resuming playback. + mAudioTrack.stop(); + } + native_flush(); } - /** + private native final void native_flush(); + + /** * Get current playback position. * <p> * The MediaTimestamp represents how the media time correlates to the system time in @@ -478,6 +493,7 @@ public final class MediaSync { /** * Queues the audio data asynchronously for playback (AudioTrack must be in streaming mode). + * If the audio track was flushed as a result of {@link #flush}, it will be restarted. * @param audioData the buffer that holds the data to play. This buffer will be returned * to the client via registered callback. * @param bufferId an integer used to identify audioData. It will be returned to @@ -519,6 +535,14 @@ public final class MediaSync { AudioBuffer audioBuffer = mAudioBuffers.get(0); int size = audioBuffer.mByteBuffer.remaining(); + // restart audio track after flush + if (size > 0 && mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) { + try { + mAudioTrack.play(); + } catch (IllegalStateException e) { + Log.w(TAG, "could not start audio track"); + } + } int sizeWritten = mAudioTrack.write( audioBuffer.mByteBuffer, size, @@ -558,17 +582,19 @@ public final class MediaSync { final MediaSync sync = this; mCallbackHandler.post(new Runnable() { public void run() { + Callback callback; synchronized(mCallbackLock) { + callback = mCallback; if (mCallbackHandler == null || mCallbackHandler.getLooper().getThread() != Thread.currentThread()) { // callback handler has been changed. return; } - if (mCallback != null) { - mCallback.onAudioBufferConsumed(sync, audioBuffer.mByteBuffer, - audioBuffer.mBufferIndex); - } + } + if (callback != null) { + callback.onAudioBufferConsumed(sync, audioBuffer.mByteBuffer, + audioBuffer.mBufferIndex); } } }); diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index faeebe6..9e9d602 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -27,6 +27,7 @@ import android.os.Binder; import android.os.RemoteException; import android.provider.MediaStore; import android.provider.Settings; +import android.provider.MediaStore.MediaColumns; import android.util.Log; import java.io.IOException; @@ -50,6 +51,8 @@ public class Ringtone { MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.TITLE }; + /** Selection that limits query results to just audio files */ + private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%'"; // keep references on active Ringtones until stopped or completion listener called. private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>(); @@ -193,11 +196,14 @@ public class Ringtone { */ public String getTitle(Context context) { if (mTitle != null) return mTitle; - return mTitle = getTitle(context, mUri, true); + return mTitle = getTitle(context, mUri, true /*followSettingsUri*/, mAllowRemote); } - private static String getTitle(Context context, Uri uri, boolean followSettingsUri) { - Cursor cursor = null; + /** + * @hide + */ + public static String getTitle( + Context context, Uri uri, boolean followSettingsUri, boolean allowRemote) { ContentResolver res = context.getContentResolver(); String title = null; @@ -209,31 +215,45 @@ public class Ringtone { if (followSettingsUri) { Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.getDefaultType(uri)); - String actualTitle = getTitle(context, actualUri, false); + String actualTitle = getTitle( + context, actualUri, false /*followSettingsUri*/, allowRemote); title = context .getString(com.android.internal.R.string.ringtone_default_with_actual, actualTitle); } } else { + Cursor cursor = null; try { if (MediaStore.AUTHORITY.equals(authority)) { - cursor = res.query(uri, MEDIA_COLUMNS, null, null, null); + final String mediaSelection = allowRemote ? null : MEDIA_SELECTION; + cursor = res.query(uri, MEDIA_COLUMNS, mediaSelection, null, null); + if (cursor != null && cursor.getCount() == 1) { + cursor.moveToFirst(); + return cursor.getString(2); + } + // missing cursor is handled below } } catch (SecurityException e) { - // missing cursor is handled below - } - - try { - if (cursor != null && cursor.getCount() == 1) { - cursor.moveToFirst(); - return cursor.getString(2); - } else { - title = uri.getLastPathSegment(); + IRingtonePlayer mRemotePlayer = null; + if (allowRemote) { + AudioManager audioManager = + (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mRemotePlayer = audioManager.getRingtonePlayer(); + } + if (mRemotePlayer != null) { + try { + title = mRemotePlayer.getTitle(uri); + } catch (RemoteException re) { + } } } finally { if (cursor != null) { cursor.close(); } + cursor = null; + } + if (title == null) { + title = uri.getLastPathSegment(); } } } diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index a1b8a3b..025029e 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -23,9 +23,11 @@ import android.annotation.SdkConstant.SdkConstantType; import android.app.Activity; import android.content.ContentUris; import android.content.Context; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Environment; +import android.os.Process; import android.provider.MediaStore; import android.provider.Settings; import android.provider.Settings.System; @@ -359,7 +361,10 @@ public class RingtoneManager { * If {@link RingtoneManager#RingtoneManager(Activity)} was not used, the * caller should manage the returned cursor through its activity's life * cycle to prevent leaking the cursor. - * + * <p> + * Note that the list of ringtones available will differ depending on whether the caller + * has the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission. + * * @return A {@link Cursor} of all the ringtones available. * @see #ID_COLUMN_INDEX * @see #TITLE_COLUMN_INDEX @@ -455,8 +460,10 @@ public class RingtoneManager { /** * Returns a valid ringtone URI. No guarantees on which it returns. If it - * cannot find one, returns null. - * + * cannot find one, returns null. If it can only find one on external storage and the caller + * doesn't have the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission, + * returns null. + * * @param context The context to use for querying. * @return A ringtone URI, or null if one cannot be found. */ @@ -495,6 +502,12 @@ public class RingtoneManager { } private Cursor getMediaRingtones() { + if (PackageManager.PERMISSION_GRANTED != mContext.checkPermission( + android.Manifest.permission.READ_EXTERNAL_STORAGE, + Process.myPid(), Process.myUid())) { + Log.w(TAG, "No READ_EXTERNAL_STORAGE permission, ignoring ringtones on ext storage"); + return null; + } // Get the external media cursor. First check to see if it is mounted. final String status = Environment.getExternalStorageState(); diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index ce72c2f..a3d748e 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -611,7 +612,7 @@ public final class TvInputInfo implements Parcelable { String format = DELIMITER_INFO_IN_ID + PREFIX_HDMI_DEVICE + "%0" + LENGTH_HDMI_PHYSICAL_ADDRESS + "X" + "%0" + LENGTH_HDMI_DEVICE_ID + "X"; - return name.flattenToShortString() + String.format(format, + return name.flattenToShortString() + String.format(Locale.ENGLISH, format, deviceInfo.getPhysicalAddress(), deviceInfo.getId()); } diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 35037bb..053d43b6 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.app.ActivityManager; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -385,13 +386,14 @@ public abstract class TvInputService extends Service { trackIdSet.clear(); // TODO: Validate the track list. + final List<TvTrackInfo> tracksCopy = new ArrayList<>(tracks); executeOrPostRunnable(new Runnable() { @Override public void run() { try { if (DEBUG) Log.d(TAG, "notifyTracksChanged"); if (mSessionCallback != null) { - mSessionCallback.onTracksChanged(tracks); + mSessionCallback.onTracksChanged(tracksCopy); } } catch (RemoteException e) { Log.w(TAG, "error in notifyTracksChanged", e); @@ -1199,12 +1201,15 @@ public abstract class TvInputService extends Service { // We make the overlay view non-focusable and non-touchable so that // the application that owns the window token can decide whether to consume or // dispatch the input events. - int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; + if (ActivityManager.isHighEndGfx()) { + flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + } mWindowParams = new WindowManager.LayoutParams( frame.right - frame.left, frame.bottom - frame.top, - frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT); + frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT); mWindowParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; mWindowParams.gravity = Gravity.START | Gravity.TOP; |