diff options
Diffstat (limited to 'media')
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(); |