summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioAttributes.java6
-rw-r--r--media/java/android/media/AudioFormat.java64
-rw-r--r--media/java/android/media/AudioManager.java101
-rw-r--r--media/java/android/media/AudioRecord.java37
-rw-r--r--media/java/android/media/AudioSystem.java8
-rw-r--r--media/java/android/media/CamcorderProfile.java75
-rw-r--r--media/java/android/media/ClosedCaptionRenderer.java6
-rw-r--r--media/java/android/media/EncoderCapabilities.java9
-rw-r--r--media/java/android/media/IAudioService.aidl17
-rw-r--r--media/java/android/media/IRemoteControlClient.aidl5
-rw-r--r--media/java/android/media/MediaCodecInfo.java7
-rw-r--r--media/java/android/media/MediaFile.java36
-rw-r--r--media/java/android/media/MediaHTTPConnection.java55
-rw-r--r--media/java/android/media/MediaMetadataEditor.java4
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java10
-rw-r--r--media/java/android/media/MediaPlayer.java17
-rw-r--r--media/java/android/media/MediaRecorder.java26
-rw-r--r--media/java/android/media/MediaScanner.java176
-rw-r--r--media/java/android/media/RemoteControlClient.java203
-rw-r--r--media/java/android/media/RemoteController.java186
-rw-r--r--media/java/android/media/Ringtone.java21
-rw-r--r--media/java/android/media/RingtoneManager.java117
-rw-r--r--media/java/android/media/SRTRenderer.java4
-rw-r--r--media/java/android/media/ToneGenerator.java7
-rw-r--r--media/java/android/media/session/ISession.aidl4
-rw-r--r--media/java/android/media/session/ISessionCallback.aidl3
-rw-r--r--media/java/android/media/session/ISessionController.aidl3
-rw-r--r--media/java/android/media/session/ISessionControllerCallback.aidl4
-rw-r--r--media/java/android/media/session/MediaController.java114
-rw-r--r--media/java/android/media/session/MediaSession.java141
-rw-r--r--media/java/android/media/session/MediaSessionLegacyHelper.java21
-rw-r--r--media/java/android/mtp/MtpServer.java5
-rw-r--r--media/jni/android_media_MediaPlayer.cpp202
-rw-r--r--media/jni/android_media_MediaProfiles.cpp17
-rw-r--r--media/jni/android_media_MediaRecorder.cpp14
-rw-r--r--media/jni/android_mtp_MtpServer.cpp13
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp9
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java12
39 files changed, 1670 insertions, 90 deletions
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 0f1be6b..e92f294 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -671,15 +671,15 @@ public final class AudioAttributes implements Parcelable {
case USAGE_VOICE_COMMUNICATION:
return new String("USAGE_VOICE_COMMUNICATION");
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
- return new String("USAGE_VOICE_COMMUNICATION");
+ return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING");
case USAGE_ALARM:
return new String("USAGE_ALARM");
case USAGE_NOTIFICATION:
return new String("USAGE_NOTIFICATION");
case USAGE_NOTIFICATION_RINGTONE:
- return new String("USAGE_NOTIFICATION");
+ return new String("USAGE_NOTIFICATION_RINGTONE");
case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
- return new String("USAGE_NOTIFICATION");
+ return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST");
case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT");
case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index bde3d19..962316b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -252,6 +252,30 @@ public class AudioFormat {
* */
public static final int ENCODING_AAC_HE_V2 = 12;
+ /** Audio data format: AMRNB
+ * @hide
+ * */
+ public static final int ENCODING_AMRNB = 100;
+ /** Audio data format: AMRWB
+ * @hide
+ * */
+ public static final int ENCODING_AMRWB = 101;
+ /** Audio data format: EVRC
+ * @hide
+ * */
+ public static final int ENCODING_EVRC = 102;
+ /** Audio data format: EVRCB
+ * @hide
+ * */
+ public static final int ENCODING_EVRCB = 103;
+ /** Audio data format: EVRCWB
+ * @hide
+ * */
+ public static final int ENCODING_EVRCWB = 104;
+ /** Audio data format: EVRCNW
+ * @hide
+ * */
+ public static final int ENCODING_EVRCNW = 105;
/** Invalid audio channel configuration */
/** @deprecated Use {@link #CHANNEL_INVALID} instead. */
@Deprecated public static final int CHANNEL_CONFIGURATION_INVALID = 0;
@@ -409,6 +433,11 @@ public class AudioFormat {
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
/** @hide */
public static final int CHANNEL_IN_FRONT_BACK = CHANNEL_IN_FRONT | CHANNEL_IN_BACK;
+ /** @hide */
+ public static final int CHANNEL_IN_5POINT1 = (CHANNEL_IN_LEFT |
+ CHANNEL_IN_RIGHT | CHANNEL_IN_FRONT | CHANNEL_IN_BACK |
+ CHANNEL_IN_LEFT_PROCESSED | CHANNEL_IN_RIGHT_PROCESSED);
+
// CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL
/** @hide */
@@ -422,6 +451,15 @@ public class AudioFormat {
return 2;
case ENCODING_PCM_FLOAT:
return 4;
+ case ENCODING_AMRNB:
+ return 32;
+ case ENCODING_AMRWB:
+ return 61;
+ case ENCODING_EVRC:
+ case ENCODING_EVRCB:
+ case ENCODING_EVRCWB:
+ case ENCODING_EVRCNW:
+ return 23;
case ENCODING_INVALID:
default:
throw new IllegalArgumentException("Bad audio format " + audioFormat);
@@ -443,6 +481,12 @@ public class AudioFormat {
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
+ case ENCODING_AMRNB:
+ case ENCODING_AMRWB:
+ case ENCODING_EVRC:
+ case ENCODING_EVRCB:
+ case ENCODING_EVRCWB:
+ case ENCODING_EVRCNW:
return true;
default:
return false;
@@ -483,6 +527,12 @@ public class AudioFormat {
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
+ case ENCODING_AMRNB:
+ case ENCODING_AMRWB:
+ case ENCODING_EVRC:
+ case ENCODING_EVRCB:
+ case ENCODING_EVRCWB:
+ case ENCODING_EVRCNW:
return false;
case ENCODING_INVALID:
default:
@@ -715,6 +765,12 @@ public class AudioFormat {
case ENCODING_E_AC3:
case ENCODING_DTS:
case ENCODING_DTS_HD:
+ case ENCODING_AMRNB:
+ case ENCODING_AMRWB:
+ case ENCODING_EVRC:
+ case ENCODING_EVRCB:
+ case ENCODING_EVRCWB:
+ case ENCODING_EVRCNW:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -859,7 +915,13 @@ public class AudioFormat {
ENCODING_AC3,
ENCODING_E_AC3,
ENCODING_DTS,
- ENCODING_DTS_HD
+ ENCODING_DTS_HD,
+ ENCODING_AMRNB,
+ ENCODING_AMRWB,
+ ENCODING_EVRC,
+ ENCODING_EVRCB,
+ ENCODING_EVRCWB,
+ ENCODING_EVRCNW
})
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 875e716..fc917f6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -67,7 +67,6 @@ public class AudioManager {
private final boolean mUseFixedVolume;
private static String TAG = "AudioManager";
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
-
/**
* Broadcast intent, a hint for applications that audio is about to become
* 'noisy' due to a change in audio outputs. For example, this intent may
@@ -310,6 +309,32 @@ public class AudioManager {
*/
public static final String EXTRA_ENCODINGS = "android.media.extra.ENCODINGS";
+ /**
+ * @hide Broadcast intent when RemoteControlClient list is updated.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String RCC_CHANGED_ACTION =
+ "org.codeaurora.bluetooth.RCC_CHANGED_ACTION";
+
+ /**
+ * @hide Used for sharing the calling package name
+ */
+ public static final String EXTRA_CALLING_PACKAGE_NAME =
+ "org.codeaurora.bluetooth.EXTRA_CALLING_PACKAGE_NAME";
+
+ /**
+ * @hide Used for sharing the focus changed value
+ */
+ public static final String EXTRA_FOCUS_CHANGED_VALUE =
+ "org.codeaurora.bluetooth.EXTRA_FOCUS_CHANGED_VALUE";
+
+ /**
+ * @hide Used for sharing the availability changed value
+ */
+ public static final String EXTRA_AVAILABLITY_CHANGED_VALUE =
+ "org.codeaurora.bluetooth.EXTRA_AVAILABLITY_CHANGED_VALUE";
+
+
/** The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/** The audio stream for system sounds */
@@ -2481,6 +2506,7 @@ public class AudioManager {
//====================================================================
// Remote Control
+
/**
* Register a component to be the sole receiver of MEDIA_BUTTON intents.
* @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
@@ -2499,6 +2525,7 @@ public class AudioManager {
"receiver and context package names don't match");
return;
}
+
// construct a PendingIntent for the media button and register it
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
// the associated intent will be handled by the component being registered
@@ -2508,6 +2535,7 @@ public class AudioManager {
registerMediaButtonIntent(pi, eventReceiver);
}
+
/**
* Register a component to be the sole receiver of MEDIA_BUTTON intents. This is like
* {@link #registerMediaButtonEventReceiver(android.content.ComponentName)}, but allows
@@ -2637,6 +2665,13 @@ public class AudioManager {
return false;
}
rctlr.startListeningToSessions();
+ IAudioService service = getService();
+ try {
+ service.updateRemoteControllerOnExistingMediaPlayers();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in calling Audio service interface" +
+ "updateRemoteControllerOnExistingMediaPlayers() due to " + e);
+ }
return true;
}
@@ -2660,6 +2695,24 @@ public class AudioManager {
/**
* @hide
+ */
+ public void updateMediaPlayerList(String packageName, boolean toAdd) {
+ IAudioService service = getService();
+ try {
+ if (toAdd) {
+ Log.d(TAG, "updateMediaPlayerList: Add RCC " + packageName + " to List");
+ service.addMediaPlayerAndUpdateRemoteController(packageName);
+ } else {
+ Log.d(TAG, "updateMediaPlayerList: Remove RCC " + packageName + " from List");
+ service.removeMediaPlayerAndUpdateRemoteController(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while executing updateMediaPlayerList: " + e);
+ }
+ }
+
+ /**
+ * @hide
* Registers a remote control display that will be sent information by remote control clients.
* Use this method if your IRemoteControlDisplay is not going to display artwork, otherwise
* use {@link #registerRemoteControlDisplay(IRemoteControlDisplay, int, int)} to pass the
@@ -2811,6 +2864,52 @@ public class AudioManager {
}
}
+ /**
+ * @hide
+ * Request the user of a RemoteControlClient to play the requested item.
+ * @param generationId the RemoteControlClient generation counter for which this request is
+ * issued.
+ * @param uid uid of the song to be played.
+ * @scope scope of the file system to use
+ */
+ public void setRemoteControlClientPlayItem(long uid, int scope) {
+ IAudioService service = getService();
+ try {
+ service.setRemoteControlClientPlayItem(uid, scope);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setRemoteControlClientPlayItem(" +
+ uid + ", " + scope + ")", e);
+ }
+ }
+
+ /**
+ * @hide
+ * Request the user of a RemoteControlClient to provide with the now playing list entries.
+ * @param generationId the RemoteControlClient generation counter for which this request is
+ * issued.
+ */
+ public void getRemoteControlClientNowPlayingEntries() {
+ IAudioService service = getService();
+ try {
+ service.getRemoteControlClientNowPlayingEntries();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in getRemoteControlClientNowPlayingEntries(" + ")", e);
+ }
+ }
+
+ /**
+ * @hide
+ * Request the user of a RemoteControlClient to set the music player as current browsed player.
+ */
+ public void setRemoteControlClientBrowsedPlayer() {
+ Log.d(TAG, "setRemoteControlClientBrowsedPlayer: ");
+ IAudioService service = getService();
+ try {
+ service.setRemoteControlClientBrowsedPlayer();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setRemoteControlClientBrowsedPlayer(" + ")", e);
+ }
+ }
/**
* @hide
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 974b62e..c96690b 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -643,6 +643,12 @@ public class AudioRecord
case AudioFormat.ENCODING_PCM_FLOAT:
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
+ case AudioFormat.ENCODING_AMRNB:
+ case AudioFormat.ENCODING_AMRWB:
+ case AudioFormat.ENCODING_EVRC:
+ case AudioFormat.ENCODING_EVRCB:
+ case AudioFormat.ENCODING_EVRCWB:
+ case AudioFormat.ENCODING_EVRCNW:
mAudioFormat = audioFormat;
break;
default:
@@ -845,6 +851,9 @@ public class AudioRecord
case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
channelCount = 2;
break;
+ case AudioFormat.CHANNEL_IN_5POINT1:
+ channelCount = 6;
+ break;
case AudioFormat.CHANNEL_INVALID:
default:
loge("getMinBufferSize(): Invalid channel configuration.");
@@ -881,6 +890,7 @@ public class AudioRecord
*/
public void startRecording()
throws IllegalStateException {
+ android.util.SeempLog.record(70);
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("startRecording() called on an "
+ "uninitialized AudioRecord.");
@@ -893,6 +903,11 @@ public class AudioRecord
mRecordingState = RECORDSTATE_RECORDING;
}
}
+
+ if (getRecordingState() == RECORDSTATE_RECORDING &&
+ getAudioSource() == MediaRecorder.AudioSource.HOTWORD) {
+ handleHotwordInput(true);
+ }
}
/**
@@ -904,6 +919,7 @@ public class AudioRecord
*/
public void startRecording(MediaSyncEvent syncEvent)
throws IllegalStateException {
+ android.util.SeempLog.record(70);
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("startRecording() called on an "
+ "uninitialized AudioRecord.");
@@ -934,6 +950,10 @@ public class AudioRecord
native_stop();
mRecordingState = RECORDSTATE_STOPPED;
}
+
+ if (getAudioSource() == MediaRecorder.AudioSource.HOTWORD) {
+ handleHotwordInput(false);
+ }
}
private final IBinder mICallBack = new Binder();
@@ -950,6 +970,23 @@ public class AudioRecord
}
}
+ private void handleHotwordInput(boolean listening) {
+ final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE);
+ final IAudioService ias = IAudioService.Stub.asInterface(b);
+ try {
+ // If the caller tries to start recording with the HOTWORD input
+ // before AUDIO_SERVICE has started, IAudioService may not be available.
+ if (ias != null) {
+ ias.handleHotwordInput(listening);
+ } else {
+ Log.e(TAG, "Error talking to AudioService when handling hotword input, "
+ + "AudioService unavailable");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to AudioService when handling hotword input.", e);
+ }
+ }
+
//---------------------------------------------------------
// Audio data supply
//--------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index c59d1c7..927cd87 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -319,7 +319,7 @@ public class AudioSystem
public static final int DEVICE_OUT_AUX_LINE = 0x200000;
public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000;
public static final int DEVICE_OUT_IP = 0x800000;
-
+ public static final int DEVICE_OUT_PROXY = 0x1000000;
public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
public static final int DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE |
@@ -346,6 +346,7 @@ public class AudioSystem
DEVICE_OUT_AUX_LINE |
DEVICE_OUT_SPEAKER_SAFE |
DEVICE_OUT_IP |
+ DEVICE_OUT_PROXY |
DEVICE_OUT_DEFAULT);
public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
@@ -385,6 +386,7 @@ public class AudioSystem
public static final int DEVICE_IN_BLUETOOTH_A2DP = DEVICE_BIT_IN | 0x20000;
public static final int DEVICE_IN_LOOPBACK = DEVICE_BIT_IN | 0x40000;
public static final int DEVICE_IN_IP = DEVICE_BIT_IN | 0x80000;
+ public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000;
public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
public static final int DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION |
@@ -407,6 +409,7 @@ public class AudioSystem
DEVICE_IN_BLUETOOTH_A2DP |
DEVICE_IN_LOOPBACK |
DEVICE_IN_IP |
+ DEVICE_IN_PROXY |
DEVICE_IN_DEFAULT);
public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET;
public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY |
@@ -442,6 +445,7 @@ public class AudioSystem
public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
public static final String DEVICE_OUT_IP_NAME = "ip";
+ public static final String DEVICE_OUT_PROXY_NAME = "proxy";
public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
@@ -515,6 +519,8 @@ public class AudioSystem
return DEVICE_OUT_SPEAKER_SAFE_NAME;
case DEVICE_OUT_IP:
return DEVICE_OUT_IP_NAME;
+ case DEVICE_OUT_PROXY:
+ return DEVICE_OUT_PROXY_NAME;
case DEVICE_OUT_DEFAULT:
default:
return Integer.toString(device);
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index d303a2e..6785670 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -206,6 +206,77 @@ public class CamcorderProfile
private static final int QUALITY_HIGH_SPEED_LIST_START = QUALITY_HIGH_SPEED_LOW;
private static final int QUALITY_HIGH_SPEED_LIST_END = QUALITY_HIGH_SPEED_2160P;
+ // Vendor-specific quality profiles
+ /**
+ * Quality level corresponding to the VGA (640 x 480) resolution.
+ * @hide
+ */
+ public static final int QUALITY_VGA = 10000;
+
+ /**
+ * Quality level corresponding to the 4k-DCI (4096 x 2160) resolution.
+ * @hide
+ */
+ public static final int QUALITY_4KDCI = 10001;
+
+ /**
+ * Time lapse quality level corresponding to the VGA (640 x 480) resolution.
+ * @hide
+ */
+ public static final int QUALITY_TIME_LAPSE_VGA = 10002;
+
+ /**
+ * Time lapse quality level corresponding to the 4k-DCI (4096 x 2160) resolution.
+ * @hide
+ */
+ public static final int QUALITY_TIME_LAPSE_4KDCI = 10003;
+
+ /**
+ * High speed ( >= 100fps) quality level corresponding to the CIF (352 x 288)
+ * @hide
+ */
+ public static final int QUALITY_HIGH_SPEED_CIF = 10004;
+
+ /**
+ * High speed ( >= 100fps) quality level corresponding to the VGA (640 x 480)
+ * @hide
+ */
+ public static final int QUALITY_HIGH_SPEED_VGA = 10005;
+
+ /**
+ * High speed ( >= 100fps) quality level corresponding to the 4K-DCI (4096 x 2160)
+ * @hide
+ */
+ public static final int QUALITY_HIGH_SPEED_4KDCI = 10006;
+
+ /**
+ * Quality level corresponding to QHD resolution
+ * @hide
+ */
+ public static final int QUALITY_QHD = 10007;
+
+ /**
+ * Quality level corresponding to 2K resolution
+ * @hide
+ */
+ public static final int QUALITY_2k = 10008;
+
+ /**
+ * Time lapse quality level corresponding to the QHD resolution.
+ * @hide
+ */
+ public static final int QUALITY_TIME_LAPSE_QHD = 10009;
+
+ /**
+ * Time lapse quality level corresponding to the 2K resolution.
+ * @hide
+ */
+ public static final int QUALITY_TIME_LAPSE_2k = 10010;
+
+ // Start and end of vendor quality list
+ private static final int QUALITY_VENDOR_LIST_START = QUALITY_VGA;
+ private static final int QUALITY_VENDOR_LIST_END = QUALITY_TIME_LAPSE_2k;
+
/**
* Default recording duration in seconds before the session is terminated.
* This is useful for applications like MMS has limited file size requirement.
@@ -391,7 +462,9 @@ public class CamcorderProfile
(quality >= QUALITY_TIME_LAPSE_LIST_START &&
quality <= QUALITY_TIME_LAPSE_LIST_END) ||
(quality >= QUALITY_HIGH_SPEED_LIST_START &&
- quality <= QUALITY_HIGH_SPEED_LIST_END))) {
+ quality <= QUALITY_HIGH_SPEED_LIST_END) ||
+ (quality >= QUALITY_VENDOR_LIST_START &&
+ quality <= QUALITY_VENDOR_LIST_END))) {
String errMessage = "Unsupported quality level: " + quality;
throw new IllegalArgumentException(errMessage);
}
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index 8403c1c..2cf754e 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -609,8 +609,10 @@ class CCParser {
if (mLines[mRow] != null) {
for (int i = 0; i < mCol; i++) {
if (mLines[mRow].charAt(i) != TS) {
- for (int j = mCol; j < mLines[mRow].length(); j++) {
- mLines[j].setCharAt(j, TS);
+ for (int j = mCol; j < mLines[mRow].length() && j < mLines.length; j++) {
+ if (mLines[j] != null){
+ mLines[j].setCharAt(j, TS);
+ }
}
return;
}
diff --git a/media/java/android/media/EncoderCapabilities.java b/media/java/android/media/EncoderCapabilities.java
index 332e360..d612c20 100644
--- a/media/java/android/media/EncoderCapabilities.java
+++ b/media/java/android/media/EncoderCapabilities.java
@@ -47,13 +47,17 @@ public class EncoderCapabilities
public final int mMinFrameRate, mMaxFrameRate; // min and max frame rate (fps)
public final int mMinFrameWidth, mMaxFrameWidth; // min and max frame width (pixel)
public final int mMinFrameHeight, mMaxFrameHeight; // minn and max frame height (pixel)
+ public final int mMaxHFRFrameWidth, mMaxHFRFrameHeight; // max HFR size (pixel)
+ public final int mMaxHFRMode; // max HFR mode
// Private constructor called by JNI
private VideoEncoderCap(int codec,
int minBitRate, int maxBitRate,
int minFrameRate, int maxFrameRate,
int minFrameWidth, int maxFrameWidth,
- int minFrameHeight, int maxFrameHeight) {
+ int minFrameHeight, int maxFrameHeight,
+ int maxHFRFrameWidth, int maxHFRFrameHeight,
+ int maxHFRMode) {
mCodec = codec;
mMinBitRate = minBitRate;
mMaxBitRate = maxBitRate;
@@ -63,6 +67,9 @@ public class EncoderCapabilities
mMaxFrameWidth = maxFrameWidth;
mMinFrameHeight = minFrameHeight;
mMaxFrameHeight = maxFrameHeight;
+ mMaxHFRFrameWidth = maxHFRFrameWidth;
+ mMaxHFRFrameHeight = maxHFRFrameHeight;
+ mMaxHFRMode = maxHFRMode;
}
};
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8aebe11..cdcd83c 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -215,4 +215,21 @@ interface IAudioService {
int setFocusPropertiesForPolicy(int duckingBehavior, in IAudioPolicyCallback pcb);
void setVolumePolicy(in VolumePolicy policy);
+
+ void setRemoteControlClientBrowsedPlayer();
+
+ void getRemoteControlClientNowPlayingEntries();
+
+ void setRemoteControlClientPlayItem(long uid, int scope);
+
+ void updateRemoteControllerOnExistingMediaPlayers();
+
+ void addMediaPlayerAndUpdateRemoteController(String packageName);
+
+ void removeMediaPlayerAndUpdateRemoteController(String packageName);
+
+ void handleHotwordInput(boolean listening);
+
+ String getCurrentHotwordInputPackageName();
+
}
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
index aa142d6..d8e73c8 100644
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -52,11 +52,14 @@ oneway interface IRemoteControlClient
*/
void setCurrentClientGenerationId(int clientGeneration);
- void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h);
+ void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h);
void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync);
void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled);
void seekTo(int clientGeneration, long timeMs);
void updateMetadata(int clientGeneration, int key, in Rating value);
+ void setPlayItem(int scope, long uid);
+ void setBrowsedPlayer();
+ void getNowPlayingEntries();
} \ No newline at end of file
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4101935..957ba16 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -589,6 +589,13 @@ public final class MediaCodecInfo {
return mDefaultFormat;
}
+ /* Return the capabilities info, so the app can query custom settings
+ * like for VT. */
+ /** @hide */
+ public MediaFormat getCapabilitiesInfoFormat() {
+ return mCapabilitiesInfo;
+ }
+
/**
* Returns the mime type for which this codec-capability object was created.
*/
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 526656a..c6de463 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -46,6 +46,18 @@ public class MediaFile {
private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3;
private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC;
+ // More audio file types
+ public static final int FILE_TYPE_DTS = 300;
+ public static final int FILE_TYPE_3GPA = 301;
+ public static final int FILE_TYPE_AC3 = 302;
+ public static final int FILE_TYPE_QCP = 303;
+ public static final int FILE_TYPE_PCM = 304;
+ public static final int FILE_TYPE_EC3 = 305;
+ public static final int FILE_TYPE_AIFF = 306;
+ public static final int FILE_TYPE_APE = 307;
+ private static final int FIRST_AUDIO_FILE_TYPE_EXT = FILE_TYPE_DTS;
+ private static final int LAST_AUDIO_FILE_TYPE_EXT = FILE_TYPE_APE;
+
// MIDI file types
public static final int FILE_TYPE_MID = 11;
public static final int FILE_TYPE_SMF = 12;
@@ -69,8 +81,10 @@ public class MediaFile {
// More video file types
public static final int FILE_TYPE_MP2PS = 200;
+ public static final int FILE_TYPE_DIVX = 201;
+ public static final int FILE_TYPE_FLV = 202;
private static final int FIRST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS;
- private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS;
+ private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_FLV;
// Image file types
public static final int FILE_TYPE_JPEG = 31;
@@ -87,14 +101,16 @@ public class MediaFile {
public static final int FILE_TYPE_PLS = 42;
public static final int FILE_TYPE_WPL = 43;
public static final int FILE_TYPE_HTTPLIVE = 44;
+ public static final int FILE_TYPE_DASH = 45;
private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U;
- private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_HTTPLIVE;
+ private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_DASH;
// Drm file types
public static final int FILE_TYPE_FL = 51;
+ public static final int FILE_TYPE_SD = 52;
private static final int FIRST_DRM_FILE_TYPE = FILE_TYPE_FL;
- private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_FL;
+ private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_SD;
// Other popular file types
public static final int FILE_TYPE_TEXT = 100;
@@ -228,6 +244,7 @@ public class MediaFile {
addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/x-mpegurl");
addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl");
+ addFileType("DCF", FILE_TYPE_SD, "application/vnd.oma.drm.content");
addFileType("TXT", FILE_TYPE_TEXT, "text/plain", MtpConstants.FORMAT_TEXT);
addFileType("HTM", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
@@ -240,13 +257,24 @@ public class MediaFile {
addFileType("ZIP", FILE_TYPE_ZIP, "application/zip");
addFileType("MPG", FILE_TYPE_MP2PS, "video/mp2p");
addFileType("MPEG", FILE_TYPE_MP2PS, "video/mp2p");
+ addFileType("DIVX", FILE_TYPE_DIVX, "video/divx");
+ addFileType("FLV", FILE_TYPE_FLV, "video/flv");
+ addFileType("MPD", FILE_TYPE_DASH, "application/dash+xml");
+ addFileType("QCP", FILE_TYPE_QCP, "audio/qcelp");
+ addFileType("AC3", FILE_TYPE_AC3, "audio/ac3");
+ addFileType("EC3", FILE_TYPE_EC3, "audio/eac3");
+ addFileType("AIF", FILE_TYPE_AIFF, "audio/x-aiff");
+ addFileType("AIFF", FILE_TYPE_AIFF, "audio/x-aiff");
+ addFileType("APE", FILE_TYPE_APE, "audio/x-ape");
}
public static boolean isAudioFileType(int fileType) {
return ((fileType >= FIRST_AUDIO_FILE_TYPE &&
fileType <= LAST_AUDIO_FILE_TYPE) ||
(fileType >= FIRST_MIDI_FILE_TYPE &&
- fileType <= LAST_MIDI_FILE_TYPE));
+ fileType <= LAST_MIDI_FILE_TYPE) ||
+ (fileType >= FIRST_AUDIO_FILE_TYPE_EXT &&
+ fileType <= LAST_AUDIO_FILE_TYPE_EXT));
}
public static boolean isVideoFileType(int fileType) {
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index d6bf421..59eaeef 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -19,6 +19,7 @@ package android.media;
import android.net.NetworkUtils;
import android.os.IBinder;
import android.os.StrictMode;
+import android.os.SystemProperties;
import android.util.Log;
import java.io.BufferedInputStream;
@@ -34,8 +35,14 @@ import java.net.NoRouteToHostException;
import java.net.ProtocolException;
import java.net.UnknownServiceException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+import java.net.SocketAddress;
+
import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
/** @hide */
@@ -48,10 +55,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
private long mCurrentOffset = -1;
private URL mURL = null;
+ private int mProxyPort = 0;
+ private String mProxyIP;
private Map<String, String> mHeaders = null;
private HttpURLConnection mConnection = null;
private long mTotalSize = -1;
private InputStream mInputStream = null;
+ private List<String> mCookies = null;
+ private boolean mIsCookieUpdated = false;
private boolean mAllowCrossDomainRedirect = true;
private boolean mAllowCrossProtocolRedirect = true;
@@ -97,10 +108,21 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
/* returns true iff header is internal */
private boolean filterOutInternalHeaders(String key, String val) {
+ Log.d(TAG, "filterOutInternalHeaders: key=" + key + ", val=" + val);
if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
mAllowCrossDomainRedirect = parseBoolean(val);
// cross-protocol redirects are also controlled by this flag
mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
+ } else if ("use-proxy".equalsIgnoreCase(key)) {
+ Log.d(TAG, "filterOutInternalHeaders use-proxy " + val);
+ int colonPos = val.indexOf(":");
+ if (colonPos > 0) {
+ mProxyIP = new String((val.substring(0, colonPos)).trim());
+ mProxyPort = Integer.parseInt(val.substring(colonPos + 1));
+ Log.d(TAG, "sta-proxy-ip " + mProxyIP + " port " + mProxyPort);
+ }
+ } else if ("Cookie".equalsIgnoreCase(key) && mIsCookieUpdated) {
+ Log.d(TAG, "filterOutInternalHeaders: Cookie");
} else {
return false;
}
@@ -180,10 +202,19 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
boolean noProxy = isLocalHost(url);
while (true) {
- if (noProxy) {
- mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
+
+ Log.d(TAG, "proxy " + mProxyIP +" port "+ mProxyPort);
+ if (mProxyPort > 0) {
+ SocketAddress socketAddr = new InetSocketAddress(mProxyIP, mProxyPort);
+ java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, socketAddr);
+ mConnection = (HttpURLConnection) url.openConnection(proxy);
+ Log.d(TAG, "connection initialized with proxy");
} else {
- mConnection = (HttpURLConnection)url.openConnection();
+ if (noProxy) {
+ mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
+ } else {
+ mConnection = (HttpURLConnection)url.openConnection();
+ }
}
mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
@@ -197,6 +228,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
}
}
+ if (mIsCookieUpdated) {
+ if (VERBOSE)
+ Log.d(TAG, "add Cookie in the request");
+ for (String cookie : mCookies) {
+ mConnection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
+ }
+ }
+
if (offset > 0) {
mConnection.setRequestProperty(
"Range", "bytes=" + offset + "-");
@@ -283,6 +322,16 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
throw new IOException();
} else {
mTotalSize = mConnection.getContentLength();
+ if (mConnection.getHeaderFields().containsKey("Set-Cookie")) {
+ mIsCookieUpdated = SystemProperties.getBoolean(
+ "persist.media.cookie.cust", false);
+ mCookies = mConnection.getHeaderFields().get("Set-Cookie");
+ if (VERBOSE) {
+ for (String cookie : mCookies) {
+ Log.d(TAG, "get Cookie" + cookie);
+ }
+ }
+ }
}
if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
diff --git a/media/java/android/media/MediaMetadataEditor.java b/media/java/android/media/MediaMetadataEditor.java
index 566b93f..eeb8fe2 100644
--- a/media/java/android/media/MediaMetadataEditor.java
+++ b/media/java/android/media/MediaMetadataEditor.java
@@ -439,7 +439,7 @@ import android.util.SparseIntArray;
protected static final SparseIntArray METADATA_KEYS_TYPE;
static {
- METADATA_KEYS_TYPE = new SparseIntArray(17);
+ METADATA_KEYS_TYPE = new SparseIntArray(18);
// NOTE: if adding to the list below, make sure you increment the array initialization size
// keys with long values
METADATA_KEYS_TYPE.put(
@@ -465,5 +465,7 @@ import android.util.SparseIntArray;
// keys with Rating values
METADATA_KEYS_TYPE.put(RATING_KEY_BY_OTHERS, METADATA_TYPE_RATING);
METADATA_KEYS_TYPE.put(RATING_KEY_BY_USER, METADATA_TYPE_RATING);
+ // Meta data for total number of tracks in Album
+ METADATA_KEYS_TYPE.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
}
}
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index a3ff080..7dd70d4 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -64,9 +64,7 @@ public class MediaMetadataRetriever
throw new IllegalArgumentException();
}
- FileInputStream is = null;
- try {
- is = new FileInputStream(path);
+ try (FileInputStream is = new FileInputStream(path)) {
FileDescriptor fd = is.getFD();
setDataSource(fd, 0, 0x7ffffffffffffffL);
} catch (FileNotFoundException fileEx) {
@@ -74,12 +72,6 @@ public class MediaMetadataRetriever
} catch (IOException ioEx) {
throw new IllegalArgumentException();
}
-
- try {
- if (is != null) {
- is.close();
- }
- } catch (Exception e) {}
}
/**
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 587d494..b3f25ee 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3453,6 +3453,23 @@ public class MediaPlayer implements SubtitleController.Listener
mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
}
+ /** @hide
+ */
+ public boolean suspend() {
+ stayAwake(false);
+ return _suspend();
+ }
+
+ private native boolean _suspend();
+
+ /** @hide
+ */
+ public boolean resume() {
+ return _resume();
+ }
+
+ private native boolean _resume();
+
/** @hide */
static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,
MediaTimeProvider {
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ed2c4cbd..7112c1a 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -345,6 +345,13 @@ public class MediaRecorder
/** VP8/VORBIS data in a WEBM container */
public static final int WEBM = 9;
+
+ /** @hide QCP file format */
+ public static final int QCP = 20;
+
+ /** @hide WAVE media file format*/
+ public static final int WAVE = 21;
+
};
/**
@@ -369,6 +376,12 @@ public class MediaRecorder
public static final int AAC_ELD = 5;
/** Ogg Vorbis audio codec */
public static final int VORBIS = 6;
+ /** @hide EVRC audio codec */
+ public static final int EVRC = 10;
+ /** @hide QCELP audio codec */
+ public static final int QCELP = 11;
+ /** @hide Linear PCM audio codec */
+ public static final int LPCM = 12;
}
/**
@@ -385,6 +398,8 @@ public class MediaRecorder
public static final int H264 = 2;
public static final int MPEG_4_SP = 3;
public static final int VP8 = 4;
+ /** @hide **/
+ public static final int H265 = 1001;
}
/**
@@ -436,11 +451,12 @@ public class MediaRecorder
setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
setVideoEncodingBitRate(profile.videoBitRate);
setVideoEncoder(profile.videoCodec);
- if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
- profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
+ if ((profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
+ profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_2160P) ||
+ profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_VGA) {
// Nothing needs to be done. Call to setCaptureRate() enables
// time lapse video recording.
- } else {
+ } else if (profile.audioCodec >= 0) {
setAudioEncodingBitRate(profile.audioBitRate);
setAudioChannels(profile.audioChannels);
setAudioSamplingRate(profile.audioSampleRate);
@@ -792,6 +808,10 @@ public class MediaRecorder
*/
public native void start() throws IllegalStateException;
+ /** @hide
+ */
+ public native void pause() throws IllegalStateException;
+
/**
* Stops recording. Call this after start(). Once recording is stopped,
* you will have to configure it again as if it has just been constructed.
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9ea6722..9264bc0 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -20,6 +20,7 @@ import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -41,11 +42,13 @@ import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.RootElement;
import android.system.ErrnoException;
import android.system.Os;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
@@ -61,6 +64,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Internal service helper that no-one should use directly.
@@ -324,26 +329,27 @@ public class MediaScanner
// used when scanning the image database so we know whether we have to prune
// old thumbnail files
private int mOriginalCount;
- /** Whether the database had any entries in it before the scan started */
- private boolean mWasEmptyPriorToScan = false;
/** Whether the scanner has set a default sound for the ringer ringtone. */
- private boolean mDefaultRingtoneSet;
+ private boolean[] mDefaultRingtonesSet;
/** Whether the scanner has set a default sound for the notification ringtone. */
private boolean mDefaultNotificationSet;
/** Whether the scanner has set a default sound for the alarm ringtone. */
private boolean mDefaultAlarmSet;
- /** The filename for the default sound for the ringer ringtone. */
- private String mDefaultRingtoneFilename;
+ /** The filenames for the default sound for the ringer ringtone. */
+ private String[] mDefaultRingtoneFilenames;
/** The filename for the default sound for the notification ringtone. */
private String mDefaultNotificationFilename;
/** The filename for the default sound for the alarm ringtone. */
private String mDefaultAlarmAlertFilename;
+ /** The number of phones in the system */
+ private int mPhoneCount = -1;
/**
* The prefix for system properties that define the default sound for
* ringtones. Concatenate the name of the setting from Settings
* to get the full system property.
*/
private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
+ private static final int DEFAULT_SIM_INDEX = 0;
// set to true if file path comparisons should be case insensitive.
// this should be set when scanning files on a case insensitive file system.
@@ -351,6 +357,11 @@ public class MediaScanner
private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
+ // For basic VorbisComment DATE tag support. It can take two forms, YYYY
+ // or YYYY-MM. This pattern is used to extract the year for compatibility
+ // with the ID3 YEAR tag.
+ private static final Pattern DATE_YEAR_DETECT_PATTERN = Pattern.compile("^(\\d{4})(-\\d{2})?$");
+
private static class FileEntry {
long mRowId;
String mPath;
@@ -401,8 +412,21 @@ public class MediaScanner
}
private void setDefaultRingtoneFileNames() {
- mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ String defaultAllSimRingtone = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.RINGTONE);
+
+ mPhoneCount = TelephonyManager.getDefault().getPhoneCount();
+ mDefaultRingtoneFilenames = new String[mPhoneCount];
+ mDefaultRingtonesSet = new boolean[mPhoneCount];
+
+ mDefaultRingtoneFilenames[DEFAULT_SIM_INDEX] = defaultAllSimRingtone;
+
+ for (int i = (DEFAULT_SIM_INDEX + 1); i < mPhoneCount; i++) {
+ String defaultIterSimRingtone = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ + Settings.System.RINGTONE + "_" + (i + 1), defaultAllSimRingtone);
+ mDefaultRingtoneFilenames[i] = defaultIterSimRingtone;
+ }
+
mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.NOTIFICATION_SOUND);
mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
@@ -529,12 +553,30 @@ public class MediaScanner
FileEntry entry = beginFile(path, mimeType, lastModified,
fileSize, isDirectory, noMedia);
+ if (entry == null) {
+ return null;
+ }
+
// if this file was just inserted via mtp, set the rowid to zero
// (even though it already exists in the database), to trigger
// the correct code path for updating its entry
if (mMtpObjectHandle != 0) {
entry.mRowId = 0;
}
+
+ if (entry.mPath != null &&
+ ((!mDefaultNotificationSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
+ || (mPhoneCount > 0 && !mDefaultRingtonesSet[(mPhoneCount-1)] &&
+ doesPathHaveFilename(entry.mPath,
+ mDefaultRingtoneFilenames[(mPhoneCount-1)]))
+ || (!mDefaultAlarmSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
+ Log.w(TAG, "forcing rescan of " + entry.mPath +
+ "since ringtone setting didn't finish");
+ scanAlways = true;
+ }
+
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
if (noMedia) {
@@ -615,6 +657,13 @@ public class MediaScanner
mGenre = getGenreName(value);
} else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
mYear = parseSubstring(value, 0, 0);
+ } else if (mYear == 0 && name.equalsIgnoreCase("date") || name.startsWith("date;")) {
+ // Since Android doesn't support DATE tag itself, just use it to extract
+ // the year, if the YEAR tag isn't present.
+ Matcher m = DATE_YEAR_DETECT_PATTERN.matcher(value);
+ if (m.find()) {
+ mYear = parseSubstring(m.group(1), 0, 0);
+ }
} else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
// track number might be of the form "2/12"
// we just read the number before the slash
@@ -914,6 +963,30 @@ public class MediaScanner
}
Uri result = null;
boolean needToSetSettings = false;
+ // Setting a flag in order not to use bulk insert for the file related with
+ // notifications, ringtones, and alarms, because the rowId of the inserted file is
+ // needed.
+ if (notifications && !mDefaultNotificationSet) {
+ if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (ringtones && !ringtoneDefaultsSet()) {
+ for (int i = 0; i < mPhoneCount; i++) {
+ // Check if ringtone matches default ringtone
+ if (TextUtils.isEmpty(mDefaultRingtoneFilenames[i]) ||
+ doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilenames[i])) {
+ needToSetSettings = true;
+ break;
+ }
+ }
+ } else if (alarms && !mDefaultAlarmSet) {
+ if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+ needToSetSettings = true;
+ }
+ }
+
if (rowId == 0) {
if (mMtpObjectHandle != 0) {
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -925,28 +998,6 @@ public class MediaScanner
}
values.put(Files.FileColumns.FORMAT, format);
}
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (mWasEmptyPriorToScan) {
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
- }
-
// New file, insert it.
// Directories need to be inserted before the files they contain, so they
// get priority when bulk inserting.
@@ -996,8 +1047,27 @@ public class MediaScanner
setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
mDefaultNotificationSet = true;
} else if (ringtones) {
- setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
- mDefaultRingtoneSet = true;
+ String uri = null;
+ for (int i = 0; i < mPhoneCount; i++) {
+ if (mDefaultRingtonesSet[i]) {
+ continue;
+ }
+ // Check if ringtone matches default ringtone
+ if (!TextUtils.isEmpty(mDefaultRingtoneFilenames[i]) &&
+ !doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilenames[i])) {
+ continue;
+ }
+ if (i == DEFAULT_SIM_INDEX) {
+ uri = Settings.System.RINGTONE;
+ } else {
+ uri = Settings.System.RINGTONE + "_" + (i + 1);
+ }
+
+ // Set default ringtone
+ setSettingIfNotSet(uri, tableUri, rowId);
+
+ mDefaultRingtonesSet[i] = true;
+ }
} else if (alarms) {
setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
mDefaultAlarmSet = true;
@@ -1007,6 +1077,15 @@ public class MediaScanner
return result;
}
+ private boolean ringtoneDefaultsSet() {
+ for (boolean defaultSet : mDefaultRingtonesSet) {
+ if (!defaultSet) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private boolean doesPathHaveFilename(String path, String filename) {
int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
int filenameLength = filename.length();
@@ -1016,14 +1095,18 @@ public class MediaScanner
private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
- String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
- settingName);
+ if(wasSettingAlreadySet(settingName)) {
+ return;
+ }
+ ContentResolver cr = mContext.getContentResolver();
+ String existingSettingValue = Settings.System.getString(cr, settingName);
if (TextUtils.isEmpty(existingSettingValue)) {
// Set the setting to the given URI
- Settings.System.putString(mContext.getContentResolver(), settingName,
+ Settings.System.putString(cr, settingName,
ContentUris.withAppendedId(uri, rowId).toString());
}
+ Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
}
private int getFileTypeFromDrm(String path) {
@@ -1050,6 +1133,20 @@ public class MediaScanner
}; // end of anonymous MediaScannerClient instance
+ private String settingSetIndicatorName(String base) {
+ return base + "_set";
+ }
+
+ private boolean wasSettingAlreadySet(String name) {
+ ContentResolver cr = mContext.getContentResolver();
+ String indicatorName = settingSetIndicatorName(name);
+ try {
+ return Settings.System.getInt(cr, indicatorName) != 0;
+ } catch (SettingNotFoundException e) {
+ return false;
+ }
+ }
+
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
Cursor c = null;
String where = null;
@@ -1071,6 +1168,13 @@ public class MediaScanner
selectionArgs = new String[] { "" };
}
+ mDefaultRingtonesSet[0] = wasSettingAlreadySet(Settings.System.RINGTONE);
+ for (int i=1; i< mPhoneCount; i++) {
+ mDefaultRingtonesSet[i] = wasSettingAlreadySet(Settings.System.RINGTONE + "_" + i);
+ }
+ mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
+ mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);
+
// Tell the provider to not delete the file.
// If the file is truly gone the delete is unnecessary, and we want to avoid
// accidentally deleting files that are really there (this may happen if the
@@ -1089,7 +1193,6 @@ public class MediaScanner
// with CursorWindow positioning.
long lastId = Long.MIN_VALUE;
Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
- mWasEmptyPriorToScan = true;
while (true) {
selectionArgs[0] = "" + lastId;
@@ -1108,7 +1211,6 @@ public class MediaScanner
if (num == 0) {
break;
}
- mWasEmptyPriorToScan = false;
while (c.moveToNext()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1260,7 +1362,7 @@ public class MediaScanner
}
}
- private void postscan(String[] directories) throws RemoteException {
+ private void postscan(final String[] directories) throws RemoteException {
// handle playlists last, after we know what media files are on the storage.
if (mProcessPlaylists) {
@@ -1364,7 +1466,7 @@ public class MediaScanner
// always scan the file, so we can return the content://media Uri for existing files
return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- false, true, MediaScanner.isNoMediaPath(path));
+ file.isDirectory(), true, MediaScanner.isNoMediaPath(path));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index c9a86d8..07b8c23 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -707,6 +707,79 @@ import java.lang.IllegalArgumentException;
}
}
+ /**
+ * @hide
+ */
+ public void playItemResponse(boolean success) {
+ Log.e(TAG, "playItemResponse");
+ playItemResponseInt(success);
+ }
+
+ private void playItemResponseInt(boolean success) {
+ Log.d(TAG, "playItemResponseInt");
+ Log.v(TAG, "success: " + success);
+
+ // USE_SESSIONS
+ if (mSession != null) {
+ mSession.playItemResponse(success);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateNowPlayingEntries(long[] playList) {
+ Log.e(TAG, "updateNowPlayingEntries: Item numbers: " + playList.length);
+ updateNowPlayingEntriesInt(playList);
+ }
+
+ private void updateNowPlayingEntriesInt(long[] playList) {
+ Log.d(TAG, "updateNowPlayingEntriesInt");
+
+ // USE_SESSIONS
+ if (mSession != null) {
+ mSession.updateNowPlayingEntries(playList);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateFolderInfoBrowsedPlayer(String stringUri) {
+ Log.e(TAG, "updateFolderInfoBrowsedPlayer");
+ synchronized(mCacheLock) {
+ updateFolderInfoBrowsedPlayerInt(stringUri);
+ }
+ }
+
+ private void updateFolderInfoBrowsedPlayerInt(String stringUri) {
+ Log.d(TAG, "updateFolderInfoBrowsedPlayerInt");
+
+ // USE_SESSIONS
+ if (mSession != null) {
+ mSession.updateFolderInfoBrowsedPlayer(stringUri);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateNowPlayingContentChange() {
+ Log.e(TAG, "updateNowPlayingContentChange");
+ synchronized(mCacheLock) {
+ updateNowPlayingContentChangeInt();
+ }
+ }
+
+ private void updateNowPlayingContentChangeInt() {
+ Log.d(TAG, "updateNowPlayingContentChangeInt");
+
+ // USE_SESSIONS
+ if (mSession != null) {
+ mSession.updateNowPlayingContentChange();
+ }
+ }
+
// TODO investigate if we still need position drift checking
private void onPositionDriftCheck() {
if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
@@ -798,6 +871,56 @@ import java.lang.IllegalArgumentException;
}
}
+ /**
+ * @hide
+ */
+ public interface OnGetNowPlayingEntriesListener {
+ public abstract void onGetNowPlayingEntries();
+ }
+
+ /**
+ * @hide
+ */
+ public void setNowPlayingEntriesUpdateListener(OnGetNowPlayingEntriesListener l) {
+ Log.d(TAG, "setNowPlayingEntriesUpdateListener");
+ synchronized(mCacheLock) {
+ mGetNowPlayingEntriesListener = l;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public interface OnSetBrowsedPlayerListener {
+ public abstract void onSetBrowsedPlayer();
+ }
+
+ /**
+ * @hide
+ */
+ public void setBrowsedPlayerUpdateListener(OnSetBrowsedPlayerListener l) {
+ Log.d(TAG, "setBrowsedPlayerUpdateListener");
+ synchronized(mCacheLock) {
+ mSetBrowsedPlayerListener = l;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public interface OnSetPlayItemListener {
+ public abstract void onSetPlayItem(int scope, long uid);
+ }
+
+ /**
+ * @hide
+ */
+ public void setPlayItemListener(OnSetPlayItemListener l) {
+ Log.d(TAG, "setPlayItemListener");
+ synchronized(mCacheLock) {
+ mSetPlayItemListener = l;
+ }
+ }
/**
* Interface definition for a callback to be invoked when the media playback position is
@@ -946,6 +1069,13 @@ import java.lang.IllegalArgumentException;
/**
* The current remote control client generation ID across the system, as known by this object
*/
+
+ private OnSetBrowsedPlayerListener mSetBrowsedPlayerListener;
+
+ private OnSetPlayItemListener mSetPlayItemListener;
+
+ private OnGetNowPlayingEntriesListener mGetNowPlayingEntriesListener;
+
private int mCurrentClientGenId = -1;
/**
@@ -999,10 +1129,43 @@ import java.lang.IllegalArgumentException;
onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating);
}
}
+
+ @Override
+ public void setPlayItem(int scope, long uid) {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ mEventHandler.removeMessages(MSG_SET_PLAY_ITEM);
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ MSG_SET_PLAY_ITEM, 0 /* arg1 */, scope /* arg2, ignored */,
+ new Long(uid)));
+ }
+ }
+
+ @Override
+ public void getNowPlayingEntries() {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ mEventHandler.removeMessages(MSG_GET_NOW_PLAYING_ENTRIES);
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ MSG_GET_NOW_PLAYING_ENTRIES, 0, 0, null));
+ }
+ }
+
+ @Override
+ public void setBrowsedPlayer() {
+ Log.d(TAG, "setBrowsedPlayer in RemoteControlClient");
+ if (mEventHandler != null) {
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ MSG_SET_BROWSED_PLAYER, 0 /* arg1 */, 0 /* arg2*/, null));
+ }
+ }
};
private EventHandler mEventHandler;
private final static int MSG_POSITION_DRIFT_CHECK = 11;
+ private final static int MSG_SET_BROWSED_PLAYER = 12;
+ private final static int MSG_SET_PLAY_ITEM = 13;
+ private final static int MSG_GET_NOW_PLAYING_ENTRIES = 14;
private class EventHandler extends Handler {
public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1015,6 +1178,16 @@ import java.lang.IllegalArgumentException;
case MSG_POSITION_DRIFT_CHECK:
onPositionDriftCheck();
break;
+ case MSG_SET_BROWSED_PLAYER:
+ Log.d(TAG, "MSG_SET_BROWSED_PLAYER in RemoteControlClient");
+ onSetBrowsedPlayer();
+ break;
+ case MSG_SET_PLAY_ITEM:
+ onSetPlayItem(msg.arg2, ((Long)msg.obj).longValue());
+ break;
+ case MSG_GET_NOW_PLAYING_ENTRIES:
+ onGetNowPlayingEntries();
+ break;
default:
Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
}
@@ -1040,6 +1213,36 @@ import java.lang.IllegalArgumentException;
}
}
+ private void onSetPlayItem(int scope, long uid) {
+ Log.d(TAG, "onSetPlayItem");
+ synchronized (mCacheLock) {
+ if (mSetPlayItemListener != null) {
+ Log.d(TAG, "mSetPlayItemListener.onSetPlayItem");
+ mSetPlayItemListener.onSetPlayItem(scope, uid);
+ }
+ }
+ }
+
+ private void onSetBrowsedPlayer() {
+ Log.d(TAG, "onSetBrowsedPlayer");
+ synchronized (mCacheLock) {
+ if (mSetBrowsedPlayerListener != null) {
+ Log.d(TAG, "mSetBrowsedPlayerListener.onSetBrowsedPlayer");
+ mSetBrowsedPlayerListener.onSetBrowsedPlayer();
+ }
+ }
+ }
+
+ private void onGetNowPlayingEntries() {
+ Log.d(TAG, "onGetNowPlayingEntries");
+ synchronized (mCacheLock) {
+ if (mGetNowPlayingEntriesListener != null) {
+ Log.d(TAG, "mGetNowPlayingEntriesListener.onGetNowPlayingEntries");
+ mGetNowPlayingEntriesListener.onGetNowPlayingEntries();
+ }
+ }
+ }
+
//===========================================================
// Internal utilities
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index d84cf30..aba7ad6 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -88,6 +88,7 @@ import java.util.List;
private boolean mIsRegistered = false;
private PendingIntent mClientPendingIntentCurrent;
private OnClientUpdateListener mOnClientUpdateListener;
+ private OnClientAvrcpUpdateListener mOnClientAvrcpUpdateListener;
private PlaybackInfo mLastPlaybackInfo;
private int mArtworkWidth = -1;
private int mArtworkHeight = -1;
@@ -150,6 +151,25 @@ import java.util.List;
}
}
+ /**
+ * @hide
+ */
+ public RemoteController(Context context, OnClientUpdateListener updateListener, Looper looper,
+ OnClientAvrcpUpdateListener avrcpUpdateListener) throws IllegalArgumentException {
+ this(context, updateListener, looper);
+ mOnClientAvrcpUpdateListener = avrcpUpdateListener;
+ }
+
+ /**
+ * @hide
+ */
+ public interface OnClientAvrcpUpdateListener {
+ public void onClientFolderInfoBrowsedPlayer(String stringUri);
+ public void onClientUpdateNowPlayingEntries(long[] playList);
+ public void onClientNowPlayingContentChange();
+ public void onClientPlayItemResponse(boolean success);
+ };
+
/**
* Interface definition for the callbacks to be invoked whenever media events, metadata
@@ -355,6 +375,7 @@ import java.util.List;
* @throws IllegalArgumentException
*/
public boolean seekTo(long timeMs) throws IllegalArgumentException {
+ Log.e(TAG, "seekTo() in RemoteController");
if (!mEnabled) {
Log.e(TAG, "Cannot use seekTo() from a disabled RemoteController");
return false;
@@ -370,6 +391,69 @@ import java.util.List;
return true;
}
+ /**
+ * @hide
+ * Request the user of a RemoteControlClient to play the requested item.
+ * @param generationId the RemoteControlClient generation counter for which this request is
+ * issued.
+ * @param uid uid of the song to be played.
+ * @scope scope of the file system to use
+ */
+ public void setRemoteControlClientPlayItem(long uid, int scope) {
+ Log.e(TAG, "setRemoteControlClientPlayItem()");
+ if (!mEnabled) {
+ Log.e(TAG, "Cannot use setRemoteControlClientPlayItem()" +
+ " from a disabled RemoteController");
+ return;
+ }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ mCurrentSession.getTransportControls().setRemoteControlClientPlayItem(uid, scope);
+ }
+ }
+ return;
+ }
+
+ /**
+ * @hide
+ * Request the user of a RemoteControlClient to provide with the now playing list entries.
+ * @param generationId the RemoteControlClient generation counter for which this request is
+ * issued.
+ */
+ public void getRemoteControlClientNowPlayingEntries() {
+ Log.e(TAG, "getRemoteControlClientNowPlayingEntries()");
+ if (!mEnabled) {
+ Log.e(TAG, "Cannot use getRemoteControlClientNowPlayingEntries()" +
+ " from a disabled RemoteController");
+ return;
+ }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ mCurrentSession.getTransportControls().getRemoteControlClientNowPlayingEntries();
+ }
+ }
+ return;
+ }
+
+ /**
+ * @hide
+ * Request the user of a RemoteControlClient to set the music player as current browsed player.
+ * @param packageName package name of the targeted media player.
+ */
+ public void setRemoteControlClientBrowsedPlayer() {
+ Log.e(TAG, "setRemoteControlClientBrowsedPlayer()");
+ if (!mEnabled) {
+ Log.e(TAG, "Cannot use setRemoteControlClientBrowsedPlayer()" +
+ " from a disabled RemoteController");
+ return;
+ }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ mCurrentSession.getTransportControls().setRemoteControlClientBrowsedPlayer();
+ }
+ }
+ return;
+ }
/**
* @hide
@@ -704,6 +788,30 @@ import java.util.List;
public void onMetadataChanged(MediaMetadata metadata) {
onNewMediaMetadata(metadata);
}
+
+ @Override
+ public void onUpdateFolderInfoBrowsedPlayer(String stringUri) {
+ Log.d(TAG, "MediaControllerCallback: onUpdateFolderInfoBrowsedPlayer");
+ onFolderInfoBrowsedPlayer(stringUri);
+ }
+
+ @Override
+ public void onUpdateNowPlayingEntries(long[] playList) {
+ Log.d(TAG, "MediaControllerCallback: onUpdateNowPlayingEntries");
+ onNowPlayingEntriesUpdate(playList);
+ }
+
+ @Override
+ public void onUpdateNowPlayingContentChange() {
+ Log.d(TAG, "MediaControllerCallback: onUpdateNowPlayingContentChange");
+ onNowPlayingContentChange();
+ }
+
+ @Override
+ public void onPlayItemResponse(boolean success) {
+ Log.d(TAG, "MediaControllerCallback: onPlayItemResponse");
+ onSetPlayItemResponse(success);
+ }
}
/**
@@ -980,6 +1088,8 @@ import java.util.List;
synchronized (mInfoLock) {
if (controller == null) {
if (mCurrentSession != null) {
+ Log.v(TAG, "Updating current controller as null");
+ mAudioManager.updateMediaPlayerList(mCurrentSession.getPackageName(), false);
mCurrentSession.unregisterCallback(mSessionCb);
mCurrentSession = null;
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
@@ -989,13 +1099,21 @@ import java.util.List;
|| !controller.getSessionToken()
.equals(mCurrentSession.getSessionToken())) {
if (mCurrentSession != null) {
+ Log.v(TAG, "Updating current controller package as " +
+ controller.getPackageName() + " from " + mCurrentSession.getPackageName());
mCurrentSession.unregisterCallback(mSessionCb);
+ } else {
+ Log.v(TAG, "Updating current controller package as " +
+ controller.getPackageName() + " from null");
}
+
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
0 /* genId */, 0 /* clearing */, null /* obj */, 0 /* delay */);
mCurrentSession = controller;
mCurrentSession.registerCallback(mSessionCb, mEventHandler);
+ mAudioManager.updateMediaPlayerList(mCurrentSession.getPackageName(), true);
+
PlaybackState state = controller.getPlaybackState();
sendMsg(mEventHandler, MSG_NEW_PLAYBACK_STATE, SENDMSG_REPLACE,
0 /* genId */, 0, state /* obj */, 0 /* delay */);
@@ -1052,6 +1170,74 @@ import java.util.List;
}
}
+ private void onFolderInfoBrowsedPlayer(String stringUri) {
+ Log.d(TAG, "RemoteController: onFolderInfoBrowsedPlayer");
+ final OnClientAvrcpUpdateListener l;
+
+ synchronized(mInfoLock) {
+ l = mOnClientAvrcpUpdateListener;
+ }
+
+ try {
+ if (l != null) {
+ l.onClientFolderInfoBrowsedPlayer(stringUri);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error Updating AVRCP on receiving Browsed player response", e);
+ }
+ }
+
+ private void onNowPlayingEntriesUpdate(long[] playList) {
+ Log.d(TAG, "RemoteController: onUpdateNowPlayingEntries");
+ final OnClientAvrcpUpdateListener l;
+
+ synchronized(mInfoLock) {
+ l = mOnClientAvrcpUpdateListener;
+ }
+
+ try {
+ if (l != null) {
+ l.onClientUpdateNowPlayingEntries(playList);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error Updating AVRCP on receiving Now Playing Entries", e);
+ }
+ }
+
+ private void onNowPlayingContentChange() {
+ Log.d(TAG, "RemoteController: onNowPlayingContentChange");
+ final OnClientAvrcpUpdateListener l;
+
+ synchronized(mInfoLock) {
+ l = mOnClientAvrcpUpdateListener;
+ }
+
+ try {
+ if (l != null) {
+ l.onClientNowPlayingContentChange();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error Updating AVRCP on Now Playing Content Change", e);
+ }
+ }
+
+ private void onSetPlayItemResponse(boolean success) {
+ Log.d(TAG, "RemoteController: onPlayItemResponse");
+ final OnClientAvrcpUpdateListener l;
+
+ synchronized(mInfoLock) {
+ l = mOnClientAvrcpUpdateListener;
+ }
+
+ try {
+ if (l != null) {
+ l.onClientPlayItemResponse(success);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error Updating AVRCP on receiving Play Item response", e);
+ }
+ }
+
//==================================================
private static class PlaybackInfo {
int mState;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index c2bcd93..b92b5d9 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -214,8 +214,19 @@ public class Ringtone {
if (Settings.AUTHORITY.equals(authority)) {
if (followSettingsUri) {
- Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context,
- RingtoneManager.getDefaultType(uri));
+ Uri actualUri;
+ if (RingtoneManager.getDefaultType(uri) == RingtoneManager.TYPE_RINGTONE) {
+ actualUri = RingtoneManager.getActualRingtoneUriBySubId(context,
+ RingtoneManager.getDefaultRingtoneSubIdByUri(uri));
+ } else {
+ actualUri = RingtoneManager.getActualDefaultRingtoneUri(context,
+ RingtoneManager.getDefaultType(uri));
+ }
+ if (actualUri == null) {
+ title = context
+ .getString(com.android.internal.R.string.ringtone_default);
+ return title;
+ }
String actualTitle = getTitle(
context, actualUri, false /*followSettingsUri*/, allowRemote);
title = context
@@ -412,9 +423,9 @@ public class Ringtone {
private boolean playFallbackRingtone() {
if (mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(mAudioAttributes))
!= 0) {
- int ringtoneType = RingtoneManager.getDefaultType(mUri);
- if (ringtoneType == -1 ||
- RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) != null) {
+ int subId = RingtoneManager.getDefaultRingtoneSubIdByUri(mUri);
+ if (subId != -1 &&
+ RingtoneManager.getActualRingtoneUriBySubId(mContext, subId) != null) {
// Default ringtone, try fallback ringtone.
try {
AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 025029e..fe4c91b 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -184,6 +184,16 @@ public class RingtoneManager {
*/
public static final String EXTRA_RINGTONE_PICKED_URI =
"android.intent.extra.ringtone.PICKED_URI";
+
+ /**
+ * Set the resource id theme to use for the dialog picker activity.<br/>
+ * The default theme is <code>com.android.internal.R.Theme_Holo_Dialog_Alert</code>.
+ *
+ * @see #ACTION_RINGTONE_PICKER
+ * @hide
+ */
+ public static final String EXTRA_RINGTONE_DIALOG_THEME =
+ "android.intent.extra.ringtone.DIALOG_THEME";
// Make sure the column ordering and then ..._COLUMN_INDEX are in sync
@@ -690,7 +700,9 @@ public class RingtoneManager {
public static int getDefaultType(Uri defaultRingtoneUri) {
if (defaultRingtoneUri == null) {
return -1;
- } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI)) {
+ } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI) ||
+ defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI_2) ||
+ defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI_3)) {
return TYPE_RINGTONE;
} else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_NOTIFICATION_URI)) {
return TYPE_NOTIFICATION;
@@ -721,5 +733,106 @@ public class RingtoneManager {
return null;
}
}
-
+
+ /**
+ * Returns the subscription ID of {@link Uri}.
+ *
+ * @param defaultRingtoneUri The default {@link Uri}. For example,
+ * {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_RINGTONE_URI_2}, or
+ * {@link System#DEFAULT_RINGTONE_URI_3}.
+ * @return The Subscription ID of the defaultRingtoneUri, or -1.
+ * @hide
+ */
+ public static int getDefaultRingtoneSubIdByUri(Uri defaultRingtoneUri) {
+ if (defaultRingtoneUri == null) {
+ return -1;
+ }
+ /**
+ * URI is encoded as below:
+ * DEFAULT_RINGTONE_URI: content://settings/system/ringtone
+ * DEFAULT_RINGTONE_URI_2: content://settings/system/ringtone_2
+ * DEFAULT_RINGTONE_URI_3: content://settings/system/ringtone_3
+ */
+ if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI)) {
+ return 0; /* Sub-1 */
+ }
+ final String uriString = defaultRingtoneUri.toString();
+ int parsedSubId = -1;
+ if (uriString.startsWith(Settings.System.DEFAULT_RINGTONE_URI.toString())) {
+ parsedSubId = Integer.parseInt(uriString.substring(uriString.lastIndexOf("_") + 1));
+ if ((parsedSubId > 0 && parsedSubId <= Settings.System.MAX_NUM_RINGTONES)) {
+ return parsedSubId - 1;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the actual default sound's {@link Uri}. This will give the actual
+ * sound {@link Uri}, instead of using this, most clients can use
+ * {@link System#DEFAULT_RINGTONE_URI}.
+ *
+ * @param subId The Subscription ID.
+ * @return A {@link Uri} pointing to the default sound for the sound type.
+ * @hide
+ */
+ public static Uri getDefaultRingtoneUriBySubId(int subId) {
+ if (!(subId >= 0 && subId < Settings.System.MAX_NUM_RINGTONES)) {
+ return null;
+ }
+ if (subId == 0) {
+ return Settings.System.DEFAULT_RINGTONE_URI;
+ } else {
+ final String uriString =
+ Settings.System.DEFAULT_RINGTONE_URI.toString() + "_" + (subId + 1);
+ return Uri.parse(uriString);
+ }
+ }
+
+ /**
+ * Gets the current default sound's {@link Uri}. This will give the actual
+ * sound {@link Uri}, instead of using this, most clients can use
+ * {@link System#DEFAULT_RINGTONE_URI}.
+ *
+ * @param context A context used for querying.
+ * @param subId The Subscription ID.
+ * @return A {@link Uri} pointing to the default sound for the sound type.
+ * @hide
+ */
+ public static Uri getActualRingtoneUriBySubId(Context context, int subId) {
+ if (!(subId >= 0 && subId < Settings.System.MAX_NUM_RINGTONES)) {
+ return null;
+ }
+ String setting;
+ if (subId == 0) {
+ setting = Settings.System.RINGTONE;
+ } else {
+ setting = Settings.System.RINGTONE + "_" + (subId + 1);
+ }
+ final String uriString = Settings.System.getString(context.getContentResolver(), setting);
+ return uriString != null ? Uri.parse(uriString) : null;
+ }
+
+ /**
+ * Sets the {@link Uri} of the default sound for a given sound type.
+ *
+ * @param context A context used for querying.
+ * @param subId The Subscription ID.
+ * @param ringtoneUri A {@link Uri} pointing to the default sound to set.
+ * @hide
+ */
+ public static void setActualRingtoneUriBySubId(Context context, int subId, Uri ringtoneUri) {
+ if (!(subId >= 0 && subId < Settings.System.MAX_NUM_RINGTONES)) {
+ return;
+ }
+ String setting;
+ if (subId == 0) {
+ setting = Settings.System.RINGTONE;
+ } else {
+ setting = Settings.System.RINGTONE + "_" + (subId + 1);
+ }
+ Settings.System.putString(context.getContentResolver(), setting,
+ ringtoneUri != null ? ringtoneUri.toString() : null);
+ }
}
diff --git a/media/java/android/media/SRTRenderer.java b/media/java/android/media/SRTRenderer.java
index ee4edee..a3e2abd 100644
--- a/media/java/android/media/SRTRenderer.java
+++ b/media/java/android/media/SRTRenderer.java
@@ -165,7 +165,6 @@ class SRTTrack extends WebVttTrack {
return;
}
- final int _ = 0;
for (Cue cue : activeCues) {
TextTrackCue ttc = (TextTrackCue) cue;
@@ -184,7 +183,8 @@ class SRTTrack extends WebVttTrack {
parcel.writeInt(buf.length);
parcel.writeByteArray(buf);
- Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, _, _, parcel);
+ Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, 0 /* arg1 */, 0 /* arg2 */,
+ parcel);
mEventHandler.sendMessage(msg);
}
activeCues.clear();
diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java
index 4661226..056244a 100644
--- a/media/java/android/media/ToneGenerator.java
+++ b/media/java/android/media/ToneGenerator.java
@@ -728,13 +728,18 @@ public class ToneGenerator
* @see #ToneGenerator(int, int)
*/
public static final int TONE_CDMA_SIGNAL_OFF = 98;
+ /**
+ * HOLD_RECALL - 440Hz
+ *
+ * @hide #ToneGenerator(int, int)
+ */
+ public static final int TONE_HOLD_RECALL = 99;
/** Maximum volume, for use with {@link #ToneGenerator(int,int)} */
public static final int MAX_VOLUME = 100;
/** Minimum volume setting, for use with {@link #ToneGenerator(int,int)} */
public static final int MIN_VOLUME = 0;
-
/**
* ToneGenerator class contructor specifying output stream type and volume.
*
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index bd0019f..34eadcb 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -45,6 +45,10 @@ interface ISession {
void setQueueTitle(CharSequence title);
void setExtras(in Bundle extras);
void setRatingType(int type);
+ void playItemResponse(boolean success);
+ void updateNowPlayingEntries(in long[] playList);
+ void updateFolderInfoBrowsedPlayer(String stringUri);
+ void updateNowPlayingContentChange();
// These commands relate to volume handling
void setPlaybackToLocal(in AudioAttributes attributes);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index adb6b06..ed13ff6 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -41,6 +41,9 @@ oneway interface ISessionCallback {
void onFastForward();
void onRewind();
void onSeekTo(long pos);
+ void setRemoteControlClientBrowsedPlayer();
+ void setRemoteControlClientPlayItem(long uid, int scope);
+ void getRemoteControlClientNowPlayingEntries();
void onRate(in Rating rating);
void onCustomAction(String action, in Bundle args);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 285e5f7..006ffac 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -62,6 +62,9 @@ interface ISessionController {
void fastForward();
void rewind();
void seekTo(long pos);
+ void setRemoteControlClientBrowsedPlayer();
+ void setRemoteControlClientPlayItem(long uid, int scope);
+ void getRemoteControlClientNowPlayingEntries();
void rate(in Rating rating);
void sendCustomAction(String action, in Bundle args);
MediaMetadata getMetadata();
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index cf31767..a5ad913 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -36,4 +36,8 @@ oneway interface ISessionControllerCallback {
void onQueueTitleChanged(CharSequence title);
void onExtrasChanged(in Bundle extras);
void onVolumeInfoChanged(in ParcelableVolumeInfo info);
+ void onPlayItemResponse(boolean success);
+ void onUpdateNowPlayingEntries(in long[] playList);
+ void onUpdateFolderInfoBrowsedPlayer(String stringUri);
+ void onUpdateNowPlayingContentChange();
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index b1a51a5..f1f9516 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -64,6 +64,10 @@ public final class MediaController {
private static final int MSG_UPDATE_QUEUE_TITLE = 6;
private static final int MSG_UPDATE_EXTRAS = 7;
private static final int MSG_DESTROYED = 8;
+ private static final int MSG_FOLDER_INFO_BROWSED_PLAYER = 9;
+ private static final int MSG_UPDATE_NOWPLAYING_ENTRIES = 10;
+ private static final int MSG_UPDATE_NOWPLAYING_CONTENT_CHANGE = 11;
+ private static final int MSG_PLAY_ITEM_RESPONSE = 12;
private final ISessionController mSessionBinder;
@@ -579,6 +583,31 @@ public final class MediaController {
*/
public void onAudioInfoChanged(PlaybackInfo info) {
}
+
+ /**
+ * @hide
+ */
+ public void onUpdateFolderInfoBrowsedPlayer(String stringUri) {
+ }
+
+ /**
+ * @hide
+ */
+ public void onUpdateNowPlayingEntries(long[] playList) {
+ }
+
+ /**
+ * @hide
+ */
+ public void onUpdateNowPlayingContentChange() {
+ }
+
+ /**
+ * @hide
+ */
+ public void onPlayItemResponse(boolean success) {
+ }
+
}
/**
@@ -704,6 +733,7 @@ public final class MediaController {
* @param pos Position to move to, in milliseconds.
*/
public void seekTo(long pos) {
+ Log.d(TAG, "seekTo in TransportControls");
try {
mSessionBinder.seekTo(pos);
} catch (RemoteException e) {
@@ -712,6 +742,42 @@ public final class MediaController {
}
/**
+ * @hide
+ */
+ public void setRemoteControlClientBrowsedPlayer() {
+ Log.d(TAG, "setRemoteControlClientBrowsedPlayer in TransportControls");
+ try {
+ mSessionBinder.setRemoteControlClientBrowsedPlayer();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling setRemoteControlClientBrowsedPlayer.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setRemoteControlClientPlayItem(long uid, int scope) {
+ Log.d(TAG, "setRemoteControlClientPlayItem in TransportControls");
+ try {
+ mSessionBinder.setRemoteControlClientPlayItem(uid, scope);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling setRemoteControlClientPlayItem.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void getRemoteControlClientNowPlayingEntries() {
+ Log.d(TAG, "getRemoteControlClientNowPlayingEntries in TransportControls");
+ try {
+ mSessionBinder.getRemoteControlClientNowPlayingEntries();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getRemoteControlClientNowPlayingEntries.", e);
+ }
+ }
+
+ /**
* Start fast forwarding. If playback is already fast forwarding this
* may increase the rate.
*/
@@ -973,6 +1039,42 @@ public final class MediaController {
}
}
+ @Override
+ public void onUpdateFolderInfoBrowsedPlayer(String stringUri) {
+ Log.d(TAG, "CallBackStub: onUpdateFolderInfoBrowsedPlayer");
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_FOLDER_INFO_BROWSED_PLAYER, stringUri, null);
+ }
+ }
+
+ @Override
+ public void onUpdateNowPlayingEntries(long[] playList) {
+ Log.d(TAG, "CallBackStub: onUpdateNowPlayingEntries");
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_NOWPLAYING_ENTRIES, playList, null);
+ }
+ }
+
+ @Override
+ public void onUpdateNowPlayingContentChange() {
+ Log.d(TAG, "CallBackStub: onUpdateNowPlayingContentChange");
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_NOWPLAYING_CONTENT_CHANGE, null, null);
+ }
+ }
+
+ @Override
+ public void onPlayItemResponse(boolean success) {
+ Log.d(TAG, "CallBackStub: onPlayItemResponse");
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_PLAY_ITEM_RESPONSE, new Boolean(success), null);
+ }
+ }
+
}
private final static class MessageHandler extends Handler {
@@ -1014,6 +1116,18 @@ public final class MediaController {
case MSG_DESTROYED:
mCallback.onSessionDestroyed();
break;
+ case MSG_FOLDER_INFO_BROWSED_PLAYER:
+ mCallback.onUpdateFolderInfoBrowsedPlayer((String) msg.obj);
+ break;
+ case MSG_UPDATE_NOWPLAYING_ENTRIES:
+ mCallback.onUpdateNowPlayingEntries((long[]) msg.obj);
+ break;
+ case MSG_UPDATE_NOWPLAYING_CONTENT_CHANGE:
+ mCallback.onUpdateNowPlayingContentChange();
+ break;
+ case MSG_PLAY_ITEM_RESPONSE:
+ mCallback.onPlayItemResponse(((Boolean)(msg.obj)).booleanValue());
+ break;
}
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index e1e9b79..be89dfc 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -493,6 +493,58 @@ public final class MediaSession {
}
/**
+ * @hide
+ */
+ public void playItemResponse(boolean success) {
+ Log.d(TAG, "MediaSession: playItemResponse");
+
+ try {
+ mBinder.playItemResponse(success);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in playItemResponse.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateNowPlayingEntries(long[] playList) {
+ Log.d(TAG, "MediaSession: updateNowPlayingEntries");
+
+ try {
+ mBinder.updateNowPlayingEntries(playList);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in updateNowPlayingEntries.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateFolderInfoBrowsedPlayer(String stringUri) {
+ Log.d(TAG, "MediaSession: updateFolderInfoBrowsedPlayer");
+
+ try {
+ mBinder.updateFolderInfoBrowsedPlayer(stringUri);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in updateFolderInfoBrowsedPlayer.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateNowPlayingContentChange() {
+ Log.d(TAG, "MediaSession: updateNowPlayingContentChange");
+
+ try {
+ mBinder.updateNowPlayingContentChange();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in updateNowPlayingContentChange.", e);
+ }
+ }
+
+ /**
* Notify the system that the remote volume changed.
*
* @param provider The provider that is handling volume changes.
@@ -572,6 +624,34 @@ public final class MediaSession {
postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
}
+ private void dispatchSetBrowsedPlayerCommand() {
+ postToCallback(CallbackMessageHandler.MSG_SET_BROWSED_PLAYER);
+ }
+
+ private void dispatchSetPlayItemCommand(long uid, int scope) {
+ PlayItemToken playItemToken = new PlayItemToken(uid, scope);
+ postToCallback(CallbackMessageHandler.MSG_SET_PLAY_ITEM, playItemToken);
+ }
+
+ private class PlayItemToken {
+ private long mUid;
+ private int mScope;
+ public PlayItemToken(long uid, int scope) {
+ mUid = uid;
+ mScope = scope;
+ }
+ public int getScope() {
+ return mScope;
+ }
+ public long getUid() {
+ return mUid;
+ }
+ }
+
+ private void dispatchGetNowPlayingItemsCommand() {
+ postToCallback(CallbackMessageHandler.MSG_GET_NOW_PLAYING_ITEMS);
+ }
+
private void dispatchAdjustVolume(int direction) {
postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction);
}
@@ -894,6 +974,25 @@ public final class MediaSession {
*/
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
}
+
+ /**
+ * @hide
+ */
+ public void setBrowsedPlayer() {
+ }
+
+ /**
+ * @hide
+ */
+ public void setPlayItem(int scope, long uid) {
+ }
+
+ /**
+ * @hide
+ */
+ public void getNowPlayingEntries() {
+ }
+
}
/**
@@ -1034,6 +1133,33 @@ public final class MediaSession {
}
@Override
+ public void setRemoteControlClientBrowsedPlayer() throws RemoteException {
+ Log.d(TAG, "setRemoteControlClientBrowsedPlayer in CallbackStub");
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchSetBrowsedPlayerCommand();
+ }
+ }
+
+ @Override
+ public void setRemoteControlClientPlayItem(long uid, int scope) throws RemoteException {
+ Log.d(TAG, "setRemoteControlClientPlayItem in CallbackStub");
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchSetPlayItemCommand(uid, scope);
+ }
+ }
+
+ @Override
+ public void getRemoteControlClientNowPlayingEntries() throws RemoteException {
+ Log.d(TAG, "getRemoteControlClientNowPlayingEntries in CallbackStub");
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchGetNowPlayingItemsCommand();
+ }
+ }
+
+ @Override
public void onCustomAction(String action, Bundle args) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1173,6 +1299,9 @@ public final class MediaSession {
private static final int MSG_ADJUST_VOLUME = 16;
private static final int MSG_SET_VOLUME = 17;
private static final int MSG_PLAY_URI = 18;
+ private static final int MSG_SET_BROWSED_PLAYER = 19;
+ private static final int MSG_SET_PLAY_ITEM = 20;
+ private static final int MSG_GET_NOW_PLAYING_ITEMS = 21;
private MediaSession.Callback mCallback;
@@ -1267,6 +1396,18 @@ public final class MediaSession {
if (vp != null) {
vp.onSetVolumeTo((int) msg.obj);
}
+ case MSG_SET_BROWSED_PLAYER:
+ Log.d(TAG, "MSG_SET_BROWSED_PLAYER received in CallbackMessageHandler");
+ mCallback.setBrowsedPlayer();
+ break;
+ case MSG_SET_PLAY_ITEM:
+ Log.d(TAG, "MSG_SET_PLAY_ITEM received in CallbackMessageHandler");
+ PlayItemToken playItemToken = (PlayItemToken) msg.obj;
+ mCallback.setPlayItem(playItemToken.getScope(), playItemToken.getUid());
+ break;
+ case MSG_GET_NOW_PLAYING_ITEMS:
+ Log.d(TAG, "MSG_GET_NOW_PLAYING_ITEMS received in CallbackMessageHandler");
+ mCallback.getNowPlayingEntries();
break;
}
}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index c61d7ad..22082b9 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -549,6 +549,27 @@ public class MediaSessionLegacyHelper {
mRccListener.onSetRating(rating);
}
}
+
+ @Override
+ public void setBrowsedPlayer() {
+ if (mRccListener != null) {
+ mRccListener.setBrowsedPlayer();
+ }
+ }
+
+ @Override
+ public void setPlayItem(int scope, long uid) {
+ if (mRccListener != null) {
+ mRccListener.setPlayItem(scope, uid);
+ }
+ }
+
+ @Override
+ public void getNowPlayingEntries() {
+ if (mRccListener != null) {
+ mRccListener.getNowPlayingEntries();
+ }
+ }
}
}
}
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 3814630..e830afd 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -56,6 +56,10 @@ public class MtpServer implements Runnable {
native_send_device_property_changed(property);
}
+ public void sendObjectUpdated(int handle) {
+ native_send_object_updated(handle);
+ }
+
public void addStorage(MtpStorage storage) {
native_add_storage(storage);
}
@@ -70,6 +74,7 @@ public class MtpServer implements Runnable {
private native final void native_send_object_added(int handle);
private native final void native_send_object_removed(int handle);
private native final void native_send_device_property_changed(int property);
+ private native final void native_send_object_updated(int handle);
private native final void native_add_storage(MtpStorage storage);
private native final void native_remove_storage(int storageId);
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d8041f4..88670ea 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -49,6 +49,7 @@
#include <gui/Surface.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <dlfcn.h>
#include "android_util_Binder.h"
// ----------------------------------------------------------------------------
@@ -137,6 +138,153 @@ void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *o
}
}
+
+static const char *EXTMEDIAJNI_LIB_NAME = "libextmedia_jni.so";
+static const char *kCreateJNIExtMediaPlayerListener = "CreateJNIExtMediaPlayerListener";
+static const char *kCheckExtMedia = "checkExtMedia";
+static const char *kCreateNativeQCMediaPlayer = "CreateNativeQCMediaPlayer";
+typedef MediaPlayerListener* (*CreateJNIExtMediaPlayerListenerFn)(JNIEnv *, jobject, jobject, sp<MediaPlayerListener> listener);
+typedef bool (*CheckExtMediaFn)(JNIEnv *env, jobject);
+typedef MediaPlayer* (*CreateNativeQCMediaPlayerFn)();
+
+
+
+class JNIMediaPlayerFactory {
+ public:
+ JNIMediaPlayerFactory() {};
+ static bool CheckAndCreateExtMediaPlayer(JNIEnv *env, jobject thiz, jobject weak_this, sp<MediaPlayerListener> &listener, sp<MediaPlayer> &mp);
+ private:
+ static void *mLibHandle;
+ static void loadLib();
+
+ static CreateJNIExtMediaPlayerListenerFn loadJNIExtMediaPlayerListener();
+ static CreateJNIExtMediaPlayerListenerFn sExtDashListnerFnPtr;
+
+ static CheckExtMediaFn sExtMediaFn;
+ static CheckExtMediaFn loadExtMedia();
+
+ static CreateNativeQCMediaPlayerFn sNativeQCMediaPlayerFn;
+ static CreateNativeQCMediaPlayerFn loadNativeQCMediaPlayer();
+
+ static sp<MediaPlayerListener> createExtMediaPlayerListener(JNIEnv *env, jobject thiz, jobject weak_this, sp<MediaPlayerListener> listener);
+ static bool checkExtMedia(JNIEnv *env, jobject thiz);
+ static void CreateNativeQCMediaPlayer(sp<MediaPlayer> &mp);
+};
+
+void *JNIMediaPlayerFactory::mLibHandle = NULL;
+
+CreateJNIExtMediaPlayerListenerFn JNIMediaPlayerFactory::sExtDashListnerFnPtr =
+ JNIMediaPlayerFactory::loadJNIExtMediaPlayerListener();
+
+CheckExtMediaFn JNIMediaPlayerFactory::sExtMediaFn =
+ JNIMediaPlayerFactory::loadExtMedia();
+
+CreateNativeQCMediaPlayerFn JNIMediaPlayerFactory::sNativeQCMediaPlayerFn =
+ JNIMediaPlayerFactory::loadNativeQCMediaPlayer();
+
+
+void JNIMediaPlayerFactory::loadLib()
+{
+ if (!mLibHandle) {
+ mLibHandle = ::dlopen(EXTMEDIAJNI_LIB_NAME, RTLD_LAZY);
+ if (!mLibHandle) {
+ ALOGV("%s", dlerror());
+ return;
+ }
+ ALOGV("Opened %s", EXTMEDIAJNI_LIB_NAME);
+ }
+}
+
+CreateJNIExtMediaPlayerListenerFn JNIMediaPlayerFactory::loadJNIExtMediaPlayerListener()
+{
+ loadLib();
+ CreateJNIExtMediaPlayerListenerFn pCreateExtDashListnerFnPtr = NULL;
+ if (mLibHandle != NULL) {
+ pCreateExtDashListnerFnPtr = (CreateJNIExtMediaPlayerListenerFn)
+ dlsym(mLibHandle, kCreateJNIExtMediaPlayerListener);
+ if (pCreateExtDashListnerFnPtr == NULL) {
+ ALOGW("Failed to load symbol %s : %s", kCreateJNIExtMediaPlayerListener, dlerror());
+ }
+ }
+ return pCreateExtDashListnerFnPtr;
+}
+
+CheckExtMediaFn JNIMediaPlayerFactory::loadExtMedia()
+{
+ loadLib();
+ CheckExtMediaFn pCheckExtMediaFnPtr = NULL;
+ if (mLibHandle != NULL) {
+ pCheckExtMediaFnPtr = (CheckExtMediaFn)dlsym(mLibHandle, kCheckExtMedia);
+ if (pCheckExtMediaFnPtr == NULL) {
+ ALOGW("Failed to load symbol %s : %s", kCheckExtMedia, dlerror());
+ }
+ }
+ return pCheckExtMediaFnPtr;
+}
+
+CreateNativeQCMediaPlayerFn JNIMediaPlayerFactory::loadNativeQCMediaPlayer()
+{
+ loadLib();
+ CreateNativeQCMediaPlayerFn pCreateNativeQCMediaPlayerFnPtr = NULL;
+ if (mLibHandle != NULL) {
+ pCreateNativeQCMediaPlayerFnPtr = (CreateNativeQCMediaPlayerFn)
+ dlsym(mLibHandle, kCreateNativeQCMediaPlayer);
+ if (pCreateNativeQCMediaPlayerFnPtr == NULL) {
+ ALOGW("Failed to load symbol %s : %s", kCreateNativeQCMediaPlayer, dlerror());
+ }
+ }
+ return pCreateNativeQCMediaPlayerFnPtr;
+}
+
+
+sp<MediaPlayerListener> JNIMediaPlayerFactory::createExtMediaPlayerListener(JNIEnv *env, jobject thiz, jobject weak_this, sp<MediaPlayerListener> listener)
+{
+ if (checkExtMedia(env, thiz)) {
+ if (sExtDashListnerFnPtr ) {
+ listener = (*sExtDashListnerFnPtr)(env, thiz, weak_this, listener);
+ if (listener != NULL) {
+ ALOGE("JNIMediaPlayerFactory: createExtMediaPlayerListener : success");
+ }
+ }
+ }
+ return listener;
+}
+
+void JNIMediaPlayerFactory::CreateNativeQCMediaPlayer(sp<MediaPlayer> &mp)
+{
+ if (sNativeQCMediaPlayerFn) {
+ mp = (*sNativeQCMediaPlayerFn)();
+ if (mp != NULL) {
+ ALOGE("JNIMediaPlayerFactory: CreateNativeQCMediaPlayer : Success");
+ }
+ }
+}
+
+
+bool JNIMediaPlayerFactory::checkExtMedia(JNIEnv *env, jobject thiz)
+{
+ bool bIsQCMediaPlayerPresent = false;
+ if (sExtMediaFn) {
+ bIsQCMediaPlayerPresent = (*sExtMediaFn)(env, thiz);
+ }
+ ALOGE("JNIMediaPlayerFactory: bIsQCMediaPlayerPresent %d", bIsQCMediaPlayerPresent);
+ return bIsQCMediaPlayerPresent;
+}
+
+bool JNIMediaPlayerFactory::CheckAndCreateExtMediaPlayer(
+ JNIEnv *env, jobject thiz, jobject weak_this, sp<MediaPlayerListener> &listener, sp<MediaPlayer> &mp)
+{
+ bool bOk = false;
+ listener = createExtMediaPlayerListener(env, thiz, weak_this, listener);
+ if (listener != NULL && checkExtMedia(env,thiz)) {
+ CreateNativeQCMediaPlayer(mp);
+ if (mp != NULL) {
+ bOk = true;
+ }
+ }
+ return bOk;
+}
+
// ----------------------------------------------------------------------------
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
@@ -868,14 +1016,26 @@ static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
- sp<MediaPlayer> mp = new MediaPlayer();
+
+ sp<MediaPlayer> mp = NULL;
+
+ bool bOk = false;
+ JNIMediaPlayerFactory *jniMediaPlayerFactory = new JNIMediaPlayerFactory();
+
+ sp<MediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
+
+ if (jniMediaPlayerFactory) {
+ bOk = jniMediaPlayerFactory->CheckAndCreateExtMediaPlayer(env, thiz, weak_this, listener, mp);
+ delete(jniMediaPlayerFactory);
+ }
+
+ if (!bOk){
+ mp = new MediaPlayer();
+ }
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
-
- // create new listener and give it to MediaPlayer
- sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
@@ -1031,6 +1191,38 @@ android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject
;
}
+static jboolean
+android_media_MediaPlayer_suspend(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return false;
+ }
+
+ if (mp->suspend() != OK) {
+ return false;
+ }
+
+ return true;
+}
+
+static jboolean
+android_media_MediaPlayer_resume(JNIEnv *env, jobject thiz)
+{
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return false;
+ }
+
+ if (mp->resume() != OK) {
+ return false;
+ }
+
+ return true;
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -1080,6 +1272,8 @@ static JNINativeMethod gMethods[] = {
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
{"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
{"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer},
+ {"_suspend", "()Z", (void *)android_media_MediaPlayer_suspend},
+ {"_resume", "()Z", (void *)android_media_MediaPlayer_resume},
};
// This function only registers the native methods
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index ca9db91..30fd4c4 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -92,12 +92,17 @@ android_media_MediaProfiles_native_get_video_encoder_cap(JNIEnv *env, jobject /*
int maxFrameWidth = sProfiles->getVideoEncoderParamByName("enc.vid.width.max", encoder);
int minFrameHeight = sProfiles->getVideoEncoderParamByName("enc.vid.height.min", encoder);
int maxFrameHeight = sProfiles->getVideoEncoderParamByName("enc.vid.height.max", encoder);
+ int maxHFRFrameWidth = sProfiles->getVideoEncoderParamByName("enc.vid.hfr.width.max", encoder);
+ int maxHFRFrameHeight = sProfiles->getVideoEncoderParamByName("enc.vid.hfr.height.max", encoder);
+ int maxHFRMode = sProfiles->getVideoEncoderParamByName("enc.vid.hfr.mode.max", encoder);
// Check on the values retrieved
if ((minBitRate == -1 || maxBitRate == -1) ||
(minFrameRate == -1 || maxFrameRate == -1) ||
(minFrameWidth == -1 || maxFrameWidth == -1) ||
- (minFrameHeight == -1 || maxFrameHeight == -1)) {
+ (minFrameHeight == -1 || maxFrameHeight == -1) ||
+ (maxHFRFrameWidth == -1 || maxHFRFrameHeight == -1) ||
+ (maxHFRMode == -1)) {
jniThrowException(env, "java/lang/RuntimeException", "Error retrieving video encoder capability params");
return NULL;
@@ -105,14 +110,16 @@ android_media_MediaProfiles_native_get_video_encoder_cap(JNIEnv *env, jobject /*
// Construct an instance of the VideoEncoderCap and set its member variables
jclass videoEncoderCapClazz = env->FindClass("android/media/EncoderCapabilities$VideoEncoderCap");
- jmethodID videoEncoderCapConstructorMethodID = env->GetMethodID(videoEncoderCapClazz, "<init>", "(IIIIIIIII)V");
+ jmethodID videoEncoderCapConstructorMethodID = env->GetMethodID(videoEncoderCapClazz, "<init>", "(IIIIIIIIIIII)V");
jobject cap = env->NewObject(videoEncoderCapClazz,
videoEncoderCapConstructorMethodID,
static_cast<int>(encoder),
minBitRate, maxBitRate,
minFrameRate, maxFrameRate,
minFrameWidth, maxFrameWidth,
- minFrameHeight, maxFrameHeight);
+ minFrameHeight, maxFrameHeight,
+ maxHFRFrameWidth, maxHFRFrameHeight,
+ maxHFRMode);
return cap;
}
@@ -170,7 +177,9 @@ static bool isCamcorderQualityKnown(int quality)
(quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START &&
quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END) ||
(quality >= CAMCORDER_QUALITY_HIGH_SPEED_LIST_START &&
- quality <= CAMCORDER_QUALITY_HIGH_SPEED_LIST_END));
+ quality <= CAMCORDER_QUALITY_HIGH_SPEED_LIST_END) ||
+ (quality >= CAMCORDER_QUALITY_VENDOR_START &&
+ quality <= CAMCORDER_QUALITY_VENDOR_END));
}
static jobject
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index f60af63..69da951 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -40,6 +40,7 @@
#include <system/audio.h>
#include <android_runtime/android_view_Surface.h>
+#include "SeempLog.h"
// ----------------------------------------------------------------------------
@@ -219,7 +220,9 @@ static void
android_media_MediaRecorder_setVideoEncoder(JNIEnv *env, jobject thiz, jint ve)
{
ALOGV("setVideoEncoder(%d)", ve);
- if (ve < VIDEO_ENCODER_DEFAULT || ve >= VIDEO_ENCODER_LIST_END) {
+ if (ve < VIDEO_ENCODER_DEFAULT ||
+ (ve >= VIDEO_ENCODER_LIST_END && ve <= VIDEO_ENCODER_LIST_VENDOR_START) ||
+ ve >= VIDEO_ENCODER_LIST_VENDOR_END) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video encoder");
return;
}
@@ -391,6 +394,14 @@ android_media_MediaRecorder_start(JNIEnv *env, jobject thiz)
}
static void
+android_media_MediaRecorder_pause(JNIEnv *env, jobject thiz)
+{
+ ALOGV("pause");
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ process_media_recorder_call(env, mr->pause(), "java/lang/RuntimeException", "pause failed.");
+}
+
+static void
android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz)
{
ALOGV("stop");
@@ -527,6 +538,7 @@ static JNINativeMethod gMethods[] = {
{"getSurface", "()Landroid/view/Surface;", (void *)android_media_MediaRecorder_getSurface},
{"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude},
{"start", "()V", (void *)android_media_MediaRecorder_start},
+ {"pause", "()V", (void *)android_media_MediaRecorder_pause},
{"stop", "()V", (void *)android_media_MediaRecorder_stop},
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 2ce2a90..b481b46 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -130,6 +130,18 @@ android_mtp_MtpServer_send_device_property_changed(JNIEnv *env, jobject thiz, ji
}
static void
+android_mtp_MtpServer_send_object_updated(JNIEnv *env, jobject thiz, jint handle)
+{
+ Mutex::Autolock autoLock(sMutex);
+
+ MtpServer* server = getMtpServer(env, thiz);
+ if (server)
+ server->sendObjectUpdated(handle);
+ else
+ ALOGE("server is null in send_object_updated");
+}
+
+static void
android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
{
Mutex::Autolock autoLock(sMutex);
@@ -188,6 +200,7 @@ static JNINativeMethod gMethods[] = {
{"native_send_object_removed", "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
{"native_send_device_property_changed", "(I)V",
(void *)android_mtp_MtpServer_send_device_property_changed},
+ {"native_send_object_updated", "(I)V", (void *)android_mtp_MtpServer_send_object_updated},
{"native_add_storage", "(Landroid/mtp/MtpStorage;)V",
(void *)android_mtp_MtpServer_add_storage},
{"native_remove_storage", "(I)V", (void *)android_mtp_MtpServer_remove_storage},
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index aba4bbe..c392034 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -162,6 +162,15 @@ static void effectCallback(int event, void* user, void *info) {
ALOGV("EVENT_PARAMETER_CHANGED");
break;
case AudioEffect::EVENT_ERROR:
+ if (info == 0) {
+ ALOGW("EVENT_ERROR info == NULL");
+ goto effectCallback_Exit;
+ }
+ status_t status = *(status_t *)info;
+ if (status == DEAD_OBJECT) {
+ ALOGE("effectCallback: Client died, no need to send callback");
+ goto effectCallback_Exit;
+ }
ALOGW("EVENT_ERROR");
break;
}
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 0557019..10df3ae 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -248,6 +248,7 @@ static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
v->incStrong((void*)setVisualizer);
}
if (old != 0) {
+ old->cancelCaptureCallBack();
old->decStrong((void*)setVisualizer);
}
env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)v.get());
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
index e884aba..eac5c28 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioTrackTest.java
@@ -386,6 +386,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+ final int TEST_LOOP_CNT = 10;
//-------- initialization --------------
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
@@ -399,9 +400,14 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
track.play();
Thread.sleep(100);
track.stop();
- Thread.sleep(100); // TODO: what is a sensible value?
- int pos = track.getPlaybackHeadPosition();
- log(TEST_NAME, "position ="+ pos);
+ int count = 0;
+ int pos;
+ do {
+ Thread.sleep(200);
+ pos = track.getPlaybackHeadPosition();
+ count++;
+ } while((pos != 0) && (count < TEST_LOOP_CNT));
+ log(TEST_NAME, "position =" + pos + ", read count ="+count);
assertTrue(TEST_NAME, pos == 0);
//-------- tear down --------------
track.release();