summaryrefslogtreecommitdiffstats
path: root/media/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'media/java/android')
-rw-r--r--media/java/android/media/IRingtonePlayer.aidl3
-rw-r--r--media/java/android/media/MediaPlayer.java6
-rw-r--r--media/java/android/media/MediaSync.java40
-rw-r--r--media/java/android/media/Ringtone.java48
-rw-r--r--media/java/android/media/RingtoneManager.java19
-rw-r--r--media/java/android/media/tv/TvInputInfo.java3
-rw-r--r--media/java/android/media/tv/TvInputService.java15
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;