diff options
Diffstat (limited to 'media/java/android')
| -rw-r--r-- | media/java/android/media/MediaCodec.java | 47 | ||||
| -rw-r--r-- | media/java/android/media/MediaCodecInfo.java | 2 | ||||
| -rw-r--r-- | media/java/android/media/SoundPool.java | 40 | ||||
| -rw-r--r-- | media/java/android/media/browse/MediaBrowser.java | 36 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvInputInfo.java | 109 | ||||
| -rw-r--r-- | media/java/android/service/media/MediaBrowserService.java | 50 |
6 files changed, 183 insertions, 101 deletions
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index eec4960..a79dd04 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -1455,15 +1455,6 @@ final public class MediaCodec { @Retention(RetentionPolicy.SOURCE) public @interface BufferFlag {} - private static class FrameRenderedInfo { - public long mPresentationTimeUs; - public long mNanoTime; - public FrameRenderedInfo(long presentationTimeUs, long nanoTime) { - mPresentationTimeUs = presentationTimeUs; - mNanoTime = nanoTime; - } - } - private EventHandler mEventHandler; private EventHandler mOnFrameRenderedHandler; private EventHandler mCallbackHandler; @@ -1503,10 +1494,16 @@ final public class MediaCodec { } case EVENT_FRAME_RENDERED: synchronized (mListenerLock) { - FrameRenderedInfo info = (FrameRenderedInfo)msg.obj; - if (mOnFrameRenderedListener != null) { + Map<String, Object> map = (Map<String, Object>)msg.obj; + for (int i = 0; ; ++i) { + Object mediaTimeUs = map.get(i + "-media-time-us"); + Object systemNano = map.get(i + "-system-nano"); + if (mediaTimeUs == null || systemNano == null + || mOnFrameRenderedListener == null) { + break; + } mOnFrameRenderedListener.onFrameRendered( - mCodec, info.mPresentationTimeUs, info.mNanoTime); + mCodec, (long)mediaTimeUs, (long)systemNano); } break; } @@ -2362,26 +2359,9 @@ final public class MediaCodec { info = mDequeuedOutputInfos.remove(index); } } - // TODO - // until codec and libgui supports callback, assume frame is rendered within 50 ms - postRenderedCallback(render, info, 50 /* delayMs */); releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */); } - private void postRenderedCallback(boolean render, @Nullable BufferInfo info, long delayMs) { - if (render && info != null) { - synchronized (mListenerLock) { - if (mOnFrameRenderedListener != null) { - FrameRenderedInfo obj = new FrameRenderedInfo( - info.presentationTimeUs, System.nanoTime() + delayMs * 1000000); - Message msg = mOnFrameRenderedHandler.obtainMessage( - EVENT_FRAME_RENDERED, obj); - mOnFrameRenderedHandler.sendMessageDelayed(msg, delayMs); - } - } - } - } - /** * If you are done with a buffer, use this call to update its surface timestamp * and return it to the codec to render it on the output surface. If you @@ -2440,12 +2420,6 @@ final public class MediaCodec { info = mDequeuedOutputInfos.remove(index); } } - // TODO - // until codec and libgui supports callback, assume frame is rendered at the - // render time or 16 ms from now, whichever is later. - postRenderedCallback( - true /* render */, info, - Math.max(renderTimestampNs - System.nanoTime(), 16666666) / 1000000); releaseOutputBuffer( index, true /* render */, true /* updatePTS */, renderTimestampNs); } @@ -3049,9 +3023,12 @@ final public class MediaCodec { } else if (mOnFrameRenderedHandler != null) { mOnFrameRenderedHandler.removeMessages(EVENT_FRAME_RENDERED); } + native_enableOnFrameRenderedListener(listener != null); } } + private native void native_enableOnFrameRenderedListener(boolean enable); + private EventHandler getEventHandlerOn( @Nullable Handler handler, @NonNull EventHandler lastHandler) { if (handler == null) { diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 89d419a..6c26220 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1382,7 +1382,7 @@ public final class MediaCodecInfo { // upper limit. // for now we are keeping the profile specific "width/height // in macroblocks" limits. - if (Integer.valueOf(1).equals(map.get("feature-can-swap-width-height"))) { + if (map.containsKey("feature-can-swap-width-height")) { if (widths != null) { mSmallerDimensionUpperLimit = Math.min(widths.getUpper(), heights.getUpper()); diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 64863c2..1355635 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -35,6 +35,7 @@ import android.os.ServiceManager; import android.util.AndroidRuntimeException; import android.util.Log; +import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; @@ -125,10 +126,12 @@ public class SoundPool { private EventHandler mEventHandler; private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener; + private boolean mHasAppOpsPlayAudio; private final Object mLock; private final AudioAttributes mAttributes; private final IAppOpsService mAppOps; + private final IAppOpsCallback mAppOpsCallback; /** * Constructor. Constructs a SoundPool object with the following @@ -159,6 +162,24 @@ public class SoundPool { mAttributes = attributes; IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOps = IAppOpsService.Stub.asInterface(b); + // initialize mHasAppOpsPlayAudio + updateAppOpsPlayAudio(); + // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed + mAppOpsCallback = new IAppOpsCallback.Stub() { + public void opChanged(int op, String packageName) { + synchronized (mLock) { + if (op == AppOpsManager.OP_PLAY_AUDIO) { + updateAppOpsPlayAudio(); + } + } + } + }; + try { + mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO, + ActivityThread.currentPackageName(), mAppOpsCallback); + } catch (RemoteException e) { + mHasAppOpsPlayAudio = false; + } } /** @@ -168,7 +189,16 @@ public class SoundPool { * object. The SoundPool can no longer be used and the reference * should be set to null. */ - public native final void release(); + public final void release() { + try { + mAppOps.stopWatchingMode(mAppOpsCallback); + } catch (RemoteException e) { + // nothing to do here, the SoundPool is being released anyway + } + native_release(); + } + + private native final void native_release(); protected void finalize() { release(); } @@ -466,13 +496,17 @@ public class SoundPool { if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) { return false; } + return !mHasAppOpsPlayAudio; + } + + private void updateAppOpsPlayAudio() { try { final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, mAttributes.getUsage(), Process.myUid(), ActivityThread.currentPackageName()); - return mode != AppOpsManager.MODE_ALLOWED; + mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED); } catch (RemoteException e) { - return false; + mHasAppOpsPlayAudio = false; } } diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index ef8d169..ba867e1 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -375,7 +375,7 @@ public final class MediaBrowser { * @param mediaId The id of the item to retrieve. * @param cb The callback to receive the result on. */ - public void getMediaItem(@NonNull String mediaId, @NonNull final MediaItemCallback cb) { + public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) { if (TextUtils.isEmpty(mediaId)) { throw new IllegalArgumentException("mediaId is empty."); } @@ -387,7 +387,7 @@ public final class MediaBrowser { mHandler.post(new Runnable() { @Override public void run() { - cb.onError(); + cb.onError(mediaId); } }); return; @@ -397,15 +397,15 @@ public final class MediaBrowser { protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode != 0 || resultData == null || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) { - cb.onError(); + cb.onError(mediaId); return; } Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM); if (!(item instanceof MediaItem)) { - cb.onError(); + cb.onError(mediaId); + return; } - cb.onMediaItemLoaded((MediaItem) resultData.getParcelable( - MediaBrowserService.KEY_MEDIA_ITEM)); + cb.onItemLoaded((MediaItem)item); } }; try { @@ -415,7 +415,7 @@ public final class MediaBrowser { mHandler.post(new Runnable() { @Override public void run() { - cb.onError(); + cb.onError(mediaId); } }); } @@ -728,6 +728,9 @@ public final class MediaBrowser { public static abstract class SubscriptionCallback { /** * Called when the list of children is loaded or updated. + * + * @param parentId The media id of the parent media item. + * @param children The children which were loaded. */ public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children) { @@ -739,29 +742,32 @@ public final class MediaBrowser { * If this is called, the subscription remains until {@link MediaBrowser#unsubscribe} * called, because some errors may heal themselves. * </p> + * + * @param parentId The media id of the parent media item whose children could + * not be loaded. */ - public void onError(@NonNull String id) { + public void onError(@NonNull String parentId) { } } /** - * Callback for receiving the result of {@link #getMediaItem}. + * Callback for receiving the result of {@link #getItem}. */ - public static abstract class MediaItemCallback { - + public static abstract class ItemCallback { /** * Called when the item has been returned by the browser service. * * @param item The item that was returned or null if it doesn't exist. */ - public void onMediaItemLoaded(MediaItem item) { + public void onItemLoaded(MediaItem item) { } /** - * Called when the id doesn't exist or there was an error retrieving the - * item. + * Called when the item doesn't exist or there was an error retrieving it. + * + * @param itemId The media id of the media item which could not be loaded. */ - public void onError() { + public void onError(@NonNull String itemId) { } } diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index c537dd6..5d1aa14 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -29,6 +29,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.hardware.hdmi.HdmiDeviceInfo; import android.net.Uri; import android.os.Parcel; @@ -125,7 +126,9 @@ public final class TvInputInfo implements Parcelable { private String mSettingsActivity; private HdmiDeviceInfo mHdmiDeviceInfo; + private int mLabelRes; private String mLabel; + private Icon mIcon; private Uri mIconUri; private boolean mIsConnectedToHdmiSwitch; @@ -155,7 +158,7 @@ public final class TvInputInfo implements Parcelable { throws XmlPullParserException, IOException { return createTvInputInfo(context, service, generateInputIdForComponentName( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)), - null, TYPE_TUNER, false, null, null, false); + null, TYPE_TUNER, false, 0, null, null, null, false); } /** @@ -165,11 +168,11 @@ public final class TvInputInfo implements Parcelable { * @param service The ResolveInfo returned from the package manager about this TV input service. * @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device. * @param parentId The ID of this TV input's parent input. {@code null} if none exists. + * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service} + * label will be loaded. * @param iconUri The {@link android.net.Uri} to load the icon image. See * {@link android.content.ContentResolver#openInputStream}. If it is {@code null}, * the application icon of {@code service} will be loaded. - * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service} - * label will be loaded. * @hide */ @SystemApi @@ -179,7 +182,34 @@ public final class TvInputInfo implements Parcelable { boolean isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0; TvInputInfo input = createTvInputInfo(context, service, generateInputIdForHdmiDevice( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - hdmiDeviceInfo), parentId, TYPE_HDMI, true, label, iconUri, isConnectedToHdmiSwitch); + hdmiDeviceInfo), parentId, TYPE_HDMI, true, 0, label, null, iconUri, + isConnectedToHdmiSwitch); + input.mHdmiDeviceInfo = hdmiDeviceInfo; + return input; + } + + /** + * Create a new instance of the TvInputInfo class, instantiating it from the given Context, + * ResolveInfo, and HdmiDeviceInfo. + * + * @param service The ResolveInfo returned from the package manager about this TV input service. + * @param hdmiDeviceInfo The HdmiDeviceInfo for a HDMI CEC logical device. + * @param parentId The ID of this TV input's parent input. {@code null} if none exists. + * @param labelRes The label resource ID of this TvInputInfo. If it is {@code 0}, + * {@code service} label will be loaded. + * @param icon The {@link android.graphics.drawable.Icon} to load the icon image. If it is + * {@code null}, the application icon of {@code service} will be loaded. + * @hide + */ + @SystemApi + public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, + HdmiDeviceInfo hdmiDeviceInfo, String parentId, int labelRes, Icon icon) + throws XmlPullParserException, IOException { + boolean isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0; + TvInputInfo input = createTvInputInfo(context, service, generateInputIdForHdmiDevice( + new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), + hdmiDeviceInfo), parentId, TYPE_HDMI, true, labelRes, null, icon, null, + isConnectedToHdmiSwitch); input.mHdmiDeviceInfo = hdmiDeviceInfo; return input; } @@ -190,11 +220,11 @@ public final class TvInputInfo implements Parcelable { * * @param service The ResolveInfo returned from the package manager about this TV input service. * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device. + * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service} + * label will be loaded. * @param iconUri The {@link android.net.Uri} to load the icon image. See * {@link android.content.ContentResolver#openInputStream}. If it is {@code null}, * the application icon of {@code service} will be loaded. - * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service} - * label will be loaded. * @hide */ @SystemApi @@ -204,12 +234,34 @@ public final class TvInputInfo implements Parcelable { int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER); return createTvInputInfo(context, service, generateInputIdForHardware( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - hardwareInfo), null, inputType, true, label, iconUri, false); + hardwareInfo), null, inputType, true, 0, label, null, iconUri, false); + } + + /** + * Create a new instance of the TvInputInfo class, instantiating it from the given Context, + * ResolveInfo, and TvInputHardwareInfo. + * + * @param service The ResolveInfo returned from the package manager about this TV input service. + * @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device. + * @param labelRes The label resource ID of this TvInputInfo. If it is {@code 0}, + * {@code service} label will be loaded. + * @param icon The {@link android.graphics.drawable.Icon} to load the icon image. If it is + * {@code null}, the application icon of {@code service} will be loaded. + * @hide + */ + @SystemApi + public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, + TvInputHardwareInfo hardwareInfo, int labelRes, Icon icon) + throws XmlPullParserException, IOException { + int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER); + return createTvInputInfo(context, service, generateInputIdForHardware( + new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), + hardwareInfo), null, inputType, true, labelRes, null, icon, null, false); } - private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, - String id, String parentId, int inputType, boolean isHardwareInput, String label, - Uri iconUri, boolean isConnectedToHdmiSwitch) + private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, String id, + String parentId, int inputType, boolean isHardwareInput, int labelRes, String label, + Icon icon, Uri iconUri, boolean isConnectedToHdmiSwitch) throws XmlPullParserException, IOException { ServiceInfo si = service.serviceInfo; PackageManager pm = context.getPackageManager(); @@ -254,7 +306,9 @@ public final class TvInputInfo implements Parcelable { } sa.recycle(); + input.mLabelRes = labelRes; input.mLabel = label; + input.mIcon = icon; input.mIconUri = iconUri; input.mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch; return input; @@ -426,11 +480,13 @@ public final class TvInputInfo implements Parcelable { * a label, its name is returned. */ public CharSequence loadLabel(@NonNull Context context) { - if (TextUtils.isEmpty(mLabel)) { - return mService.loadLabel(context.getPackageManager()); - } else { + if (mLabelRes != 0) { + return context.getPackageManager().getText(mService.serviceInfo.packageName, mLabelRes, + null); + } else if (!TextUtils.isEmpty(mLabel)) { return mLabel; } + return mService.loadLabel(context.getPackageManager()); } /** @@ -454,19 +510,20 @@ public final class TvInputInfo implements Parcelable { * application's icon is returned. If it's unavailable too, {@code null} is returned. */ public Drawable loadIcon(@NonNull Context context) { - if (mIconUri == null) { - return loadServiceIcon(context); - } - try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) { - Drawable drawable = Drawable.createFromStream(is, null); - if (drawable == null) { - return loadServiceIcon(context); + if (mIcon != null) { + return mIcon.loadDrawable(context); + } else if (mIconUri != null) { + try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) { + Drawable drawable = Drawable.createFromStream(is, null); + if (drawable != null) { + return drawable; + } + } catch (IOException e) { + Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e); + // Falls back. } - return drawable; - } catch (IOException e) { - Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e); - return loadServiceIcon(context); } + return loadServiceIcon(context); } @Override @@ -516,7 +573,9 @@ public final class TvInputInfo implements Parcelable { dest.writeInt(mType); dest.writeByte(mIsHardwareInput ? (byte) 1 : 0); dest.writeParcelable(mHdmiDeviceInfo, flags); + dest.writeParcelable(mIcon, flags); dest.writeParcelable(mIconUri, flags); + dest.writeInt(mLabelRes); dest.writeString(mLabel); dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0); } @@ -591,7 +650,9 @@ public final class TvInputInfo implements Parcelable { mType = in.readInt(); mIsHardwareInput = in.readByte() == 1 ? true : false; mHdmiDeviceInfo = in.readParcelable(null); + mIcon = in.readParcelable(null); mIconUri = in.readParcelable(null); + mLabelRes = in.readInt(); mLabel = in.readString(); mIsConnectedToHdmiSwitch = in.readByte() == 1 ? true : false; } diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index 41156cb..1bb99ff 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -76,7 +76,7 @@ public abstract class MediaBrowserService extends Service { public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; /** - * A key for passing the MediaItem to the ResultReceiver in getMediaItem. + * A key for passing the MediaItem to the ResultReceiver in getItem. * * @hide */ @@ -109,6 +109,7 @@ public abstract class MediaBrowserService extends Service { * be thrown. * * @see MediaBrowserService#onLoadChildren + * @see MediaBrowserService#onGetMediaItem */ public class Result<T> { private Object mDebug; @@ -279,20 +280,7 @@ public abstract class MediaBrowserService extends Service { mHandler.post(new Runnable() { @Override public void run() { - final Result<MediaBrowser.MediaItem> result - = new Result<MediaBrowser.MediaItem>(mediaId) { - @Override - void onResultSent(MediaBrowser.MediaItem item) { - Bundle bundle = new Bundle(); - bundle.putParcelable(KEY_MEDIA_ITEM, item); - receiver.send(0, bundle); - } - }; - try { - MediaBrowserService.this.getMediaItem(mediaId, result); - } catch (UnsupportedOperationException e) { - receiver.send(-1, null); - } + performLoadItem(mediaId, receiver); } }); } @@ -357,8 +345,7 @@ public abstract class MediaBrowserService extends Service { @NonNull Result<List<MediaBrowser.MediaItem>> result); /** - * Called to get a specific media item. The mediaId should be the same id - * that would be returned for this item when it is in a list of child items. + * Called to get information about a specific media item. * <p> * Implementations must call {@link Result#sendResult result.sendResult}. If * loading the item will be an expensive operation {@link Result#detach @@ -366,17 +353,15 @@ public abstract class MediaBrowserService extends Service { * then {@link Result#sendResult result.sendResult} called when the item has * been loaded. * <p> - * The default implementation throws an exception. + * The default implementation sends a null result. * - * @param mediaId The id for the specific + * @param itemId The id for the specific * {@link android.media.browse.MediaBrowser.MediaItem}. * @param result The Result to send the item to, or null if the id is * invalid. - * @throws UnsupportedOperationException */ - public void getMediaItem(String mediaId, Result<MediaBrowser.MediaItem> result) - throws UnsupportedOperationException { - throw new UnsupportedOperationException("getMediaItem is not supported."); + public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) { + result.sendResult(null); } /** @@ -515,6 +500,25 @@ public abstract class MediaBrowserService extends Service { } } + private void performLoadItem(String itemId, final ResultReceiver receiver) { + final Result<MediaBrowser.MediaItem> result = + new Result<MediaBrowser.MediaItem>(itemId) { + @Override + void onResultSent(MediaBrowser.MediaItem item) { + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_MEDIA_ITEM, item); + receiver.send(0, bundle); + } + }; + + MediaBrowserService.this.onLoadItem(itemId, result); + + if (!result.isDone()) { + throw new IllegalStateException("onLoadItem must call detach() or sendResult()" + + " before returning for id=" + itemId); + } + } + /** * Contains information that the browser service needs to send to the client * when first connected. |
