summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioManager.java29
-rw-r--r--media/java/android/media/AudioRecord.java12
-rw-r--r--media/java/android/media/AudioService.java29
-rw-r--r--media/java/android/media/AudioSystem.java11
-rw-r--r--media/java/android/media/AudioTrack.java12
-rw-r--r--media/java/android/media/DngCreator.java168
-rw-r--r--media/java/android/media/MediaCodec.java1
-rw-r--r--media/java/android/media/MediaCodecInfo.java31
-rw-r--r--media/java/android/media/MediaMetadata.aidl (renamed from media/java/android/media/session/SessionToken.aidl)4
-rw-r--r--media/java/android/media/MediaMetadata.java (renamed from media/java/android/media/session/MediaMetadata.java)5
-rw-r--r--media/java/android/media/MediaMetadataEditor.java1
-rw-r--r--media/java/android/media/RemoteControlClient.java5
-rw-r--r--media/java/android/media/routeprovider/RouteConnection.java1
-rw-r--r--media/java/android/media/routeprovider/RouteInterfaceHandler.java9
-rw-r--r--media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java1
-rw-r--r--media/java/android/media/routeprovider/RouteProviderService.java1
-rw-r--r--media/java/android/media/routeprovider/RouteRequest.java11
-rw-r--r--media/java/android/media/session/ISession.aidl2
-rw-r--r--media/java/android/media/session/ISessionCallback.aidl2
-rw-r--r--media/java/android/media/session/ISessionController.aidl2
-rw-r--r--media/java/android/media/session/ISessionControllerCallback.aidl2
-rw-r--r--media/java/android/media/session/ISessionManager.aidl2
-rw-r--r--media/java/android/media/session/MediaController.java (renamed from media/java/android/media/session/SessionController.java)38
-rw-r--r--media/java/android/media/session/MediaSession.java (renamed from media/java/android/media/session/Session.java)105
-rw-r--r--media/java/android/media/session/MediaSessionInfo.java (renamed from media/java/android/media/session/SessionInfo.java)18
-rw-r--r--media/java/android/media/session/MediaSessionLegacyHelper.java121
-rw-r--r--media/java/android/media/session/MediaSessionManager.java (renamed from media/java/android/media/session/SessionManager.java)55
-rw-r--r--media/java/android/media/session/MediaSessionToken.aidl (renamed from media/java/android/media/session/MediaMetadata.aidl)2
-rw-r--r--media/java/android/media/session/MediaSessionToken.java (renamed from media/java/android/media/session/SessionToken.java)18
-rw-r--r--media/java/android/media/session/PlaybackState.java3
-rw-r--r--media/java/android/media/session/Route.java7
-rw-r--r--media/java/android/media/session/RouteInfo.java1
-rw-r--r--media/java/android/media/session/RouteInterface.java7
-rw-r--r--media/java/android/media/session/RouteOptions.java1
-rw-r--r--media/java/android/media/session/RoutePlaybackControls.java2
-rw-r--r--media/java/android/media/session/TransportController.java1
-rw-r--r--media/java/android/media/session/TransportPerformer.java1
-rw-r--r--media/jni/Android.mk3
-rw-r--r--media/jni/android_media_DngCreator.cpp772
-rw-r--r--media/jni/android_media_MediaPlayer.cpp6
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java12
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java2
44 files changed, 1306 insertions, 214 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1dcfcb8..3a3f76d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -428,7 +428,6 @@ public class AudioManager {
public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
private static IAudioService sService;
- private MediaSessionLegacyHelper mSessionHelper;
/**
* @hide
@@ -439,9 +438,6 @@ public class AudioManager {
com.android.internal.R.bool.config_useMasterVolume);
mUseVolumeKeySounds = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useVolumeKeySounds);
- if (USE_SESSIONS) {
- mSessionHelper = MediaSessionLegacyHelper.getHelper(context);
- }
}
private static IAudioService getService()
@@ -478,11 +474,16 @@ public class AudioManager {
* or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
*/
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- IAudioService service = getService();
- try {
- service.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e);
+ if (USE_SESSIONS) {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendMediaButtonEvent(keyEvent, false);
+ } else {
+ IAudioService service = getService();
+ try {
+ service.dispatchMediaKeyEvent(keyEvent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e);
+ }
}
}
@@ -2178,7 +2179,8 @@ public class AudioManager {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
if (USE_SESSIONS) {
- mSessionHelper.addMediaButtonListener(pi, mContext);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.addMediaButtonListener(pi, mContext);
}
}
@@ -2254,7 +2256,8 @@ public class AudioManager {
Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
}
if (USE_SESSIONS) {
- mSessionHelper.removeMediaButtonListener(pi);
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.removeMediaButtonListener(pi);
}
}
@@ -2281,7 +2284,7 @@ public class AudioManager {
Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
}
if (USE_SESSIONS) {
- rcClient.registerWithSession(mSessionHelper);
+ rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
}
}
@@ -2303,7 +2306,7 @@ public class AudioManager {
Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
}
if (USE_SESSIONS) {
- rcClient.unregisterWithSession(mSessionHelper);
+ rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
}
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 384e120..d4e85c8 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -61,25 +61,25 @@ public class AudioRecord
*/
public static final int RECORDSTATE_RECORDING = 3;// matches SL_RECORDSTATE_RECORDING
- // Error codes:
- // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp
/**
* Denotes a successful operation.
*/
- public static final int SUCCESS = 0;
+ public static final int SUCCESS = AudioSystem.SUCCESS;
/**
* Denotes a generic operation failure.
*/
- public static final int ERROR = -1;
+ public static final int ERROR = AudioSystem.ERROR;
/**
* Denotes a failure due to the use of an invalid value.
*/
- public static final int ERROR_BAD_VALUE = -2;
+ public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
/**
* Denotes a failure due to the improper use of a method.
*/
- public static final int ERROR_INVALID_OPERATION = -3;
+ public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
+ // Error codes:
+ // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp
private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16;
private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK = -17;
private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 724022b..bb8cfa6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -46,6 +46,7 @@ import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
+import android.media.session.MediaSessionLegacyHelper;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
@@ -108,6 +109,10 @@ public class AudioService extends IAudioService.Stub {
/** Debug volumes */
protected static final boolean DEBUG_VOL = false;
+ /** Reroute calls to media session apis */
+ private static final boolean USE_SESSIONS = true;
+ private static final boolean DEBUG_SESSIONS = true;
+
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
@@ -3472,7 +3477,7 @@ public class AudioService extends IAudioService.Stub {
if (volume < 0) {
volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
} else {
- volFloat = (float) volume / 1000.0f;
+ volFloat = volume / 1000.0f;
}
if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
@@ -3554,7 +3559,7 @@ public class AudioService extends IAudioService.Stub {
}
Settings.System.putFloatForUser(mContentResolver,
Settings.System.VOLUME_MASTER,
- (float)msg.arg1 / (float)1000.0,
+ msg.arg1 / (float)1000.0,
UserHandle.USER_CURRENT);
break;
@@ -4325,11 +4330,27 @@ public class AudioService extends IAudioService.Stub {
}
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+ if (USE_SESSIONS) {
+ if (DEBUG_SESSIONS) {
+ int pid = getCallingPid();
+ Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
+ }
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
+ } else {
+ mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+ }
}
public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+ if (USE_SESSIONS) {
+ if (DEBUG_SESSIONS) {
+ int pid = getCallingPid();
+ Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
+ }
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
+ } else {
+ mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+ }
}
//==========================================================================================
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 327c10c..5ddb198 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -199,6 +199,17 @@ public class AudioSystem
}
}
+ /*
+ * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
+ * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h
+ */
+ public static final int SUCCESS = 0;
+ public static final int ERROR = -1;
+ public static final int BAD_VALUE = -2;
+ public static final int INVALID_OPERATION = -3;
+ public static final int PERMISSION_DENIED = -4;
+ public static final int NO_INIT = -5;
+ public static final int DEAD_OBJECT = -6;
/*
* AudioPolicyService methods
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 1a64cff..1baaaa4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -125,25 +125,25 @@ public class AudioTrack
*/
public static final int STATE_NO_STATIC_DATA = 2;
- // Error codes:
- // to keep in sync with frameworks/base/core/jni/android_media_AudioTrack.cpp
/**
* Denotes a successful operation.
*/
- public static final int SUCCESS = 0;
+ public static final int SUCCESS = AudioSystem.SUCCESS;
/**
* Denotes a generic operation failure.
*/
- public static final int ERROR = -1;
+ public static final int ERROR = AudioSystem.ERROR;
/**
* Denotes a failure due to the use of an invalid value.
*/
- public static final int ERROR_BAD_VALUE = -2;
+ public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
/**
* Denotes a failure due to the improper use of a method.
*/
- public static final int ERROR_INVALID_OPERATION = -3;
+ public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
+ // Error codes:
+ // to keep in sync with frameworks/base/core/jni/android_media_AudioTrack.cpp
private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -16;
private static final int ERROR_NATIVESETUP_INVALIDCHANNELMASK = -17;
private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -18;
diff --git a/media/java/android/media/DngCreator.java b/media/java/android/media/DngCreator.java
index b2a38ab..76c6d46 100644
--- a/media/java/android/media/DngCreator.java
+++ b/media/java/android/media/DngCreator.java
@@ -17,9 +17,12 @@
package android.media;
import android.graphics.Bitmap;
+import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.impl.CameraMetadataNative;
import android.location.Location;
+import android.util.Size;
import java.io.IOException;
import java.io.InputStream;
@@ -50,7 +53,7 @@ import java.nio.ByteBuffer;
* Adobe DNG 1.4.0.0 specification</a>.
* </p>
*/
-public final class DngCreator {
+public final class DngCreator implements AutoCloseable {
/**
* Create a new DNG object.
@@ -68,7 +71,12 @@ public final class DngCreator {
* {@link android.hardware.camera2.CameraCharacteristics}.
* @param metadata a metadata object to generate tags from.
*/
- public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {/*TODO*/}
+ public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {
+ if (characteristics == null || metadata == null) {
+ throw new NullPointerException("Null argument to DngCreator constructor");
+ }
+ nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());
+ }
/**
* Set the orientation value to write.
@@ -92,6 +100,13 @@ public final class DngCreator {
* @return this {@link #DngCreator} object.
*/
public DngCreator setOrientation(int orientation) {
+
+ if (orientation < ExifInterface.ORIENTATION_UNDEFINED ||
+ orientation > ExifInterface.ORIENTATION_ROTATE_270) {
+ throw new IllegalArgumentException("Orientation " + orientation +
+ " is not a valid EXIF orientation value");
+ }
+ nativeSetOrientation(orientation);
return this;
}
@@ -111,6 +126,20 @@ public final class DngCreator {
* @return this {@link #DngCreator} object.
*/
public DngCreator setThumbnail(Bitmap pixels) {
+ if (pixels == null) {
+ throw new NullPointerException("Null argument to setThumbnail");
+ }
+
+ Bitmap.Config config = pixels.getConfig();
+
+ if (config != Bitmap.Config.ARGB_8888) {
+ pixels = pixels.copy(Bitmap.Config.ARGB_8888, false);
+ if (pixels == null) {
+ throw new IllegalArgumentException("Unsupported Bitmap format " + config);
+ }
+ nativeSetThumbnailBitmap(pixels);
+ }
+
return this;
}
@@ -130,6 +159,21 @@ public final class DngCreator {
* @return this {@link #DngCreator} object.
*/
public DngCreator setThumbnail(Image pixels) {
+ if (pixels == null) {
+ throw new NullPointerException("Null argument to setThumbnail");
+ }
+
+ int format = pixels.getFormat();
+ if (format != ImageFormat.YUV_420_888) {
+ throw new IllegalArgumentException("Unsupported image format " + format);
+ }
+
+ Image.Plane[] planes = pixels.getPlanes();
+ nativeSetThumbnailImage(pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
+ planes[0].getRowStride(), planes[0].getPixelStride(), planes[1].getBuffer(),
+ planes[1].getRowStride(), planes[1].getPixelStride(), planes[1].getBuffer(),
+ planes[1].getRowStride(), planes[1].getPixelStride());
+
return this;
}
@@ -150,7 +194,10 @@ public final class DngCreator {
* @throws java.lang.IllegalArgumentException if the given location object doesn't
* contain enough information to set location metadata.
*/
- public DngCreator setLocation(Location location) { return this; }
+ public DngCreator setLocation(Location location) {
+ /*TODO*/
+ return this;
+ }
/**
* Set the user description string to write.
@@ -163,6 +210,7 @@ public final class DngCreator {
* @return this {@link #DngCreator} object.
*/
public DngCreator setDescription(String description) {
+ /*TODO*/
return this;
}
@@ -172,32 +220,33 @@ public final class DngCreator {
*
* <p>
* Raw pixel data must have 16 bits per pixel, and the input must contain at least
- * {@code offset + 2 * (stride * (height - 1) + width * height)} bytes. The width and height of
+ * {@code offset + 2 * width * height)} bytes. The width and height of
* the input are taken from the width and height set in the {@link DngCreator} metadata tags,
* and will typically be equal to the width and height of
- * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
- * If insufficient metadata is set to write a well-formatted DNG file, and
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+ * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
+ * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}. If insufficient
+ * metadata is available to write a well-formatted DNG file, an
* {@link java.lang.IllegalStateException} will be thrown.
* </p>
*
- * <p>
- * When reading from the pixel input, {@code stride} pixels will be skipped
- * after each row (excluding the last).
- * </p>
- *
* @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+ * @param size the {@link Size} of the image to write, in pixels.
* @param pixels an {@link java.io.InputStream} of pixel data to write.
- * @param stride the stride of the raw image in pixels.
* @param offset the offset of the raw image in bytes. This indicates how many bytes will
* be skipped in the input before any pixel data is read.
*
* @throws IOException if an error was encountered in the input or output stream.
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
+ * @throws java.lang.IllegalArgumentException if the size passed in does not match the
*/
- public void writeInputStream(OutputStream dngOutput, InputStream pixels, int stride,
- long offset) throws IOException {
- /*TODO*/
+ public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
+ throws IOException {
+ if (dngOutput == null || pixels == null) {
+ throw new NullPointerException("Null argument to writeImage");
+ }
+ nativeWriteInputStream(dngOutput, pixels, offset);
}
/**
@@ -206,22 +255,18 @@ public final class DngCreator {
*
* <p>
* Raw pixel data must have 16 bits per pixel, and the input must contain at least
- * {@code offset + 2 * (stride * (height - 1) + width * height)} bytes. The width and height of
+ * {@code offset + 2 * width * height)} bytes. The width and height of
* the input are taken from the width and height set in the {@link DngCreator} metadata tags,
* and will typically be equal to the width and height of
- * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
- * If insufficient metadata is set to write a well-formatted DNG file, and
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+ * The pixel layout in the input is determined from the reported color filter arrangement (CFA)
+ * set in {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}. If insufficient
+ * metadata is available to write a well-formatted DNG file, an
* {@link java.lang.IllegalStateException} will be thrown.
* </p>
*
- * <p>
- * When reading from the pixel input, {@code stride} pixels will be skipped
- * after each row (excluding the last).
- * </p>
- *
* @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
* @param pixels an {@link java.nio.ByteBuffer} of pixel data to write.
- * @param stride the stride of the raw image in pixels.
* @param offset the offset of the raw image in bytes. This indicates how many bytes will
* be skipped in the input before any pixel data is read.
*
@@ -229,8 +274,13 @@ public final class DngCreator {
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
*/
- public void writeByteBuffer(OutputStream dngOutput, ByteBuffer pixels, int stride,
- long offset) throws IOException {/*TODO*/}
+ public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
+ throws IOException {
+ if (dngOutput == null || pixels == null) {
+ throw new NullPointerException("Null argument to writeImage");
+ }
+ nativeWriteByteBuffer(dngOutput, pixels, offset);
+ }
/**
* Write the pixel data to a DNG file with the currently configured metadata.
@@ -249,6 +299,70 @@ public final class DngCreator {
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
*/
- public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {/*TODO*/}
+ public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {
+ if (dngOutput == null || pixels == null) {
+ throw new NullPointerException("Null argument to writeImage");
+ }
+ int format = pixels.getFormat();
+ if (format != ImageFormat.RAW_SENSOR) {
+ throw new IllegalArgumentException("Unsupported image format " + format);
+ }
+
+ Image.Plane[] planes = pixels.getPlanes();
+ nativeWriteImage(dngOutput, pixels.getWidth(), pixels.getHeight(), planes[0].getBuffer(),
+ planes[0].getRowStride(), planes[0].getPixelStride());
+ }
+
+ @Override
+ public void close() {
+ nativeDestroy();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * This field is used by native code, do not access or modify.
+ */
+ private long mNativeContext;
+
+ private static native void nativeClassInit();
+
+ private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
+ CameraMetadataNative nativeResult);
+
+ private synchronized native void nativeDestroy();
+
+ private synchronized native void nativeSetOrientation(int orientation);
+
+ private synchronized native void nativeSetThumbnailBitmap(Bitmap bitmap);
+
+ private synchronized native void nativeSetThumbnailImage(int width, int height,
+ ByteBuffer yBuffer, int yRowStride,
+ int yPixStride, ByteBuffer uBuffer,
+ int uRowStride, int uPixStride,
+ ByteBuffer vBuffer, int vRowStride,
+ int vPixStride);
+
+ private synchronized native void nativeWriteImage(OutputStream out, int width, int height,
+ ByteBuffer rawBuffer, int rowStride,
+ int pixStride) throws IOException;
+
+ private synchronized native void nativeWriteByteBuffer(OutputStream out, ByteBuffer rawBuffer,
+ long offset) throws IOException;
+
+ private synchronized native void nativeWriteInputStream(OutputStream out, InputStream rawStream,
+ long offset) throws IOException;
+
+ static {
+ System.loadLibrary("media_jni");
+ nativeClassInit();
+ }
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 34c5520..c7b3fc9 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -212,6 +212,7 @@ final public class MediaCodec {
* <li>"video/x-vnd.on2.vp8" - VP8 video (i.e. video in .webm)
* <li>"video/x-vnd.on2.vp9" - VP9 video (i.e. video in .webm)
* <li>"video/avc" - H.264/AVC video
+ * <li>"video/hevc" - H.265/HEVC video
* <li>"video/mp4v-es" - MPEG4 video
* <li>"video/3gpp" - H.263 video
* <li>"audio/3gpp" - AMR narrowband audio
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 90c12c6..b5d0a57 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -264,6 +264,37 @@ public final class MediaCodecInfo {
// from OMX_VIDEO_VP8PROFILETYPE
public static final int VP8ProfileMain = 0x01;
+ // from OMX_VIDEO_HEVCPROFILETYPE
+ public static final int HEVCProfileMain = 0x01;
+ public static final int HEVCProfileMain10 = 0x02;
+
+ // from OMX_VIDEO_HEVCLEVELTYPE
+ public static final int HEVCMainTierLevel1 = 0x1;
+ public static final int HEVCHighTierLevel1 = 0x2;
+ public static final int HEVCMainTierLevel2 = 0x4;
+ public static final int HEVCHighTierLevel2 = 0x8;
+ public static final int HEVCMainTierLevel21 = 0x10;
+ public static final int HEVCHighTierLevel21 = 0x20;
+ public static final int HEVCMainTierLevel3 = 0x40;
+ public static final int HEVCHighTierLevel3 = 0x80;
+ public static final int HEVCMainTierLevel31 = 0x100;
+ public static final int HEVCHighTierLevel31 = 0x200;
+ public static final int HEVCMainTierLevel4 = 0x400;
+ public static final int HEVCHighTierLevel4 = 0x800;
+ public static final int HEVCMainTierLevel41 = 0x1000;
+ public static final int HEVCHighTierLevel41 = 0x2000;
+ public static final int HEVCMainTierLevel5 = 0x4000;
+ public static final int HEVCHighTierLevel5 = 0x8000;
+ public static final int HEVCMainTierLevel51 = 0x10000;
+ public static final int HEVCHighTierLevel51 = 0x20000;
+ public static final int HEVCMainTierLevel52 = 0x40000;
+ public static final int HEVCHighTierLevel52 = 0x80000;
+ public static final int HEVCMainTierLevel6 = 0x100000;
+ public static final int HEVCHighTierLevel6 = 0x200000;
+ public static final int HEVCMainTierLevel61 = 0x400000;
+ public static final int HEVCHighTierLevel61 = 0x800000;
+ public static final int HEVCMainTierLevel62 = 0x1000000;
+ public static final int HEVCHighTierLevel62 = 0x2000000;
/**
* Defined in the OpenMAX IL specs, depending on the type of media
diff --git a/media/java/android/media/session/SessionToken.aidl b/media/java/android/media/MediaMetadata.aidl
index db35f85..66ee483 100644
--- a/media/java/android/media/session/SessionToken.aidl
+++ b/media/java/android/media/MediaMetadata.aidl
@@ -13,6 +13,6 @@
** limitations under the License.
*/
-package android.media.session;
+package android.media;
-parcelable SessionToken;
+parcelable MediaMetadata;
diff --git a/media/java/android/media/session/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 8a8af45..ff73a10 100644
--- a/media/java/android/media/session/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -13,12 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.session;
+package android.media;
import android.graphics.Bitmap;
-import android.media.MediaMetadataEditor;
-import android.media.MediaMetadataRetriever;
-import android.media.Rating;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/media/java/android/media/MediaMetadataEditor.java b/media/java/android/media/MediaMetadataEditor.java
index 1a4e8da..ca44e9d 100644
--- a/media/java/android/media/MediaMetadataEditor.java
+++ b/media/java/android/media/MediaMetadataEditor.java
@@ -17,7 +17,6 @@
package android.media;
import android.graphics.Bitmap;
-import android.media.session.MediaMetadata;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 8368df9..37f45c2 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -24,10 +24,9 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
-import android.media.session.MediaMetadata;
import android.media.session.MediaSessionLegacyHelper;
import android.media.session.PlaybackState;
-import android.media.session.Session;
+import android.media.session.MediaSession;
import android.media.session.TransportPerformer;
import android.os.Bundle;
import android.os.Handler;
@@ -341,7 +340,7 @@ public class RemoteControlClient
*/
public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
- private Session mSession;
+ private MediaSession mSession;
/**
* Class constructor.
diff --git a/media/java/android/media/routeprovider/RouteConnection.java b/media/java/android/media/routeprovider/RouteConnection.java
index 9214ff8..43692c1 100644
--- a/media/java/android/media/routeprovider/RouteConnection.java
+++ b/media/java/android/media/routeprovider/RouteConnection.java
@@ -40,6 +40,7 @@ import java.util.List;
* interfaces. Use {@link #addRouteInterface(String)} to add an interface and
* {@link #getRouteInterface(String)} to retrieve the interface's handle anytime
* after it has been added.
+ * @hide
*/
public final class RouteConnection {
private static final String TAG = "RouteConnection";
diff --git a/media/java/android/media/routeprovider/RouteInterfaceHandler.java b/media/java/android/media/routeprovider/RouteInterfaceHandler.java
index 9693dc6..e7f8bbf 100644
--- a/media/java/android/media/routeprovider/RouteInterfaceHandler.java
+++ b/media/java/android/media/routeprovider/RouteInterfaceHandler.java
@@ -16,7 +16,7 @@
package android.media.routeprovider;
import android.media.session.Route;
-import android.media.session.Session;
+import android.media.session.MediaSession;
import android.media.session.RouteInterface;
import android.os.Bundle;
import android.os.Handler;
@@ -33,7 +33,7 @@ import java.util.ArrayList;
* connected media route.
* <p>
* A {@link RouteProviderService} may expose multiple interfaces on a
- * {@link RouteConnection} for a {@link Session} to interact with. A
+ * {@link RouteConnection} for a {@link MediaSession} to interact with. A
* provider creates an interface with
* {@link RouteConnection#addRouteInterface(String)} to allow messages to be
* routed appropriately. Events are then sent through a specific interface and
@@ -47,6 +47,7 @@ import java.util.ArrayList;
* It is recommended you wrap this interface with a standard implementation to
* avoid errors, but for simple interfaces this class may be used directly. TODO
* add link to sample code.
+ * @hide
*/
public final class RouteInterfaceHandler {
private static final String TAG = "RouteInterfaceHandler";
@@ -184,7 +185,7 @@ public final class RouteInterfaceHandler {
public abstract static class CommandListener {
/**
* This is called when a command is received that matches this
- * interface. Commands are sent by a {@link Session} that is
+ * interface. Commands are sent by a {@link MediaSession} that is
* connected to the route this interface is registered with.
*
* @param iface The interface the command was received on.
@@ -197,7 +198,7 @@ public final class RouteInterfaceHandler {
* true may be returned if the command will be handled
* asynchronously.
* @see Route
- * @see Session
+ * @see MediaSession
*/
public abstract boolean onCommand(RouteInterfaceHandler iface, String command, Bundle args,
ResultReceiver cb);
diff --git a/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java b/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
index dcef79a..f2c40d2 100644
--- a/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
+++ b/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
@@ -28,6 +28,7 @@ import android.util.Log;
* Standard wrapper for using playback controls over a {@link RouteInterfaceHandler}.
* This is the provider half of the interface. Sessions should use
* {@link RoutePlaybackControls} to interact with this interface.
+ * @hide
*/
public final class RoutePlaybackControlsHandler {
private static final String TAG = "RoutePlaybackControls";
diff --git a/media/java/android/media/routeprovider/RouteProviderService.java b/media/java/android/media/routeprovider/RouteProviderService.java
index 6ebfb5b..a6ef0bb 100644
--- a/media/java/android/media/routeprovider/RouteProviderService.java
+++ b/media/java/android/media/routeprovider/RouteProviderService.java
@@ -64,6 +64,7 @@ import java.util.List;
* &lt;/intent-filter>
* &lt;/service>
* </pre>
+ * @hide
*/
public abstract class RouteProviderService extends Service {
private static final String TAG = "RouteProvider";
diff --git a/media/java/android/media/routeprovider/RouteRequest.java b/media/java/android/media/routeprovider/RouteRequest.java
index 68475c0..2ba75de 100644
--- a/media/java/android/media/routeprovider/RouteRequest.java
+++ b/media/java/android/media/routeprovider/RouteRequest.java
@@ -16,7 +16,7 @@
package android.media.routeprovider;
import android.media.session.RouteOptions;
-import android.media.session.SessionInfo;
+import android.media.session.MediaSessionInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,16 +30,17 @@ import java.io.PrintWriter;
* provides the full set of connection parameters they would like to use for a
* connection. An app that can connect in multiple ways will be represented by
* multiple requests.
+ * @hide
*/
public final class RouteRequest implements Parcelable {
- private final SessionInfo mSessionInfo;
+ private final MediaSessionInfo mSessionInfo;
private final RouteOptions mOptions;
private final boolean mActive;
/**
* @hide
*/
- public RouteRequest(SessionInfo info, RouteOptions connRequest,
+ public RouteRequest(MediaSessionInfo info, RouteOptions connRequest,
boolean active) {
mSessionInfo = info;
mOptions = connRequest;
@@ -47,7 +48,7 @@ public final class RouteRequest implements Parcelable {
}
private RouteRequest(Parcel in) {
- mSessionInfo = SessionInfo.CREATOR.createFromParcel(in);
+ mSessionInfo = MediaSessionInfo.CREATOR.createFromParcel(in);
mOptions = RouteOptions.CREATOR.createFromParcel(in);
mActive = in.readInt() != 0;
}
@@ -57,7 +58,7 @@ public final class RouteRequest implements Parcelable {
*
* @return Info on the session making the request
*/
- public SessionInfo getSessionInfo() {
+ public MediaSessionInfo getSessionInfo() {
return mSessionInfo;
}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 096550f..c4233c3 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,8 +15,8 @@
package android.media.session;
+import android.media.MediaMetadata;
import android.media.session.ISessionController;
-import android.media.session.MediaMetadata;
import android.media.session.RouteOptions;
import android.media.session.RouteCommand;
import android.media.session.RouteInfo;
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 1552513..7b0412e 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -28,7 +28,7 @@ import android.os.ResultReceiver;
*/
oneway interface ISessionCallback {
void onCommand(String command, in Bundle extras, in ResultReceiver cb);
- void onMediaButton(in Intent mediaButtonIntent);
+ void onMediaButton(in Intent mediaButtonIntent, in ResultReceiver cb);
void onRequestRouteChange(in RouteInfo route);
void onRouteConnected(in RouteInfo route, in RouteOptions options);
void onRouteDisconnected(in RouteInfo route, int reason);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e2e046f..5ddb6db 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -16,9 +16,9 @@
package android.media.session;
import android.content.Intent;
+import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
-import android.media.session.MediaMetadata;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.ResultReceiver;
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index bc1ae05..e823153 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -15,7 +15,7 @@
package android.media.session;
-import android.media.session.MediaMetadata;
+import android.media.MediaMetadata;
import android.media.session.RouteInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index e341647..38b9293 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -19,6 +19,7 @@ import android.content.ComponentName;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.os.Bundle;
+import android.view.KeyEvent;
/**
* Interface to the MediaSessionManagerService
@@ -27,4 +28,5 @@ import android.os.Bundle;
interface ISessionManager {
ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
List<IBinder> getSessions(in ComponentName compName, int userId);
+ void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
} \ No newline at end of file
diff --git a/media/java/android/media/session/SessionController.java b/media/java/android/media/session/MediaController.java
index dc4f7d9..642ac2f 100644
--- a/media/java/android/media/session/SessionController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -16,6 +16,7 @@
package android.media.session;
+import android.media.MediaMetadata;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -34,13 +35,13 @@ import java.util.ArrayList;
* other commands can be sent to the session. A callback may be registered to
* receive updates from the session, such as metadata and play state changes.
* <p>
- * A MediaController can be created through {@link SessionManager} if you
+ * A MediaController can be created through {@link MediaSessionManager} if you
* hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
- * you have a {@link SessionToken} from the session owner.
+ * you have a {@link MediaSessionToken} from the session owner.
* <p>
* MediaController objects are thread-safe.
*/
-public final class SessionController {
+public final class MediaController {
private static final String TAG = "SessionController";
private static final int MSG_EVENT = 1;
@@ -58,15 +59,15 @@ public final class SessionController {
private TransportController mTransportController;
- private SessionController(ISessionController sessionBinder) {
+ private MediaController(ISessionController sessionBinder) {
mSessionBinder = sessionBinder;
}
/**
* @hide
*/
- public static SessionController fromBinder(ISessionController sessionBinder) {
- SessionController controller = new SessionController(sessionBinder);
+ public static MediaController fromBinder(ISessionController sessionBinder) {
+ MediaController controller = new MediaController(sessionBinder);
try {
controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
if (controller.mSessionBinder.isTransportControlEnabled()) {
@@ -87,7 +88,7 @@ public final class SessionController {
* @param token The session token to use
* @return A controller for the session or null
*/
- public static SessionController fromToken(SessionToken token) {
+ public static MediaController fromToken(MediaSessionToken token) {
return fromBinder(token.getBinder());
}
@@ -184,6 +185,8 @@ public final class SessionController {
/**
* Request that the route picker be shown for this session. This should
* generally be called in response to a user action.
+ *
+ * @hide
*/
public void showRoutePicker() {
try {
@@ -285,22 +288,23 @@ public final class SessionController {
/**
* Override to handle route changes for this session.
*
- * @param route
+ * @param route The new route
+ * @hide
*/
public void onRouteChanged(RouteInfo route) {
}
}
private final static class CallbackStub extends ISessionControllerCallback.Stub {
- private final WeakReference<SessionController> mController;
+ private final WeakReference<MediaController> mController;
- public CallbackStub(SessionController controller) {
- mController = new WeakReference<SessionController>(controller);
+ public CallbackStub(MediaController controller) {
+ mController = new WeakReference<MediaController>(controller);
}
@Override
public void onEvent(String event, Bundle extras) {
- SessionController controller = mController.get();
+ MediaController controller = mController.get();
if (controller != null) {
controller.postEvent(event, extras);
}
@@ -308,7 +312,7 @@ public final class SessionController {
@Override
public void onRouteChanged(RouteInfo route) {
- SessionController controller = mController.get();
+ MediaController controller = mController.get();
if (controller != null) {
controller.postRouteChanged(route);
}
@@ -316,7 +320,7 @@ public final class SessionController {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
- SessionController controller = mController.get();
+ MediaController controller = mController.get();
if (controller != null) {
TransportController tc = controller.getTransportController();
if (tc != null) {
@@ -327,7 +331,7 @@ public final class SessionController {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
- SessionController controller = mController.get();
+ MediaController controller = mController.get();
if (controller != null) {
TransportController tc = controller.getTransportController();
if (tc != null) {
@@ -339,9 +343,9 @@ public final class SessionController {
}
private final static class MessageHandler extends Handler {
- private final SessionController.Callback mCallback;
+ private final MediaController.Callback mCallback;
- public MessageHandler(Looper looper, SessionController.Callback cb) {
+ public MessageHandler(Looper looper, MediaController.Callback cb) {
super(looper, null, true);
mCallback = cb;
}
diff --git a/media/java/android/media/session/Session.java b/media/java/android/media/session/MediaSession.java
index 2ffced6..5b9adaa 100644
--- a/media/java/android/media/session/Session.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -36,21 +36,21 @@ import java.util.ArrayList;
import java.util.List;
/**
- * Allows interaction with media controllers, media routes, volume keys, media
- * buttons, and transport controls.
+ * Allows interaction with media controllers, volume keys, media buttons, and
+ * transport controls.
* <p>
* A MediaSession should be created when an app wants to publish media playback
- * information or negotiate with a media route. In general an app only needs one
- * session for all playback, though multiple sessions can be created for sending
- * media to multiple routes or to provide finer grain controls of media.
+ * information or handle media keys. In general an app only needs one session
+ * for all playback, though multiple sessions can be created to provide finer
+ * grain controls of media.
* <p>
* A MediaSession is created by calling
- * {@link SessionManager#createSession(String)}. Once a session is created apps
- * that have the MEDIA_CONTENT_CONTROL permission can interact with the session
- * through
- * {@link SessionManager#getActiveSessions(android.content.ComponentName)}. The
- * owner of the session may also use {@link #getSessionToken()} to allow apps
- * without this permission to create a {@link SessionController} to interact
+ * {@link MediaSessionManager#createSession(String)}. Once a session is created
+ * apps that have the MEDIA_CONTENT_CONTROL permission can interact with the
+ * session through
+ * {@link MediaSessionManager#getActiveSessions(android.content.ComponentName)}.
+ * The owner of the session may also use {@link #getSessionToken()} to allow
+ * apps without this permission to create a {@link MediaController} to interact
* with this session.
* <p>
* To receive commands, media keys, and other events a Callback must be set with
@@ -61,7 +61,7 @@ import java.util.List;
* <p>
* MediaSession objects are thread safe
*/
-public final class Session {
+public final class MediaSession {
private static final String TAG = "Session";
/**
@@ -89,31 +89,43 @@ public final class Session {
/**
* Indicates the session was disconnected because the user that the session
* belonged to is stopping.
+ * @hide
*/
public static final int DISCONNECT_REASON_USER_STOPPING = 1;
/**
* Indicates the session was disconnected because the provider disconnected
* the route.
+ * @hide
*/
public static final int DISCONNECT_REASON_PROVIDER_DISCONNECTED = 2;
/**
* Indicates the session was disconnected because the route has changed.
+ * @hide
*/
public static final int DISCONNECT_REASON_ROUTE_CHANGED = 3;
/**
* Indicates the session was disconnected because the session owner
* requested it disconnect.
+ * @hide
*/
public static final int DISCONNECT_REASON_SESSION_DISCONNECTED = 4;
/**
* Indicates the session was disconnected because it was destroyed.
+ * @hide
*/
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
+ /**
+ * Status code indicating the call was handled.
+ *
+ * @hide
+ */
+ public static final int RESULT_SUCCESS = 0;
+
private static final int MSG_MEDIA_BUTTON = 1;
private static final int MSG_COMMAND = 2;
private static final int MSG_ROUTE_CHANGE = 3;
@@ -126,7 +138,7 @@ public final class Session {
private final Object mLock = new Object();
- private final SessionToken mSessionToken;
+ private final MediaSessionToken mSessionToken;
private final ISession mBinder;
private final CallbackStub mCbStub;
@@ -143,7 +155,7 @@ public final class Session {
/**
* @hide
*/
- public Session(ISession binder, CallbackStub cbStub) {
+ public MediaSession(ISession binder, CallbackStub cbStub) {
mBinder = binder;
mCbStub = cbStub;
ISessionController controllerBinder = null;
@@ -152,7 +164,7 @@ public final class Session {
} catch (RemoteException e) {
throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
}
- mSessionToken = new SessionToken(controllerBinder);
+ mSessionToken = new MediaSessionToken(controllerBinder);
mPerformer = new TransportPerformer(mBinder);
}
@@ -167,7 +179,7 @@ public final class Session {
/**
* Add a callback to receive updates for the MediaSession. This includes
- * events like route updates, media buttons, and focus changes.
+ * media button and volume events.
*
* @param callback The callback to receive updates on.
* @param handler The handler that events should be posted on.
@@ -288,13 +300,13 @@ public final class Session {
/**
* Retrieve a token object that can be used by apps to create a
- * {@link SessionController} for interacting with this session. The owner of
+ * {@link MediaController} for interacting with this session. The owner of
* the session is responsible for deciding how to distribute these tokens.
*
* @return A token that can be used to create a MediaController for this
* session
*/
- public SessionToken getSessionToken() {
+ public MediaSessionToken getSessionToken() {
return mSessionToken;
}
@@ -304,8 +316,8 @@ public final class Session {
* Connection updates will be sent to the callback's
* {@link Callback#onRouteConnected(Route)} and
* {@link Callback#onRouteDisconnected(Route, int)} methods. If the
- * connection fails {@link Callback#onRouteDisconnected(Route, int)}
- * will be called.
+ * connection fails {@link Callback#onRouteDisconnected(Route, int)} will be
+ * called.
* <p>
* If you already have a connection to this route it will be disconnected
* before the new connection is established. TODO add an easy way to compare
@@ -313,6 +325,7 @@ public final class Session {
*
* @param route The route the app is trying to connect to.
* @param request The connection request to use.
+ * @hide
*/
public void connect(RouteInfo route, RouteOptions request) {
if (route == null) {
@@ -331,6 +344,8 @@ public final class Session {
/**
* Disconnect from the current route. After calling you will be switched
* back to the default route.
+ *
+ * @hide
*/
public void disconnect() {
if (mRoute != null) {
@@ -347,6 +362,7 @@ public final class Session {
* will be used for picking valid routes.
*
* @param options The set of route options your app may use to connect.
+ * @hide
*/
public void setRouteOptions(List<RouteOptions> options) {
try {
@@ -491,6 +507,7 @@ public final class Session {
* ongoing playback if necessary.
*
* @param route
+ * @hide
*/
public void onRequestRouteChange(RouteInfo route) {
}
@@ -500,6 +517,7 @@ public final class Session {
* are now valid.
*
* @param route The route that was connected
+ * @hide
*/
public void onRouteConnected(Route route) {
}
@@ -519,6 +537,7 @@ public final class Session {
*
* @param route The route that disconnected
* @param reason The reason for the disconnect
+ * @hide
*/
public void onRouteDisconnected(Route route, int reason) {
}
@@ -528,32 +547,36 @@ public final class Session {
* @hide
*/
public static class CallbackStub extends ISessionCallback.Stub {
- private WeakReference<Session> mMediaSession;
+ private WeakReference<MediaSession> mMediaSession;
- public void setMediaSession(Session session) {
- mMediaSession = new WeakReference<Session>(session);
+ public void setMediaSession(MediaSession session) {
+ mMediaSession = new WeakReference<MediaSession>(session);
}
@Override
public void onCommand(String command, Bundle extras, ResultReceiver cb)
throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
session.postCommand(command, extras, cb);
}
}
@Override
- public void onMediaButton(Intent mediaButtonIntent) throws RemoteException {
- Session session = mMediaSession.get();
+ public void onMediaButton(Intent mediaButtonIntent, ResultReceiver cb)
+ throws RemoteException {
+ MediaSession session = mMediaSession.get();
if (session != null) {
session.postMediaButton(mediaButtonIntent);
}
+ if (cb != null) {
+ cb.send(RESULT_SUCCESS, null);
+ }
}
@Override
public void onRequestRouteChange(RouteInfo route) throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
session.postRequestRouteChange(route);
}
@@ -561,7 +584,7 @@ public final class Session {
@Override
public void onRouteConnected(RouteInfo route, RouteOptions options) {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
session.postRouteConnected(route, options);
}
@@ -569,7 +592,7 @@ public final class Session {
@Override
public void onRouteDisconnected(RouteInfo route, int reason) {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
session.postRouteDisconnected(route, reason);
}
@@ -577,7 +600,7 @@ public final class Session {
@Override
public void onPlay() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -588,7 +611,7 @@ public final class Session {
@Override
public void onPause() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -599,7 +622,7 @@ public final class Session {
@Override
public void onStop() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -610,7 +633,7 @@ public final class Session {
@Override
public void onNext() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -621,7 +644,7 @@ public final class Session {
@Override
public void onPrevious() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -632,7 +655,7 @@ public final class Session {
@Override
public void onFastForward() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -643,7 +666,7 @@ public final class Session {
@Override
public void onRewind() throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -654,7 +677,7 @@ public final class Session {
@Override
public void onSeekTo(long pos) throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -665,7 +688,7 @@ public final class Session {
@Override
public void onRate(Rating rating) throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
TransportPerformer tp = session.getTransportPerformer();
if (tp != null) {
@@ -676,7 +699,7 @@ public final class Session {
@Override
public void onRouteEvent(RouteEvent event) throws RemoteException {
- Session session = mMediaSession.get();
+ MediaSession session = mMediaSession.get();
if (session != null) {
RouteInterface.EventListener iface
= session.mInterfaceListeners.get(event.getIface());
@@ -697,9 +720,9 @@ public final class Session {
}
private class MessageHandler extends Handler {
- private Session.Callback mCallback;
+ private MediaSession.Callback mCallback;
- public MessageHandler(Looper looper, Session.Callback callback) {
+ public MessageHandler(Looper looper, MediaSession.Callback callback) {
super(looper, null, true);
mCallback = callback;
}
diff --git a/media/java/android/media/session/SessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java
index 2b65528..3d8d33f 100644
--- a/media/java/android/media/session/SessionInfo.java
+++ b/media/java/android/media/session/MediaSessionInfo.java
@@ -21,19 +21,19 @@ import android.os.Parcelable;
/**
* Information about a media session, including the owner's package name.
*/
-public final class SessionInfo implements Parcelable {
+public final class MediaSessionInfo implements Parcelable {
private final String mId;
private final String mPackageName;
/**
* @hide
*/
- public SessionInfo(String id, String packageName) {
+ public MediaSessionInfo(String id, String packageName) {
mId = id;
mPackageName = packageName;
}
- private SessionInfo(Parcel in) {
+ private MediaSessionInfo(Parcel in) {
mId = in.readString();
mPackageName = in.readString();
}
@@ -72,16 +72,16 @@ public final class SessionInfo implements Parcelable {
dest.writeString(mPackageName);
}
- public static final Parcelable.Creator<SessionInfo> CREATOR
- = new Parcelable.Creator<SessionInfo>() {
+ public static final Parcelable.Creator<MediaSessionInfo> CREATOR
+ = new Parcelable.Creator<MediaSessionInfo>() {
@Override
- public SessionInfo createFromParcel(Parcel in) {
- return new SessionInfo(in);
+ public MediaSessionInfo createFromParcel(Parcel in) {
+ return new MediaSessionInfo(in);
}
@Override
- public SessionInfo[] newArray(int size) {
- return new SessionInfo[size];
+ public MediaSessionInfo[] newArray(int size) {
+ return new MediaSessionInfo[size];
}
};
}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index c07229d..2e02a66 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -35,11 +35,12 @@ import android.view.KeyEvent;
*/
public class MediaSessionLegacyHelper {
private static final String TAG = "MediaSessionHelper";
+ private static final boolean DEBUG = true;
private static final Object sLock = new Object();
private static MediaSessionLegacyHelper sInstance;
- private SessionManager mSessionManager;
+ private MediaSessionManager mSessionManager;
private Handler mHandler = new Handler(Looper.getMainLooper());
// The legacy APIs use PendingIntents to register/unregister media button
// receivers and these are associated with RCC.
@@ -47,11 +48,14 @@ public class MediaSessionLegacyHelper {
= new ArrayMap<PendingIntent, SessionHolder>();
private MediaSessionLegacyHelper(Context context) {
- mSessionManager = (SessionManager) context
+ mSessionManager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
}
public static MediaSessionLegacyHelper getHelper(Context context) {
+ if (DEBUG) {
+ Log.d(TAG, "Attempting to get helper with context " + context);
+ }
synchronized (sLock) {
if (sInstance == null) {
sInstance = new MediaSessionLegacyHelper(context);
@@ -60,17 +64,30 @@ public class MediaSessionLegacyHelper {
return sInstance;
}
- public Session getSession(PendingIntent pi) {
+ public MediaSession getSession(PendingIntent pi) {
SessionHolder holder = mSessions.get(pi);
return holder == null ? null : holder.mSession;
}
- public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+ public void sendMediaButtonEvent(KeyEvent keyEvent, boolean needWakeLock) {
+ mSessionManager.dispatchMediaKeyEvent(keyEvent, needWakeLock);
+ if (DEBUG) {
+ Log.d(TAG, "dispatched media key " + keyEvent);
+ }
+ }
+ public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
+ if (pi == null) {
+ Log.w(TAG, "Pending intent was null, can't add rcc listener.");
+ return;
+ }
SessionHolder holder = getHolder(pi, true);
TransportPerformer performer = holder.mSession.getTransportPerformer();
if (holder.mRccListener != null) {
if (holder.mRccListener == listener) {
+ if (DEBUG) {
+ Log.d(TAG, "addRccListener listener already added.");
+ }
// This is already the registered listener, ignore
return;
}
@@ -79,50 +96,82 @@ public class MediaSessionLegacyHelper {
}
performer.addListener(listener, mHandler);
holder.mRccListener = listener;
- holder.mFlags |= Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
+ holder.mFlags |= MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS;
holder.mSession.setFlags(holder.mFlags);
holder.update();
+ if (DEBUG) {
+ Log.d(TAG, "Added rcc listener for " + pi + ".");
+ }
}
public void removeRccListener(PendingIntent pi) {
+ if (pi == null) {
+ return;
+ }
SessionHolder holder = getHolder(pi, false);
if (holder != null && holder.mRccListener != null) {
holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
holder.mRccListener = null;
- holder.mFlags &= ~Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
+ holder.mFlags &= ~MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS;
holder.mSession.setFlags(holder.mFlags);
holder.update();
+ if (DEBUG) {
+ Log.d(TAG, "Removed rcc listener for " + pi + ".");
+ }
}
}
public void addMediaButtonListener(PendingIntent pi,
Context context) {
+ if (pi == null) {
+ Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");
+ return;
+ }
SessionHolder holder = getHolder(pi, true);
if (holder.mMediaButtonListener != null) {
- // Already have this listener registered
+ // Already have this listener registered, but update it anyway as
+ // the extras may have changed.
+ if (DEBUG) {
+ Log.d(TAG, "addMediaButtonListener already added " + pi);
+ }
return;
}
holder.mMediaButtonListener = new MediaButtonListener(pi, context);
- holder.mFlags |= Session.FLAG_HANDLES_MEDIA_BUTTONS;
+ holder.mFlags |= MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
holder.mSession.setFlags(holder.mFlags);
holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
+
+ holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
+ holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
+ if (DEBUG) {
+ Log.d(TAG, "addMediaButtonListener added " + pi);
+ }
}
public void removeMediaButtonListener(PendingIntent pi) {
+ if (pi == null) {
+ return;
+ }
SessionHolder holder = getHolder(pi, false);
if (holder != null && holder.mMediaButtonListener != null) {
holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
- holder.mFlags &= ~Session.FLAG_HANDLES_MEDIA_BUTTONS;
+ holder.mFlags &= ~MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
holder.mSession.setFlags(holder.mFlags);
holder.mMediaButtonListener = null;
+
+ holder.mSession.removeCallback(holder.mMediaButtonReceiver);
+ holder.mMediaButtonReceiver = null;
holder.update();
+ if (DEBUG) {
+ Log.d(TAG, "removeMediaButtonListener removed " + pi);
+ }
}
}
private SessionHolder getHolder(PendingIntent pi, boolean createIfMissing) {
SessionHolder holder = mSessions.get(pi);
if (holder == null && createIfMissing) {
- Session session = mSessionManager.createSession(TAG);
+ MediaSession session = mSessionManager.createSession(TAG);
session.setActive(true);
holder = new SessionHolder(session, pi);
mSessions.put(pi, holder);
@@ -130,7 +179,32 @@ public class MediaSessionLegacyHelper {
return holder;
}
- public static class MediaButtonListener extends TransportPerformer.Listener {
+ private static void sendKeyEvent(PendingIntent pi, Context context, Intent intent) {
+ try {
+ pi.send(context, 0, intent);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending media key down event:", e);
+ // Don't bother sending up if down failed
+ return;
+ }
+ }
+
+ private static final class MediaButtonReceiver extends MediaSession.Callback {
+ private final PendingIntent mPendingIntent;
+ private final Context mContext;
+
+ public MediaButtonReceiver(PendingIntent pi, Context context) {
+ mPendingIntent = pi;
+ mContext = context;
+ }
+
+ @Override
+ public void onMediaButton(Intent mediaButtonIntent) {
+ MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, mediaButtonIntent);
+ }
+ }
+
+ private static final class MediaButtonListener extends TransportPerformer.Listener {
private final PendingIntent mPendingIntent;
private final Context mContext;
@@ -179,32 +253,27 @@ public class MediaSessionLegacyHelper {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.putExtra(Intent.EXTRA_KEY_EVENT, ke);
- try {
- mPendingIntent.send(mContext, 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending media key down event:", e);
- // Don't bother sending up if down failed
- return;
- }
+ MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, intent);
ke = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
intent.putExtra(Intent.EXTRA_KEY_EVENT, ke);
- try {
- mPendingIntent.send(mContext, 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending media key up event:", e);
+ MediaSessionLegacyHelper.sendKeyEvent(mPendingIntent, mContext, intent);
+
+ if (DEBUG) {
+ Log.d(TAG, "Sent " + keyCode + " to pending intent " + mPendingIntent);
}
}
}
private class SessionHolder {
- public final Session mSession;
+ public final MediaSession mSession;
public final PendingIntent mPi;
public MediaButtonListener mMediaButtonListener;
+ public MediaButtonReceiver mMediaButtonReceiver;
public TransportPerformer.Listener mRccListener;
public int mFlags;
- public SessionHolder(Session session, PendingIntent pi) {
+ public SessionHolder(MediaSession session, PendingIntent pi) {
mSession = session;
mPi = pi;
}
@@ -213,10 +282,6 @@ public class MediaSessionLegacyHelper {
if (mMediaButtonListener == null && mRccListener == null) {
mSession.release();
mSessions.remove(mPi);
- } else if (mMediaButtonListener != null && mRccListener != null) {
- // TODO set session to active
- } else {
- // TODO set session to inactive
}
}
}
diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 1eb3b7a..0589a7d 100644
--- a/media/java/android/media/session/SessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -25,6 +25,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.util.Log;
+import android.view.KeyEvent;
import java.util.ArrayList;
import java.util.List;
@@ -38,10 +39,10 @@ import java.util.List;
* get an instance of this class.
* <p>
*
- * @see Session
- * @see SessionController
+ * @see MediaSession
+ * @see MediaController
*/
-public final class SessionManager {
+public final class MediaSessionManager {
private static final String TAG = "SessionManager";
private final ISessionManager mService;
@@ -51,7 +52,7 @@ public final class SessionManager {
/**
* @hide
*/
- public SessionManager(Context context) {
+ public MediaSessionManager(Context context) {
// Consider rewriting like DisplayManagerGlobal
// Decide if we need context
mContext = context;
@@ -63,9 +64,9 @@ public final class SessionManager {
* Creates a new session.
*
* @param tag A short name for debugging purposes
- * @return a {@link Session} for the new session
+ * @return a {@link MediaSession} for the new session
*/
- public Session createSession(String tag) {
+ public MediaSession createSession(String tag) {
return createSessionAsUser(tag, UserHandle.myUserId());
}
@@ -77,13 +78,13 @@ public final class SessionManager {
*
* @param tag A short name for debugging purposes
* @param userId The user id to create the session as.
- * @return a {@link Session} for the new session
+ * @return a {@link MediaSession} for the new session
* @hide
*/
- public Session createSessionAsUser(String tag, int userId) {
+ public MediaSession createSessionAsUser(String tag, int userId) {
try {
- Session.CallbackStub cbStub = new Session.CallbackStub();
- Session session = new Session(mService
+ MediaSession.CallbackStub cbStub = new MediaSession.CallbackStub();
+ MediaSession session = new MediaSession(mService
.createSession(mContext.getPackageName(), cbStub, tag, userId), cbStub);
cbStub.setMediaSession(session);
@@ -106,7 +107,7 @@ public final class SessionManager {
* May be null.
* @return A list of controllers for ongoing sessions
*/
- public List<SessionController> getActiveSessions(ComponentName notificationListener) {
+ public List<MediaController> getActiveSessions(ComponentName notificationListener) {
return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
}
@@ -123,13 +124,13 @@ public final class SessionManager {
* @return A list of controllers for ongoing sessions.
* @hide
*/
- public List<SessionController> getActiveSessionsForUser(ComponentName notificationListener,
+ public List<MediaController> getActiveSessionsForUser(ComponentName notificationListener,
int userId) {
- ArrayList<SessionController> controllers = new ArrayList<SessionController>();
+ ArrayList<MediaController> controllers = new ArrayList<MediaController>();
try {
List<IBinder> binders = mService.getSessions(notificationListener, userId);
for (int i = binders.size() - 1; i >= 0; i--) {
- SessionController controller = SessionController.fromBinder(ISessionController.Stub
+ MediaController controller = MediaController.fromBinder(ISessionController.Stub
.asInterface(binders.get(i)));
controllers.add(controller);
}
@@ -138,4 +139,30 @@ public final class SessionManager {
}
return controllers;
}
+
+ /**
+ * Send a media key event. The receiver will be selected automatically.
+ *
+ * @param keyEvent The KeyEvent to send.
+ * @hide
+ */
+ public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
+ dispatchMediaKeyEvent(keyEvent, false);
+ }
+
+ /**
+ * Send a media key event. The receiver will be selected automatically.
+ *
+ * @param keyEvent The KeyEvent to send
+ * @param needWakeLock true if a wake lock should be held while sending the
+ * key
+ * @hide
+ */
+ public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+ try {
+ mService.dispatchMediaKeyEvent(keyEvent, needWakeLock);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send key event.", e);
+ }
+ }
}
diff --git a/media/java/android/media/session/MediaMetadata.aidl b/media/java/android/media/session/MediaSessionToken.aidl
index 4431d9d..5812682 100644
--- a/media/java/android/media/session/MediaMetadata.aidl
+++ b/media/java/android/media/session/MediaSessionToken.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable MediaMetadata;
+parcelable MediaSessionToken;
diff --git a/media/java/android/media/session/SessionToken.java b/media/java/android/media/session/MediaSessionToken.java
index 59486f6..f5569a4 100644
--- a/media/java/android/media/session/SessionToken.java
+++ b/media/java/android/media/session/MediaSessionToken.java
@@ -20,17 +20,17 @@ import android.media.session.ISessionController;
import android.os.Parcel;
import android.os.Parcelable;
-public class SessionToken implements Parcelable {
+public class MediaSessionToken implements Parcelable {
private ISessionController mBinder;
/**
* @hide
*/
- SessionToken(ISessionController binder) {
+ MediaSessionToken(ISessionController binder) {
mBinder = binder;
}
- private SessionToken(Parcel in) {
+ private MediaSessionToken(Parcel in) {
mBinder = ISessionController.Stub.asInterface(in.readStrongBinder());
}
@@ -51,16 +51,16 @@ public class SessionToken implements Parcelable {
dest.writeStrongBinder(mBinder.asBinder());
}
- public static final Parcelable.Creator<SessionToken> CREATOR
- = new Parcelable.Creator<SessionToken>() {
+ public static final Parcelable.Creator<MediaSessionToken> CREATOR
+ = new Parcelable.Creator<MediaSessionToken>() {
@Override
- public SessionToken createFromParcel(Parcel in) {
- return new SessionToken(in);
+ public MediaSessionToken createFromParcel(Parcel in) {
+ return new MediaSessionToken(in);
}
@Override
- public SessionToken[] newArray(int size) {
- return new SessionToken[size];
+ public MediaSessionToken[] newArray(int size) {
+ return new MediaSessionToken[size];
}
};
}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 3254e5d..7ef38eaa 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -21,7 +21,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
/**
- * Playback state for a {@link Session}. This includes a state like
+ * Playback state for a {@link MediaSession}. This includes a state like
* {@link PlaybackState#PLAYSTATE_PLAYING}, the current playback position,
* and the current control capabilities.
*/
@@ -160,6 +160,7 @@ public final class PlaybackState implements Parcelable {
* route. Depending on the implementation you may return to the previous
* state when the connection finishes or enter {@link #PLAYSTATE_NONE}. If
* the connection failed {@link #PLAYSTATE_ERROR} should be used.
+ * @hide
*/
public final static int PLAYSTATE_CONNECTING = 8;
diff --git a/media/java/android/media/session/Route.java b/media/java/android/media/session/Route.java
index c9530a6..935eb5b 100644
--- a/media/java/android/media/session/Route.java
+++ b/media/java/android/media/session/Route.java
@@ -28,17 +28,18 @@ import java.util.List;
* to. The MediaRoute must be used to get {@link RouteInterface}
* instances which can be used to communicate over a specific interface on the
* route.
+ * @hide
*/
public final class Route {
private static final String TAG = "Route";
private final RouteInfo mInfo;
- private final Session mSession;
+ private final MediaSession mSession;
private final RouteOptions mOptions;
/**
* @hide
*/
- public Route(RouteInfo info, RouteOptions options, Session session) {
+ public Route(RouteInfo info, RouteOptions options, MediaSession session) {
if (info == null || options == null) {
throw new IllegalStateException("Route info was not valid!");
}
@@ -93,7 +94,7 @@ public final class Route {
/**
* @hide
*/
- Session getSession() {
+ MediaSession getSession() {
return mSession;
}
}
diff --git a/media/java/android/media/session/RouteInfo.java b/media/java/android/media/session/RouteInfo.java
index 17df969..02f78f9 100644
--- a/media/java/android/media/session/RouteInfo.java
+++ b/media/java/android/media/session/RouteInfo.java
@@ -25,6 +25,7 @@ import java.util.List;
/**
* Information about a route, including its display name, a way to identify it,
* and the ways it can be connected to.
+ * @hide
*/
public final class RouteInfo implements Parcelable {
private final String mName;
diff --git a/media/java/android/media/session/RouteInterface.java b/media/java/android/media/session/RouteInterface.java
index e9c9fd3..8de4d89 100644
--- a/media/java/android/media/session/RouteInterface.java
+++ b/media/java/android/media/session/RouteInterface.java
@@ -25,7 +25,7 @@ import android.util.Log;
import java.util.ArrayList;
/**
- * A route can support multiple interfaces for a {@link Session} to
+ * A route can support multiple interfaces for a {@link MediaSession} to
* interact with. To use a specific interface with a route a
* MediaSessionRouteInterface needs to be retrieved from the route. An
* implementation of the specific interface, like
@@ -33,6 +33,7 @@ import java.util.ArrayList;
* and reduce errors on that interface.
*
* @see RoutePlaybackControls for an example
+ * @hide
*/
public final class RouteInterface {
private static final String TAG = "RouteInterface";
@@ -67,7 +68,7 @@ public final class RouteInterface {
private final Route mRoute;
private final String mIface;
- private final Session mSession;
+ private final MediaSession mSession;
private final Object mLock = new Object();
private final ArrayList<EventHandler> mListeners = new ArrayList<EventHandler>();
@@ -75,7 +76,7 @@ public final class RouteInterface {
/**
* @hide
*/
- RouteInterface(Route route, String iface, Session session) {
+ RouteInterface(Route route, String iface, MediaSession session) {
mRoute = route;
mIface = iface;
mSession = session;
diff --git a/media/java/android/media/session/RouteOptions.java b/media/java/android/media/session/RouteOptions.java
index 5105867..b4fb341 100644
--- a/media/java/android/media/session/RouteOptions.java
+++ b/media/java/android/media/session/RouteOptions.java
@@ -34,6 +34,7 @@ import java.util.List;
* appropriate route options when it is ready to connect to the route. Each
* route options instance must specify a complete set of capabilities to request
* when the connection is established.
+ * @hide
*/
public final class RouteOptions implements Parcelable {
private static final String TAG = "RouteOptions";
diff --git a/media/java/android/media/session/RoutePlaybackControls.java b/media/java/android/media/session/RoutePlaybackControls.java
index a3ffb58..8211983 100644
--- a/media/java/android/media/session/RoutePlaybackControls.java
+++ b/media/java/android/media/session/RoutePlaybackControls.java
@@ -15,6 +15,7 @@
*/
package android.media.session;
+import android.media.MediaMetadata;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
@@ -23,6 +24,7 @@ import android.os.ResultReceiver;
* A standard media control interface for Routes that support queueing and
* transport controls. Routes may support multiple interfaces for MediaSessions
* to interact with.
+ * @hide
*/
public final class RoutePlaybackControls {
private static final String TAG = "RoutePlaybackControls";
diff --git a/media/java/android/media/session/TransportController.java b/media/java/android/media/session/TransportController.java
index 9574df6..090489b 100644
--- a/media/java/android/media/session/TransportController.java
+++ b/media/java/android/media/session/TransportController.java
@@ -15,6 +15,7 @@
*/
package android.media.session;
+import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Handler;
import android.os.Looper;
diff --git a/media/java/android/media/session/TransportPerformer.java b/media/java/android/media/session/TransportPerformer.java
index 187f48d..1588d8f 100644
--- a/media/java/android/media/session/TransportPerformer.java
+++ b/media/java/android/media/session/TransportPerformer.java
@@ -16,6 +16,7 @@
package android.media.session;
import android.media.AudioManager;
+import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Handler;
import android.os.Looper;
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 90fe695..d658654 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ android_media_DngCreator.cpp \
android_media_ImageReader.cpp \
android_media_MediaCrypto.cpp \
android_media_MediaCodec.cpp \
@@ -41,6 +42,7 @@ LOCAL_SHARED_LIBRARIES := \
libjhead \
libexif \
libstagefright_amrnb_common \
+ libimg_utils \
LOCAL_REQUIRED_MODULES := \
libjhead_jni
@@ -53,6 +55,7 @@ LOCAL_C_INCLUDES += \
external/tremor/Tremor \
frameworks/base/core/jni \
frameworks/av/media/libmedia \
+ frameworks/av/media/img_utils/include \
frameworks/av/media/libstagefright \
frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
frameworks/av/media/libstagefright/codecs/amrnb/common \
diff --git a/media/jni/android_media_DngCreator.cpp b/media/jni/android_media_DngCreator.cpp
new file mode 100644
index 0000000..860d896
--- /dev/null
+++ b/media/jni/android_media_DngCreator.cpp
@@ -0,0 +1,772 @@
+/*
+ * Copyright 2014 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DngCreator_JNI"
+
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+#include <img_utils/DngUtils.h>
+#include <img_utils/TagDefinitions.h>
+#include <img_utils/TiffIfd.h>
+#include <img_utils/TiffWriter.h>
+#include <img_utils/Output.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+#include <cutils/properties.h>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+using namespace android;
+using namespace img_utils;
+
+#define BAIL_IF_INVALID(expr, jnienv, tagId) \
+ if ((expr) != OK) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Invalid metadata for tag %x", tagId); \
+ return; \
+ }
+
+#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
+ if (entry.count == 0) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Missing metadata fields for tag %x", tagId); \
+ return; \
+ }
+
+#define ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID "mNativeContext"
+
+static struct {
+ jfieldID mNativeContext;
+} gDngCreatorClassInfo;
+
+static struct {
+ jmethodID mWriteMethod;
+} gOutputStreamClassInfo;
+
+enum {
+ BITS_PER_SAMPLE = 16,
+ BYTES_PER_SAMPLE = 2,
+ TIFF_IFD_0 = 0
+};
+
+// ----------------------------------------------------------------------------
+
+// This class is not intended to be used across JNI calls.
+class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
+public:
+ JniOutputStream(JNIEnv* env, jobject outStream);
+
+ virtual ~JniOutputStream();
+
+ status_t open();
+ status_t write(const uint8_t* buf, size_t offset, size_t count);
+ status_t close();
+private:
+ enum {
+ BYTE_ARRAY_LENGTH = 1024
+ };
+ jobject mOutputStream;
+ JNIEnv* mEnv;
+ jbyteArray mByteArray;
+};
+
+JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
+ mEnv(env) {
+ mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
+ if (mByteArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
+ }
+}
+
+JniOutputStream::~JniOutputStream() {
+ mEnv->DeleteLocalRef(mByteArray);
+}
+
+status_t JniOutputStream::open() {
+ // Do nothing
+ return OK;
+}
+
+status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
+ while(count > 0) {
+ size_t len = BYTE_ARRAY_LENGTH;
+ len = (count > len) ? len : count;
+ mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
+ 0, len);
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ count -= len;
+ offset += len;
+ }
+ return OK;
+}
+
+status_t JniOutputStream::close() {
+ // Do nothing
+ return OK;
+}
+
+// ----------------------------------------------------------------------------
+
+extern "C" {
+
+static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
+ gDngCreatorClassInfo.mNativeContext));
+}
+
+static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
+ ALOGV("%s:", __FUNCTION__);
+ TiffWriter* current = DngCreator_getCreator(env, thiz);
+ if (writer != NULL) {
+ writer->incStrong((void*) DngCreator_setCreator);
+ }
+ if (current) {
+ current->decStrong((void*) DngCreator_setCreator);
+ }
+ env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
+ reinterpret_cast<jlong>(writer.get()));
+}
+
+static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
+ ALOGV("%s:", __FUNCTION__);
+
+ gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
+ ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
+ "can't find android/media/DngCreator.%s", ANDROID_MEDIA_DNGCREATOR_CTX_JNI_ID);
+
+ jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
+ LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
+ gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
+ LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
+}
+
+static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
+ jobject resultsPtr) {
+ ALOGV("%s:", __FUNCTION__);
+ CameraMetadata characteristics;
+ CameraMetadata results;
+ if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "No native metadata defined for camera characteristics.");
+ return;
+ }
+ if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "No native metadata defined for capture results.");
+ return;
+ }
+
+ sp<TiffWriter> writer = new TiffWriter();
+
+ writer->addIfd(TIFF_IFD_0);
+
+ status_t err = OK;
+
+ const uint32_t samplesPerPixel = 1;
+ const uint32_t bitsPerSample = BITS_PER_SAMPLE;
+ const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
+ uint32_t imageWidth = 0;
+ uint32_t imageHeight = 0;
+
+ OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+
+ // TODO: Greensplit.
+ // TODO: UniqueCameraModel
+ // TODO: Add remaining non-essential tags
+ {
+ // Set orientation
+ uint16_t orientation = 1; // Normal
+ BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
+ TAG_ORIENTATION);
+ }
+
+ {
+ // Set subfiletype
+ uint32_t subfileType = 0; // Main image
+ BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
+ TAG_NEWSUBFILETYPE);
+ }
+
+ {
+ // Set bits per sample
+ uint16_t bits = static_cast<uint16_t>(bitsPerSample);
+ BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
+ TAG_BITSPERSAMPLE);
+ }
+
+ {
+ // Set compression
+ uint16_t compression = 1; // None
+ BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
+ TAG_COMPRESSION);
+ }
+
+ {
+ // Set dimensions
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
+ uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
+ uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
+ TAG_IMAGEWIDTH);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
+ TAG_IMAGELENGTH);
+ imageWidth = width;
+ imageHeight = height;
+ }
+
+ {
+ // Set photometric interpretation
+ uint16_t interpretation = 32803;
+ BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
+ TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
+ }
+
+ {
+ // Set blacklevel tags
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+ BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
+ const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
+ BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
+ TAG_BLACKLEVEL);
+
+ uint16_t repeatDim[2] = {2, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
+ TAG_BLACKLEVELREPEATDIM);
+ }
+
+ {
+ // Set samples per pixel
+ uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
+ BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
+ env, TAG_SAMPLESPERPIXEL);
+ }
+
+ {
+ // Set planar configuration
+ uint16_t config = 1; // Chunky
+ BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
+ env, TAG_PLANARCONFIGURATION);
+ }
+
+ {
+ // Set CFA pattern dimensions
+ uint16_t repeatDim[2] = {2, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
+ env, TAG_CFAREPEATPATTERNDIM);
+ }
+
+ {
+ // Set CFA pattern
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
+ camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
+ static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
+ entry.data.u8[0]);
+ switch(cfa) {
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
+ uint8_t cfa[4] = {0, 1, 1, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
+ uint8_t cfa[4] = {1, 0, 2, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
+ uint8_t cfa[4] = {1, 2, 0, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
+ uint8_t cfa[4] = {2, 1, 1, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
+ break;
+ }
+ default: {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid metadata for tag %d", TAG_CFAPATTERN);
+ return;
+ }
+ }
+ }
+
+ {
+ // Set CFA plane color
+ uint8_t cfaPlaneColor[3] = {0, 1, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
+ env, TAG_CFAPLANECOLOR);
+ }
+
+ {
+ // Set CFA layout
+ uint16_t cfaLayout = 1;
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
+ env, TAG_CFALAYOUT);
+ }
+
+ {
+ // Set DNG version information
+ uint8_t version[4] = {1, 4, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
+ env, TAG_DNGVERSION);
+
+ uint8_t backwardVersion[4] = {1, 1, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
+ env, TAG_DNGBACKWARDVERSION);
+ }
+
+ {
+ // Set whitelevel
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
+ BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
+ uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
+ BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
+ TAG_WHITELEVEL);
+ }
+
+ {
+ // Set default scale
+ uint32_t defaultScale[4] = {1, 1, 1, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
+ env, TAG_DEFAULTSCALE);
+ }
+
+ bool singleIlluminant = false;
+ {
+ // Set calibration illuminants
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
+ if (entry2.count == 0) {
+ singleIlluminant = true;
+ }
+ uint16_t ref1 = entry1.data.u8[0];
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
+
+ if (!singleIlluminant) {
+ uint16_t ref2 = entry2.data.u8[0];
+ BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
+ }
+ }
+
+ {
+ // Set color transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
+ BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
+
+ int32_t colorTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ colorTransform1[ctr++] = entry1.data.r[i].numerator;
+ colorTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
+ env, TAG_COLORMATRIX1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
+ BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
+ int32_t colorTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ colorTransform2[ctr++] = entry2.data.r[i].numerator;
+ colorTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
+ env, TAG_COLORMATRIX2);
+ }
+ }
+
+ {
+ // Set calibration transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
+
+ int32_t calibrationTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
+ calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
+ BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
+ int32_t calibrationTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
+ calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
+ }
+ }
+
+ {
+ // Set forward transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
+ BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
+
+ int32_t forwardTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ forwardTransform1[ctr++] = entry1.data.r[i].numerator;
+ forwardTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
+ BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
+ int32_t forwardTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ forwardTransform2[ctr++] = entry2.data.r[i].numerator;
+ forwardTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
+ }
+ }
+
+ {
+ // Set camera neutral
+ camera_metadata_entry entry =
+ results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
+ BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
+ uint32_t cameraNeutral[entry.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry.count; ++i) {
+ cameraNeutral[ctr++] =
+ static_cast<uint32_t>(entry.data.r[i].numerator);
+ cameraNeutral[ctr++] =
+ static_cast<uint32_t>(entry.data.r[i].denominator);
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
+ TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
+ }
+
+ {
+ // Setup data strips
+ // TODO: Switch to tiled implementation.
+ uint32_t offset = 0;
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
+ TAG_STRIPOFFSETS);
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
+ TAG_ROWSPERSTRIP);
+
+ uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
+ bitsPerByte;
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
+ TAG_STRIPBYTECOUNTS);
+ }
+
+ {
+ // Setup default crop + crop origin tags
+ uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
+ uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
+ if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
+ uint32_t defaultCropOrigin[] = {margin, margin};
+ uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
+ TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
+ TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
+ }
+ }
+
+ {
+ // Setup unique camera model tag
+ char model[PROPERTY_VALUE_MAX];
+ property_get("ro.product.model", model, "");
+
+ char manufacturer[PROPERTY_VALUE_MAX];
+ property_get("ro.product.manufacturer", manufacturer, "");
+
+ char brand[PROPERTY_VALUE_MAX];
+ property_get("ro.product.brand", brand, "");
+
+ String8 cameraModel(model);
+ cameraModel += "-";
+ cameraModel += manufacturer;
+ cameraModel += "-";
+ cameraModel += brand;
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
+ reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
+ TAG_UNIQUECAMERAMODEL);
+ }
+
+ {
+ // Setup opcode List 2
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+ BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
+ uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
+ uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
+
+ camera_metadata_entry entry2 =
+ results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+ BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
+ if (entry2.count == lsmWidth * lsmHeight * 4) {
+
+ OpcodeListBuilder builder;
+ status_t err = builder.addGainMapsForMetadata(lsmWidth,
+ lsmHeight,
+ 0,
+ 0,
+ imageHeight,
+ imageWidth,
+ opcodeCfaLayout,
+ entry2.data.f);
+ if (err == OK) {
+ size_t listSize = builder.getSize();
+ uint8_t opcodeListBuf[listSize];
+ err = builder.buildOpList(opcodeListBuf);
+ if (err == OK) {
+ BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
+ TIFF_IFD_0), env, TAG_OPCODELIST2);
+ } else {
+ ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
+ }
+ } else {
+ ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to add lens shading map.");
+ }
+ } else {
+ ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
+ }
+ }
+
+ DngCreator_setCreator(env, thiz, writer);
+}
+
+static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ DngCreator_setCreator(env, thiz, NULL);
+}
+
+static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
+ jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
+ jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
+}
+
+static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
+ jint height, jobject inBuffer, jint rowStride, jint pixStride) {
+ ALOGV("%s:", __FUNCTION__);
+
+ sp<JniOutputStream> out = new JniOutputStream(env, outStream);
+ if(env->ExceptionCheck()) {
+ ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
+ return;
+ }
+
+ uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
+ if (pixelBytes == NULL) {
+ ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
+ return;
+ }
+
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ if (writer == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "Write called with uninitialized DngCreator");
+ return;
+ }
+ // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
+ uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
+ uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
+ if (metadataWidth != width) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+ "Metadata width %d doesn't match image width %d", metadataWidth, width);
+ return;
+ }
+
+ if (metadataHeight != height) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+ "Metadata height %d doesn't match image height %d", metadataHeight, height);
+ return;
+ }
+
+ uint32_t stripOffset = writer->getTotalSize();
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
+ TAG_STRIPOFFSETS);
+
+ if (writer->write(out.get()) != OK) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write metadata");
+ }
+ return;
+ }
+
+ size_t fullSize = rowStride * height;
+ jlong capacity = env->GetDirectBufferCapacity(inBuffer);
+ if (capacity < 0 || fullSize > capacity) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid size %d for Image, size given in metadata is %d at current stride",
+ capacity, fullSize);
+ return;
+ }
+
+ if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
+ if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ } else if (pixStride == BYTES_PER_SAMPLE) {
+ for (size_t i = 0; i < height; ++i) {
+ if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
+ env->ExceptionCheck()) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ }
+ } else {
+ for (size_t i = 0; i < height; ++i) {
+ for (size_t j = 0; j < width; ++j) {
+ if (out->write(pixelBytes, i * rowStride + j * pixStride,
+ BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
+ if (env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ }
+ }
+ }
+
+}
+
+static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
+ jobject rawBuffer, jlong offset) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
+}
+
+static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
+ jobject inStream, jlong offset) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
+}
+
+} /*extern "C" */
+
+static JNINativeMethod gDngCreatorMethods[] = {
+ {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
+ {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
+ "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
+ {"nativeDestroy", "()V", (void*) DngCreator_destroy},
+ {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
+ {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
+ (void*) DngCreator_nativeSetThumbnailBitmap},
+ {"nativeSetThumbnailImage",
+ "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
+ (void*) DngCreator_nativeSetThumbnailImage},
+ {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
+ (void*) DngCreator_nativeWriteImage},
+ {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
+ (void*) DngCreator_nativeWriteByteBuffer},
+ {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
+ (void*) DngCreator_nativeWriteInputStream},
+};
+
+int register_android_media_DngCreator(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
+}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6f42057..9d03cc3 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -884,6 +884,7 @@ static int register_android_media_MediaPlayer(JNIEnv *env)
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
+extern int register_android_media_DngCreator(JNIEnv *env);
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
@@ -913,6 +914,11 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
}
assert(env != NULL);
+ if (register_android_media_DngCreator(env) < 0) {
+ ALOGE("ERROR: ImageReader native registration failed");
+ goto bail;
+ }
+
if (register_android_media_ImageReader(env) < 0) {
ALOGE("ERROR: ImageReader native registration failed");
goto bail;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 89886ef..8a7e642 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -23,10 +23,10 @@ import android.hardware.ICameraServiceListener;
import android.hardware.IProCameraCallbacks;
import android.hardware.IProCameraUser;
import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureResultExtras;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.utils.BinderHolder;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.os.Binder;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 74ce997..7b2e7dd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -21,10 +21,10 @@ import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResultExtras;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.CaptureResultExtras;
import android.hardware.camera2.utils.BinderHolder;
import android.media.Image;
import android.media.ImageReader;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 5ab586f..a77b647 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -18,6 +18,7 @@ package com.android.mediaframeworktest.unit;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Range;
+import android.util.Rational;
import android.util.SizeF;
import android.graphics.ImageFormat;
import android.graphics.Point;
@@ -26,15 +27,14 @@ import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.ColorSpaceTransform;
-import android.hardware.camera2.Face;
-import android.hardware.camera2.MeteringRectangle;
-import android.hardware.camera2.Rational;
-import android.hardware.camera2.RggbChannelVector;
-import android.hardware.camera2.Size;
+import android.util.Size;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.Face;
+import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.ReprocessFormatsMap;
+import android.hardware.camera2.params.RggbChannelVector;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.params.StreamConfigurationMap;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
index 9621f92..18c0d3e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
@@ -17,7 +17,7 @@
package com.android.mediaframeworktest.unit;
import android.test.suitebuilder.annotation.SmallTest;
-import android.hardware.camera2.Rational;
+import android.util.Rational;
/**
* <pre>