summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioFormat.java89
-rw-r--r--media/java/android/media/AudioManager.java21
-rw-r--r--media/java/android/media/AudioRecord.java3
-rw-r--r--media/java/android/media/AudioTrack.java20
-rw-r--r--media/java/android/media/ClosedCaptionRenderer.java30
-rw-r--r--media/java/android/media/ExifInterface.java35
-rw-r--r--media/java/android/media/Image.java4
-rw-r--r--media/java/android/media/MediaFormat.java3
-rw-r--r--media/java/android/media/MediaPlayer.java48
-rw-r--r--media/java/android/media/Ringtone.java40
-rw-r--r--media/java/android/media/Utils.java3
-rw-r--r--media/java/android/media/midi/IMidiDeviceOpenCallback.aidl25
-rw-r--r--media/java/android/media/midi/IMidiDeviceServer.aidl1
-rw-r--r--media/java/android/media/midi/IMidiManager.aidl15
-rw-r--r--media/java/android/media/midi/MidiDevice.java27
-rw-r--r--media/java/android/media/midi/MidiDeviceServer.java16
-rw-r--r--media/java/android/media/midi/MidiDeviceService.java11
-rw-r--r--media/java/android/media/midi/MidiManager.java118
-rw-r--r--media/java/android/media/midi/package.html6
-rw-r--r--media/java/android/media/session/MediaController.java4
-rw-r--r--media/java/android/media/tv/DvbDeviceInfo.java4
-rw-r--r--media/java/android/media/tv/ITvInputSessionWrapper.java5
-rw-r--r--media/java/android/media/tv/TvContract.java5
-rw-r--r--media/java/android/media/tv/TvInputInfo.java13
-rw-r--r--media/java/android/media/tv/TvInputManager.java33
-rw-r--r--media/java/android/media/tv/TvInputService.java23
-rw-r--r--media/jni/Android.mk2
-rw-r--r--media/jni/android_media_MediaCrypto.cpp22
-rw-r--r--media/jni/android_media_MediaPlayer.cpp2
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp115
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp148
-rw-r--r--media/jni/soundpool/Android.mk2
-rw-r--r--media/jni/soundpool/SoundPool.cpp1
-rw-r--r--media/packages/BluetoothMidiService/AndroidManifest.xml3
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java26
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java21
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java24
-rw-r--r--media/tests/audiotests/Android.mk2
38 files changed, 582 insertions, 388 deletions
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index ee6d661..c29ec0d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -18,16 +18,19 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/**
- * The <code>AudioFormat</code> class is used to access a number of audio format and
+ * The {@link AudioFormat} class is used to access a number of audio format and
* channel configuration constants. They are for instance used
* in {@link AudioTrack} and {@link AudioRecord}, as valid values in individual parameters of
* constructors like {@link AudioTrack#AudioTrack(int, int, int, int, int, int)}, where the fourth
* parameter is one of the <code>AudioFormat.ENCODING_*</code> constants.
+ * The <code>AudioFormat</code> constants are also used in {@link MediaFormat} to specify
+ * audio related values commonly used in media, such as for {@link MediaFormat#KEY_CHANNEL_MASK}.
* <p>The {@link AudioFormat.Builder} class can be used to create instances of
* the <code>AudioFormat</code> format class.
* Refer to
@@ -39,6 +42,9 @@ import java.util.Arrays;
* <li><a href="#encoding">encoding</a>
* <li><a href="#channelMask">channel masks</a>
* </ol>
+ * <p>Closely associated with the <code>AudioFormat</code> is the notion of an
+ * <a href="#audioFrame">audio frame</a>, which is used throughout the documentation
+ * to represent the minimum size complete unit of audio data.
*
* <h4 id="sampleRate">Sample rate</h4>
* <p>Expressed in Hz, the sample rate in an <code>AudioFormat</code> instance expresses the number
@@ -48,10 +54,69 @@ import java.util.Arrays;
* can be played on a device operating at a sample rate of 48000Hz; the sample rate conversion is
* automatically handled by the platform, it will not play at 6x speed.
*
+ * <p>As of API {@link android.os.Build.VERSION_CODES#MNC},
+ * sample rates up to 192kHz are supported
+ * for <code>AudioRecord</code> and <code>AudioTrack</code>, with sample rate conversion
+ * performed as needed.
+ * To improve efficiency and avoid lossy conversions, it is recommended to match the sample rate
+ * for <code>AudioRecord</code> and <code>AudioTrack</code> to the endpoint device
+ * sample rate, and limit the sample rate to no more than 48kHz unless there are special
+ * device capabilities that warrant a higher rate.
+ *
* <h4 id="encoding">Encoding</h4>
- * <p>For PCM audio, audio encoding is used to describe the bit representation of an audio data
- * sample; for example, the size as 8 bit, 16 bit, and the representation as integer or float.
- * <br>For compressed formats, audio encoding is used to describe the compression scheme being used.
+ * <p>Audio encoding is used to describe the bit representation of audio data, which can be
+ * either linear PCM or compressed audio, such as AC3 or DTS.
+ * <p>For linear PCM, the audio encoding describes the sample size, 8 bits, 16 bits, or 32 bits,
+ * and the sample representation, integer or float.
+ * <ul>
+ * <li> {@link #ENCODING_PCM_8BIT}: The audio sample is a 8 bit unsigned integer in the
+ * range [0, 255], with a 128 offset for zero. This is typically stored as a Java byte in a
+ * byte array or ByteBuffer. Since the Java byte is <em>signed</em>,
+ * be careful with math operations and conversions as the most significant bit is inverted.
+ * </li>
+ * <li> {@link #ENCODING_PCM_16BIT}: The audio sample is a 16 bit signed integer
+ * typically stored as a Java short in a short array, but when the short
+ * is stored in a ByteBuffer, it is native endian (as compared to the default Java big endian).
+ * The short has full range from [-32768, 32767],
+ * and is sometimes interpreted as fixed point Q.15 data.
+ * </li>
+ * <li> {@link #ENCODING_PCM_FLOAT}: Introduced in
+ * API {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this encoding specifies that
+ * the audio sample is a 32 bit IEEE single precision float. The sample can be
+ * manipulated as a Java float in a float array, though within a ByteBuffer
+ * it is stored in native endian byte order.
+ * The nominal range of <code>ENCODING_PCM_FLOAT</code> audio data is [-1.0, 1.0].
+ * It is implementation dependent whether the positive maximum of 1.0 is included
+ * in the interval. Values outside of the nominal range are clamped before
+ * sending to the endpoint device. Beware that
+ * the handling of NaN is undefined; subnormals may be treated as zero; and
+ * infinities are generally clamped just like other values for <code>AudioTrack</code>
+ * &ndash; try to avoid infinities because they can easily generate a NaN.
+ * <br>
+ * To achieve higher audio bit depth than a signed 16 bit integer short,
+ * it is recommended to use <code>ENCODING_PCM_FLOAT</code> for audio capture, processing,
+ * and playback.
+ * Floats are efficiently manipulated by modern CPUs,
+ * have greater precision than 24 bit signed integers,
+ * and have greater dynamic range than 32 bit signed integers.
+ * <code>AudioRecord</code> as of API {@link android.os.Build.VERSION_CODES#MNC} and
+ * <code>AudioTrack</code> as of API {@link android.os.Build.VERSION_CODES#LOLLIPOP}
+ * support <code>ENCODING_PCM_FLOAT</code>.
+ * </li>
+ * </ul>
+ * <p>For compressed audio, the encoding specifies the method of compression,
+ * for example {@link #ENCODING_AC3} and {@link #ENCODING_DTS}. The compressed
+ * audio data is typically stored as bytes in
+ * a byte array or ByteBuffer. When a compressed audio encoding is specified
+ * for an <code>AudioTrack</code>, it creates a direct (non-mixed) track
+ * for output to an endpoint (such as HDMI) capable of decoding the compressed audio.
+ * For (most) other endpoints, which are not capable of decoding such compressed audio,
+ * you will need to decode the data first, typically by creating a {@link MediaCodec}.
+ * Alternatively, one may use {@link MediaPlayer} for playback of compressed
+ * audio files or streams.
+ * <p>When compressed audio is sent out through a direct <code>AudioTrack</code>,
+ * it need not be written in exact multiples of the audio access unit;
+ * this differs from <code>MediaCodec</code> input buffers.
*
* <h4 id="channelMask">Channel mask</h4>
* <p>Channel masks are used in <code>AudioTrack</code> and <code>AudioRecord</code> to describe
@@ -127,6 +192,22 @@ import java.util.Arrays;
* about position it corresponds to, in which case the channel index mask is <code>0xC</code>.
* Multichannel <code>AudioRecord</code> sessions should use channel index masks.
* </ul>
+ * <h4 id="audioFrame">Audio Frame</h4>
+ * <p>For linear PCM, an audio frame consists of a set of samples captured at the same time,
+ * whose count and
+ * channel association are given by the <a href="#channelMask">channel mask</a>,
+ * and whose sample contents are specified by the <a href="#encoding">encoding</a>.
+ * For example, a stereo 16 bit PCM frame consists of
+ * two 16 bit linear PCM samples, with a frame size of 4 bytes.
+ * For compressed audio, an audio frame may alternately
+ * refer to an access unit of compressed data bytes that is logically grouped together for
+ * decoding and bitstream access (e.g. {@link MediaCodec}),
+ * or a single byte of compressed data (e.g. {@link AudioTrack#getBufferSizeInFrames()
+ * AudioTrack.getBufferSizeInFrames()}),
+ * or the linear PCM frame result from decoding the compressed data
+ * (e.g.{@link AudioTrack#getPlaybackHeadPosition()
+ * AudioTrack.getPlaybackHeadPosition()}),
+ * depending on the context where audio frame is used.
*/
public class AudioFormat {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 316ccf6..33db9cf 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -38,7 +38,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.SystemClock;
import android.os.ServiceManager;
import android.provider.Settings;
@@ -69,16 +68,6 @@ public class AudioManager {
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
/**
- * System properties for whether the default microphone and speaker paths support
- * near-ultrasound frequencies (range of 18 - 21 kHz).
- */
- private static final String SYSTEM_PROPERTY_MIC_NEAR_ULTRASOUND =
- "persist.audio.mic.ultrasound";
- private static final String SYSTEM_PROPERTY_SPEAKER_NEAR_ULTRASOUND =
- "persist.audio.spkr.ultrasound";
- private static final String DEFAULT_RESULT_FALSE_STRING = "false";
-
- /**
* 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
* be sent when a wired headset is unplugged, or when an A2DP audio
@@ -3213,11 +3202,13 @@ public class AudioManager {
int outputFramesPerBuffer = AudioSystem.getPrimaryOutputFrameCount();
return outputFramesPerBuffer > 0 ? Integer.toString(outputFramesPerBuffer) : null;
} else if (PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND.equals(key)) {
- return SystemProperties.get(SYSTEM_PROPERTY_MIC_NEAR_ULTRASOUND,
- DEFAULT_RESULT_FALSE_STRING);
+ // Will throw a RuntimeException Resources.NotFoundException if this config value is
+ // not found.
+ return String.valueOf(getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_supportMicNearUltrasound));
} else if (PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND.equals(key)) {
- return SystemProperties.get(SYSTEM_PROPERTY_SPEAKER_NEAR_ULTRASOUND,
- DEFAULT_RESULT_FALSE_STRING);
+ return String.valueOf(getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_supportSpeakerNearUltrasound));
} else {
// null or unknown key
return null;
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 7eb1357..3cbc405 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1277,7 +1277,8 @@ public class AudioRecord
native_enableDeviceCallback();
}
mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
}
}
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index f76189c..f395cb3 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1990,9 +1990,24 @@ public class AudioTrack
* The dead object error code is not returned if some data was successfully transferred.
* In this case, the error is returned at the next write().
*/
- public int write(ByteBuffer audioData, int sizeInBytes,
+ public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
@WriteMode int writeMode, long timestamp) {
+ if (mState == STATE_UNINITIALIZED) {
+ Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED");
+ return ERROR_INVALID_OPERATION;
+ }
+
+ if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+ Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+ return ERROR_BAD_VALUE;
+ }
+
+ if (mDataLoadMode != MODE_STREAM) {
+ Log.e(TAG, "AudioTrack.write() with timestamp called for non-streaming mode track");
+ return ERROR_INVALID_OPERATION;
+ }
+
if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) == 0) {
Log.d(TAG, "AudioTrack.write() called on a regular AudioTrack. Ignoring pts...");
return write(audioData, sizeInBytes, writeMode);
@@ -2229,7 +2244,8 @@ public class AudioTrack
native_enableDeviceCallback();
}
mRoutingChangeListeners.put(
- listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+ listener, new NativeRoutingEventHandlerDelegate(this, listener,
+ handler != null ? handler : new Handler(mInitializationLooper)));
}
}
}
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index e3680e9..8403c1c 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -1044,42 +1044,26 @@ class CCParser {
}
/**
- * @hide
- *
- * MutableBackgroundColorSpan
- *
- * This is a mutable version of BackgroundSpan to facilitate text
- * rendering with edge styles.
+ * Mutable version of BackgroundSpan to facilitate text rendering with edge
+ * styles.
*
+ * @hide
*/
-class MutableBackgroundColorSpan extends CharacterStyle
- implements UpdateAppearance, ParcelableSpan {
+class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance {
private int mColor;
public MutableBackgroundColorSpan(int color) {
mColor = color;
}
- public MutableBackgroundColorSpan(Parcel src) {
- mColor = src.readInt();
- }
+
public void setBackgroundColor(int color) {
mColor = color;
}
+
public int getBackgroundColor() {
return mColor;
}
- @Override
- public int getSpanTypeId() {
- return TextUtils.BACKGROUND_COLOR_SPAN;
- }
- @Override
- public int describeContents() {
- return 0;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mColor);
- }
+
@Override
public void updateDrawState(TextPaint ds) {
ds.bgColor = mColor;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 9db35fc..6bf5721 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -57,6 +57,21 @@ public class ExifInterface {
public static final String TAG_APERTURE = "FNumber";
/** Type is String. */
public static final String TAG_ISO = "ISOSpeedRatings";
+ /** Type is String. */
+ public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME = "SubSecTime";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+
+
+
+ /**
+ * @hide
+ */
+ public static final String TAG_SUBSECTIME = "SubSecTime";
/**
* The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
@@ -346,7 +361,7 @@ public class ExifInterface {
}
/**
- * Returns number of milliseconds since Jan. 1, 1970, midnight.
+ * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
* Returns -1 if the date time information if not available.
* @hide
*/
@@ -356,9 +371,24 @@ public class ExifInterface {
ParsePosition pos = new ParsePosition(0);
try {
+ // The exif field is in local time. Parsing it as if it is UTC will yield time
+ // since 1/1/1970 local time
Date datetime = sFormatter.parse(dateTimeString, pos);
if (datetime == null) return -1;
- return datetime.getTime();
+ long msecs = datetime.getTime();
+
+ String subSecs = mAttributes.get(TAG_SUBSECTIME);
+ if (subSecs != null) {
+ try {
+ long sub = Long.valueOf(subSecs);
+ while (sub > 1000) {
+ sub /= 10;
+ }
+ msecs += sub;
+ } catch (NumberFormatException e) {
+ }
+ }
+ return msecs;
} catch (IllegalArgumentException ex) {
return -1;
}
@@ -375,7 +405,6 @@ public class ExifInterface {
if (date == null || time == null) return -1;
String dateTimeString = date + ' ' + time;
- if (dateTimeString == null) return -1;
ParsePosition pos = new ParsePosition(0);
try {
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index e18e9a3..045216b 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -228,7 +228,9 @@ public abstract class Image implements AutoCloseable {
if (cropRect != null) {
cropRect = new Rect(cropRect); // make a copy
- cropRect.intersect(0, 0, getWidth(), getHeight());
+ if (!cropRect.intersect(0, 0, getWidth(), getHeight())) {
+ cropRect.setEmpty();
+ }
}
mCropRect = cropRect;
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 0e67daa..b2fa0ac 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -106,9 +106,6 @@ public final class MediaFormat {
public static final String MIMETYPE_AUDIO_FLAC = "audio/flac";
public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
- /**
- * @hide
- */
public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
/**
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 668f80a..13714d3 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -77,7 +77,6 @@ import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.Vector;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.lang.ref.WeakReference;
/**
@@ -624,9 +623,6 @@ public class MediaPlayer implements SubtitleController.Listener
private int mUsage = -1;
private boolean mBypassInterruptionPolicy;
- // use AtomicBoolean instead of boolean so we can use the same member both as a flag and a lock.
- private AtomicBoolean mPreparing = new AtomicBoolean();
-
/**
* Default constructor. Consider using one of the create() methods for
* synchronously instantiating a MediaPlayer from a Uri or resource.
@@ -1166,10 +1162,6 @@ public class MediaPlayer implements SubtitleController.Listener
* @throws IllegalStateException if it is called in an invalid state
*/
public void prepare() throws IOException, IllegalStateException {
- // The synchronous version of prepare also recieves a MEDIA_PREPARED message.
- synchronized (mPreparing) {
- mPreparing.set(true);
- }
_prepare();
scanInternalSubtitleTracks();
}
@@ -1186,14 +1178,7 @@ public class MediaPlayer implements SubtitleController.Listener
*
* @throws IllegalStateException if it is called in an invalid state
*/
- public void prepareAsync() throws IllegalStateException {
- synchronized (mPreparing) {
- mPreparing.set(true);
- }
- _prepareAsync();
- }
-
- private native void _prepareAsync() throws IllegalStateException;
+ public native void prepareAsync() throws IllegalStateException;
/**
* Starts or resumes playback. If playback had previously been paused,
@@ -1244,9 +1229,6 @@ public class MediaPlayer implements SubtitleController.Listener
* initialized.
*/
public void stop() throws IllegalStateException {
- synchronized (mPreparing) {
- mPreparing.set(false);
- }
stayAwake(false);
_stop();
}
@@ -1676,9 +1658,6 @@ public class MediaPlayer implements SubtitleController.Listener
* at the same time.
*/
public void release() {
- synchronized (mPreparing) {
- mPreparing.set(false);
- }
stayAwake(false);
updateSurfaceScreenOn();
mOnPreparedListener = null;
@@ -1705,9 +1684,6 @@ public class MediaPlayer implements SubtitleController.Listener
* data source and calling prepare().
*/
public void reset() {
- synchronized (mPreparing) {
- mPreparing.set(false);
- }
mSelectedSubtitleTrackIndex = -1;
synchronized(mOpenSubtitleSources) {
for (final InputStream is: mOpenSubtitleSources) {
@@ -2828,11 +2804,15 @@ public class MediaPlayer implements SubtitleController.Listener
}
switch(msg.what) {
case MEDIA_PREPARED:
- synchronized (mPreparing) {
- if (mPreparing.get()) {
- scanInternalSubtitleTracks();
- mPreparing.set(false);
- }
+ try {
+ scanInternalSubtitleTracks();
+ } catch (RuntimeException e) {
+ // send error message instead of crashing;
+ // send error message instead of inlining a call to onError
+ // to avoid code duplication.
+ Message msg2 = obtainMessage(
+ MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+ sendMessage(msg2);
}
if (mOnPreparedListener != null)
mOnPreparedListener.onPrepared(mMediaPlayer);
@@ -2908,7 +2888,13 @@ public class MediaPlayer implements SubtitleController.Listener
Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
break;
case MEDIA_INFO_METADATA_UPDATE:
- scanInternalSubtitleTracks();
+ try {
+ scanInternalSubtitleTracks();
+ } catch (RuntimeException e) {
+ Message msg2 = obtainMessage(
+ MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+ sendMessage(msg2);
+ }
// fall through
case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 8441541..166ff38 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
+import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
@@ -29,6 +30,7 @@ import android.provider.Settings;
import android.util.Log;
import java.io.IOException;
+import java.util.ArrayList;
/**
* Ringtone provides a quick method for playing a ringtone, notification, or
@@ -49,6 +51,9 @@ public class Ringtone {
MediaStore.Audio.Media.TITLE
};
+ // keep references on active Ringtones until stopped or completion listener called.
+ private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
+
private final Context mContext;
private final AudioManager mAudioManager;
@@ -62,6 +67,7 @@ public class Ringtone {
private final Binder mRemoteToken;
private MediaPlayer mLocalPlayer;
+ private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
private Uri mUri;
private String mTitle;
@@ -247,7 +253,7 @@ public class Ringtone {
// (typically because ringer mode is silent).
if (mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
- mLocalPlayer.start();
+ startLocalPlayer();
}
} else if (mAllowRemote && (mRemotePlayer != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
@@ -285,7 +291,21 @@ public class Ringtone {
mLocalPlayer.reset();
mLocalPlayer.release();
mLocalPlayer = null;
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.remove(this);
+ }
+ }
+ }
+
+ private void startLocalPlayer() {
+ if (mLocalPlayer == null) {
+ return;
}
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.add(this);
+ }
+ mLocalPlayer.setOnCompletionListener(mCompletionListener);
+ mLocalPlayer.start();
}
/**
@@ -330,7 +350,7 @@ public class Ringtone {
}
mLocalPlayer.setAudioAttributes(mAudioAttributes);
mLocalPlayer.prepare();
- mLocalPlayer.start();
+ startLocalPlayer();
afd.close();
return true;
} else {
@@ -352,4 +372,20 @@ public class Ringtone {
void setTitle(String title) {
mTitle = title;
}
+
+ @Override
+ protected void finalize() {
+ if (mLocalPlayer != null) {
+ mLocalPlayer.release();
+ }
+ }
+
+ class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
+ public void onCompletion(MediaPlayer mp)
+ {
+ synchronized (sActiveRingtones) {
+ sActiveRingtones.remove(Ringtone.this);
+ }
+ }
+ }
}
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index 9e01e65..2cd3c43 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -209,6 +209,9 @@ class Utils {
}
static int parseIntSafely(Object o, int fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
return Integer.parseInt(s);
diff --git a/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl b/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
new file mode 100644
index 0000000..6e3dbbf
--- /dev/null
+++ b/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.media.midi.IMidiDeviceServer;
+
+/** @hide */
+oneway interface IMidiDeviceOpenCallback
+{
+ void onDeviceOpened(in IMidiDeviceServer server, IBinder token);
+}
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
index 96d12fd..e30796d 100644
--- a/media/java/android/media/midi/IMidiDeviceServer.aidl
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -25,6 +25,7 @@ interface IMidiDeviceServer
ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
void closePort(IBinder token);
+ void closeDevice();
// connects the input port pfd to the specified output port
void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index fcd4aff..d3c8e0a 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -16,7 +16,10 @@
package android.media.midi;
+import android.bluetooth.BluetoothDevice;
import android.media.midi.IMidiDeviceListener;
+import android.media.midi.IMidiDeviceOpenCallback;
+import android.media.midi.IMidiDeviceServer;
import android.media.midi.IMidiDeviceServer;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceStatus;
@@ -29,11 +32,13 @@ interface IMidiManager
MidiDeviceInfo[] getDevices();
// for device creation & removal notifications
- void registerListener(IBinder token, in IMidiDeviceListener listener);
- void unregisterListener(IBinder token, in IMidiDeviceListener listener);
+ void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
+ void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
- // for opening built-in MIDI devices
- IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device);
+ void openDevice(IBinder clientToken, in MidiDeviceInfo device, in IMidiDeviceOpenCallback callback);
+ void openBluetoothDevice(IBinder clientToken, in BluetoothDevice bluetoothDevice,
+ in IMidiDeviceOpenCallback callback);
+ void closeDevice(IBinder clientToken, IBinder deviceToken);
// for registering built-in MIDI devices
MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
@@ -52,5 +57,5 @@ interface IMidiManager
// used by MIDI devices to report their status
// the token is used by MidiService for death notification
- void setDeviceStatus(IBinder token, in MidiDeviceStatus status);
+ void setDeviceStatus(in IMidiDeviceServer server, in MidiDeviceStatus status);
}
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
index 6526adc..7998a92 100644
--- a/media/java/android/media/midi/MidiDevice.java
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -16,8 +16,6 @@
package android.media.midi;
-import android.content.Context;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -40,9 +38,9 @@ public final class MidiDevice implements Closeable {
private final MidiDeviceInfo mDeviceInfo;
private final IMidiDeviceServer mDeviceServer;
- private Context mContext;
- private ServiceConnection mServiceConnection;
-
+ private final IMidiManager mMidiManager;
+ private final IBinder mClientToken;
+ private final IBinder mDeviceToken;
private final CloseGuard mGuard = CloseGuard.get();
@@ -71,16 +69,13 @@ public final class MidiDevice implements Closeable {
}
}
- /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
- this(deviceInfo, server, null, null);
- }
-
/* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
- Context context, ServiceConnection serviceConnection) {
+ IMidiManager midiManager, IBinder clientToken, IBinder deviceToken) {
mDeviceInfo = deviceInfo;
mDeviceServer = server;
- mContext = context;
- mServiceConnection = serviceConnection;
+ mMidiManager = midiManager;
+ mClientToken = clientToken;
+ mDeviceToken = deviceToken;
mGuard.open("close");
}
@@ -170,10 +165,10 @@ public final class MidiDevice implements Closeable {
public void close() throws IOException {
synchronized (mGuard) {
mGuard.close();
- if (mContext != null && mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- mContext = null;
- mServiceConnection = null;
+ try {
+ mMidiManager.closeDevice(mClientToken, mDeviceToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in closeDevice");
}
}
}
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index a316a44..1b56e1c 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -65,7 +65,6 @@ public final class MidiDeviceServer implements Closeable {
// for reporting device status
- private final IBinder mDeviceStatusToken = new Binder();
private final boolean[] mInputPortOpen;
private final int[] mOutputPortOpenCount;
@@ -81,6 +80,11 @@ public final class MidiDeviceServer implements Closeable {
* @param status the {@link MidiDeviceStatus} for the device
*/
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status);
+
+ /**
+ * Called to notify when the device is closed
+ */
+ public void onClose();
}
abstract private class PortClient implements IBinder.DeathRecipient {
@@ -242,6 +246,14 @@ public final class MidiDeviceServer implements Closeable {
}
@Override
+ public void closeDevice() {
+ if (mCallback != null) {
+ mCallback.onClose();
+ }
+ IoUtils.closeQuietly(MidiDeviceServer.this);
+ }
+
+ @Override
public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
int outputPortNumber) {
MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
@@ -305,7 +317,7 @@ public final class MidiDeviceServer implements Closeable {
mCallback.onDeviceStatusChanged(this, status);
}
try {
- mMidiManager.setDeviceStatus(mDeviceStatusToken, status);
+ mMidiManager.setDeviceStatus(mServer, status);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in updateDeviceStatus");
} finally {
diff --git a/media/java/android/media/midi/MidiDeviceService.java b/media/java/android/media/midi/MidiDeviceService.java
index ce12a4f..d897ad2 100644
--- a/media/java/android/media/midi/MidiDeviceService.java
+++ b/media/java/android/media/midi/MidiDeviceService.java
@@ -59,6 +59,11 @@ abstract public class MidiDeviceService extends Service {
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
MidiDeviceService.this.onDeviceStatusChanged(status);
}
+
+ @Override
+ public void onClose() {
+ MidiDeviceService.this.onClose();
+ }
};
@Override
@@ -125,6 +130,12 @@ abstract public class MidiDeviceService extends Service {
public void onDeviceStatusChanged(MidiDeviceStatus status) {
}
+ /**
+ * Called to notify when our device has been closed by all its clients
+ */
+ public void onClose() {
+ }
+
@Override
public IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index d19cf36..0beb9a4 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -17,11 +17,6 @@
package android.media.midi;
import android.bluetooth.BluetoothDevice;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.Bundle;
@@ -52,16 +47,17 @@ public final class MidiManager {
/**
* BluetoothMidiService package name
+ * @hide
*/
- private static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
+ public static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
/**
* BluetoothMidiService class name
+ * @hide
*/
- private static final String BLUETOOTH_MIDI_SERVICE_CLASS =
+ public static final String BLUETOOTH_MIDI_SERVICE_CLASS =
"com.android.bluetoothmidiservice.BluetoothMidiService";
- private final Context mContext;
private final IMidiManager mService;
private final IBinder mToken = new Binder();
@@ -166,8 +162,7 @@ public final class MidiManager {
/**
* @hide
*/
- public MidiManager(Context context, IMidiManager service) {
- mContext = context;
+ public MidiManager(IMidiManager service) {
mService = service;
}
@@ -237,7 +232,7 @@ public final class MidiManager {
* Opens a MIDI device for reading and writing.
*
* @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open
- * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called
+ * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called
* to receive the result
* @param handler the {@link android.os.Handler Handler} that will be used for delivering
* the result. If handler is null, then the thread used for the
@@ -245,52 +240,28 @@ public final class MidiManager {
*/
public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener,
Handler handler) {
- MidiDevice device = null;
- try {
- IMidiDeviceServer server = mService.openDevice(mToken, deviceInfo);
- if (server == null) {
- ServiceInfo serviceInfo = (ServiceInfo)deviceInfo.getProperties().getParcelable(
- MidiDeviceInfo.PROPERTY_SERVICE_INFO);
- if (serviceInfo == null) {
- Log.e(TAG, "no ServiceInfo for " + deviceInfo);
- } else {
- Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
- intent.setComponent(new ComponentName(serviceInfo.packageName,
- serviceInfo.name));
- final MidiDeviceInfo deviceInfoF = deviceInfo;
- final OnDeviceOpenedListener listenerF = listener;
- final Handler handlerF = handler;
- if (mContext.bindService(intent,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- IMidiDeviceServer server =
- IMidiDeviceServer.Stub.asInterface(binder);
- MidiDevice device = new MidiDevice(deviceInfoF, server, mContext,
- this);
- sendOpenDeviceResponse(device, listenerF, handlerF);
- }
+ final MidiDeviceInfo deviceInfoF = deviceInfo;
+ final OnDeviceOpenedListener listenerF = listener;
+ final Handler handlerF = handler;
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // FIXME - anything to do here?
- }
- },
- Context.BIND_AUTO_CREATE))
- {
- // return immediately to avoid calling sendOpenDeviceResponse below
- return;
- } else {
- Log.e(TAG, "Unable to bind service: " + intent);
- }
+ IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
+ @Override
+ public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+ MidiDevice device;
+ if (server != null) {
+ device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken);
+ } else {
+ device = null;
}
- } else {
- device = new MidiDevice(deviceInfo, server);
+ sendOpenDeviceResponse(device, listenerF, handlerF);
}
+ };
+
+ try {
+ mService.openDevice(mToken, deviceInfo, callback);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in openDevice");
}
- sendOpenDeviceResponse(device, listener, handler);
}
/**
@@ -303,38 +274,33 @@ public final class MidiManager {
* the result. If handler is null, then the thread used for the
* listener is unspecified.
*/
- public void openBluetoothDevice(final BluetoothDevice bluetoothDevice,
- final OnDeviceOpenedListener listener, final Handler handler) {
- Intent intent = new Intent(BLUETOOTH_MIDI_SERVICE_INTENT);
- intent.setComponent(new ComponentName(BLUETOOTH_MIDI_SERVICE_PACKAGE,
- BLUETOOTH_MIDI_SERVICE_CLASS));
- intent.putExtra("device", bluetoothDevice);
- if (!mContext.bindService(intent,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- IMidiDeviceServer server =
- IMidiDeviceServer.Stub.asInterface(binder);
+ public void openBluetoothDevice(BluetoothDevice bluetoothDevice,
+ OnDeviceOpenedListener listener, Handler handler) {
+ final OnDeviceOpenedListener listenerF = listener;
+ final Handler handlerF = handler;
+
+ IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
+ @Override
+ public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+ MidiDevice device = null;
+ if (server != null) {
try {
// fetch MidiDeviceInfo from the server
MidiDeviceInfo deviceInfo = server.getDeviceInfo();
- MidiDevice device = new MidiDevice(deviceInfo, server, mContext, this);
- sendOpenDeviceResponse(device, listener, handler);
+ device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
+ sendOpenDeviceResponse(device, listenerF, handlerF);
} catch (RemoteException e) {
- Log.e(TAG, "remote exception in onServiceConnected");
- sendOpenDeviceResponse(null, listener, handler);
+ Log.e(TAG, "remote exception in getDeviceInfo()");
}
}
+ sendOpenDeviceResponse(device, listenerF, handlerF);
+ }
+ };
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // FIXME - anything to do here?
- }
- },
- Context.BIND_AUTO_CREATE))
- {
- Log.e(TAG, "Unable to bind service: " + intent);
- sendOpenDeviceResponse(null, listener, handler);
+ try {
+ mService.openBluetoothDevice(mToken, bluetoothDevice, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in openDevice");
}
}
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 8323bdf..673c4ba 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -255,7 +255,8 @@ messages.</p>
<p>An app can provide a MIDI Service that can be used by other apps. For example,
-an app can provide a custom synthesizer that other apps can send messages to. </p>
+an app can provide a custom synthesizer that other apps can send messages to.
+The service must be guarded with permission &quot;android.permission.BIND_MIDI_DEVICE_SERVICE&quot;.</p>
<h2 id=manifest_files>Manifest Files</h2>
@@ -264,7 +265,8 @@ an app can provide a custom synthesizer that other apps can send messages to. </
AndroidManifest.xml file.</p>
<pre class=prettyprint>
-&lt;service android:name="<strong>MySynthDeviceService</strong>">
+&lt;service android:name="<strong>MySynthDeviceService</strong>"
+ android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
&lt;intent-filter>
&lt;action android:name="android.media.midi.MidiDeviceService" />
&lt;/intent-filter>
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 2acee04..b1a51a5 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -1018,7 +1018,9 @@ public final class MediaController {
}
public void post(int what, Object obj, Bundle data) {
- obtainMessage(what, obj).sendToTarget();
+ Message msg = obtainMessage(what, obj);
+ msg.setData(data);
+ msg.sendToTarget();
}
}
diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
index 1885a34..e07f3a6 100644
--- a/media/java/android/media/tv/DvbDeviceInfo.java
+++ b/media/java/android/media/tv/DvbDeviceInfo.java
@@ -16,14 +16,10 @@
package android.media.tv;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.media.tv.TvInputManager;
import android.util.Log;
-import java.lang.IllegalArgumentException;
-
/**
* Simple container for information about DVB device.
* Not for third-party developers.
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index fed0ddf..5ad1bce 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -285,8 +285,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
@Override
public void timeShiftSeekTo(long timeMs) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TIME_SHIFT_SEEK_TO,
- Long.valueOf(timeMs)));
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TIME_SHIFT_SEEK_TO, timeMs));
}
@Override
@@ -298,7 +297,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
@Override
public void timeShiftEnablePositionTracking(boolean enable) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(
- DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, Boolean.valueOf(enable)));
+ DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, enable));
}
private final class TvInputEventReceiver extends InputEventReceiver {
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index f5a6f2b..91b1a49 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -485,8 +485,7 @@ public final class TvContract {
/** The video resolution for ultra high-definition. */
public static final String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
- private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP =
- new HashMap<String, String>();
+ private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP = new HashMap<>();
static {
VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480I, VIDEO_RESOLUTION_SD);
@@ -1128,7 +1127,7 @@ public final class TvContract {
/** The genre for Tech/Science. */
public static final String TECH_SCIENCE = "TECH_SCIENCE";
- private static final ArraySet<String> CANONICAL_GENRES = new ArraySet<String>();
+ private static final ArraySet<String> CANONICAL_GENRES = new ArraySet<>();
static {
CANONICAL_GENRES.add(FAMILY_KIDS);
CANONICAL_GENRES.add(SPORTS);
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 46d33b4..c537dd6 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -549,8 +549,9 @@ public final class TvInputInfo implements Parcelable {
private static final String generateInputIdForHdmiDevice(
ComponentName name, HdmiDeviceInfo deviceInfo) {
// Example of the format : "/HDMI%04X%02X"
- String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_HDMI_DEVICE,
- LENGTH_HDMI_PHYSICAL_ADDRESS, LENGTH_HDMI_DEVICE_ID);
+ String format = DELIMITER_INFO_IN_ID + PREFIX_HDMI_DEVICE
+ + "%0" + LENGTH_HDMI_PHYSICAL_ADDRESS + "X"
+ + "%0" + LENGTH_HDMI_DEVICE_ID + "X";
return name.flattenToShortString() + String.format(format,
deviceInfo.getPhysicalAddress(), deviceInfo.getId());
}
@@ -564,8 +565,8 @@ public final class TvInputInfo implements Parcelable {
*/
private static final String generateInputIdForHardware(
ComponentName name, TvInputHardwareInfo hardwareInfo) {
- return name.flattenToShortString() + String.format("%s%s%d",
- DELIMITER_INFO_IN_ID, PREFIX_HARDWARE_DEVICE, hardwareInfo.getDeviceId());
+ return name.flattenToShortString() + DELIMITER_INFO_IN_ID + PREFIX_HARDWARE_DEVICE
+ + hardwareInfo.getDeviceId();
}
public static final Parcelable.Creator<TvInputInfo> CREATOR =
@@ -626,7 +627,7 @@ public final class TvInputInfo implements Parcelable {
public static Set<String> getHiddenTvInputIds(Context context, int userId) {
String hiddenIdsString = Settings.Secure.getStringForUser(
context.getContentResolver(), Settings.Secure.TV_INPUT_HIDDEN_INPUTS, userId);
- Set<String> set = new HashSet<String>();
+ Set<String> set = new HashSet<>();
if (TextUtils.isEmpty(hiddenIdsString)) {
return set;
}
@@ -648,7 +649,7 @@ public final class TvInputInfo implements Parcelable {
public static Map<String, String> getCustomLabels(Context context, int userId) {
String labelsString = Settings.Secure.getStringForUser(
context.getContentResolver(), Settings.Secure.TV_INPUT_CUSTOM_LABELS, userId);
- Map<String, String> map = new HashMap<String, String>();
+ Map<String, String> map = new HashMap<>();
if (TextUtils.isEmpty(labelsString)) {
return map;
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 7fc19f1..01de898 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -221,16 +221,15 @@ public final class TvInputManager {
private final Object mLock = new Object();
// @GuardedBy("mLock")
- private final List<TvInputCallbackRecord> mCallbackRecords =
- new LinkedList<TvInputCallbackRecord>();
+ private final List<TvInputCallbackRecord> mCallbackRecords = new LinkedList<>();
// A mapping from TV input ID to the state of corresponding input.
// @GuardedBy("mLock")
- private final Map<String, Integer> mStateMap = new ArrayMap<String, Integer>();
+ private final Map<String, Integer> mStateMap = new ArrayMap<>();
// A mapping from the sequence number of a session to its SessionCallbackRecord.
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
- new SparseArray<SessionCallbackRecord>();
+ new SparseArray<>();
// A sequence number for the next session to be created. Should be protected by a lock
// {@code mSessionCallbackRecordMap}.
@@ -238,8 +237,6 @@ public final class TvInputManager {
private final ITvInputClient mClient;
- private final ITvInputManagerCallback mManagerCallback;
-
private final int mUserId;
/**
@@ -879,7 +876,7 @@ public final class TvInputManager {
}
}
};
- mManagerCallback = new ITvInputManagerCallback.Stub() {
+ ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
@Override
public void onInputStateChanged(String inputId, int state) {
synchronized (mLock) {
@@ -921,7 +918,7 @@ public final class TvInputManager {
};
try {
if (mService != null) {
- mService.registerCallback(mManagerCallback, mUserId);
+ mService.registerCallback(managerCallback, mUserId);
List<TvInputInfo> infos = mService.getTvInputList(mUserId);
synchronized (mLock) {
for (TvInputInfo info : infos) {
@@ -985,7 +982,7 @@ public final class TvInputManager {
Log.w(TAG, "Unrecognized input ID: " + inputId);
return INPUT_STATE_DISCONNECTED;
}
- return state.intValue();
+ return state;
}
}
@@ -1076,7 +1073,7 @@ public final class TvInputManager {
@SystemApi
public List<TvContentRating> getBlockedRatings() {
try {
- List<TvContentRating> ratings = new ArrayList<TvContentRating>();
+ List<TvContentRating> ratings = new ArrayList<>();
for (String rating : mService.getBlockedRatings(mUserId)) {
ratings.add(TvContentRating.unflattenFromString(rating));
}
@@ -1334,8 +1331,8 @@ public final class TvInputManager {
// protect pending input events and the input channel.
private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
- private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
- private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+ private final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
+ private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
private IBinder mToken;
@@ -1344,11 +1341,11 @@ public final class TvInputManager {
private final Object mMetadataLock = new Object();
// @GuardedBy("mMetadataLock")
- private final List<TvTrackInfo> mAudioTracks = new ArrayList<TvTrackInfo>();
+ private final List<TvTrackInfo> mAudioTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
- private final List<TvTrackInfo> mVideoTracks = new ArrayList<TvTrackInfo>();
+ private final List<TvTrackInfo> mVideoTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
- private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<TvTrackInfo>();
+ private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<>();
// @GuardedBy("mMetadataLock")
private String mSelectedAudioTrackId;
// @GuardedBy("mMetadataLock")
@@ -1589,17 +1586,17 @@ public final class TvInputManager {
if (mAudioTracks == null) {
return null;
}
- return new ArrayList<TvTrackInfo>(mAudioTracks);
+ return new ArrayList<>(mAudioTracks);
} else if (type == TvTrackInfo.TYPE_VIDEO) {
if (mVideoTracks == null) {
return null;
}
- return new ArrayList<TvTrackInfo>(mVideoTracks);
+ return new ArrayList<>(mVideoTracks);
} else if (type == TvTrackInfo.TYPE_SUBTITLE) {
if (mSubtitleTracks == null) {
return null;
}
- return new ArrayList<TvTrackInfo>(mSubtitleTracks);
+ return new ArrayList<>(mSubtitleTracks);
}
}
throw new IllegalArgumentException("invalid type: " + type);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 4b84090..50a215c 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -27,7 +27,6 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.PlaybackParams;
-import android.media.tv.TvInputService.HardwareSession;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -98,7 +97,7 @@ public abstract class TvInputService extends Service {
*/
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
- new RemoteCallbackList<ITvInputServiceCallback>();
+ new RemoteCallbackList<>();
private TvInputManager mTvInputManager;
@@ -341,9 +340,13 @@ public abstract class TvInputService extends Service {
}
/**
- * Notifies the channel of the session is retuned by TV input.
+ * Informs the application that the current channel is re-tuned for some reason and the
+ * session now displays the content from a new channel. This is used to handle special cases
+ * such as when the current channel becomes unavailable, it is necessary to send the user to
+ * a certain channel or the user changes channel in some other way (e.g. by using a
+ * dedicated remote).
*
- * @param channelUri The URI of a channel.
+ * @param channelUri The URI of the new channel.
*/
public void notifyChannelRetuned(final Uri channelUri) {
executeOrPostRunnable(new Runnable() {
@@ -374,7 +377,7 @@ public abstract class TvInputService extends Service {
* @throws IllegalArgumentException if {@code tracks} contains redundant tracks.
*/
public void notifyTracksChanged(final List<TvTrackInfo> tracks) {
- Set<String> trackIdSet = new HashSet<String>();
+ Set<String> trackIdSet = new HashSet<>();
for (TvTrackInfo track : tracks) {
String trackId = track.getId();
if (trackIdSet.contains(trackId)) {
@@ -933,6 +936,10 @@ public abstract class TvInputService extends Service {
* Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
* moment.
*
+ * <p>Note that the current playback position should be equal to or greater than the start
+ * playback position reported by {@link #onTimeShiftGetStartPosition}. Failure to notifying
+ * the correct current position might lead to bad user experience.
+ *
* @see #onTimeShiftResume
* @see #onTimeShiftPause
* @see #onTimeShiftSeekTo
@@ -1396,6 +1403,12 @@ public abstract class TvInputService extends Service {
notifyTimeShiftStartPositionChanged(startPositionMs);
}
long currentPositionMs = onTimeShiftGetCurrentPosition();
+ if (currentPositionMs < mStartPositionMs) {
+ Log.w(TAG, "Current position (" + currentPositionMs + ") cannot be earlier than"
+ + " start position (" + mStartPositionMs + "). Reset to the start "
+ + "position.");
+ currentPositionMs = mStartPositionMs;
+ }
if (mCurrentPositionMs != currentPositionMs) {
mCurrentPositionMs = currentPositionMs;
notifyTimeShiftCurrentPositionChanged(currentPositionMs);
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 51d0140..79557bc 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -69,7 +69,7 @@ LOCAL_C_INCLUDES += \
$(PV_INCLUDES) \
$(JNI_H_INCLUDE)
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunreachable-code
LOCAL_MODULE:= libmedia_jni
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index a9accb0..d7968d2 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -301,17 +301,19 @@ static void android_media_MediaCrypto_setMediaDrmSession(
status_t err = crypto->setMediaDrmSession(sessionId);
- String8 msg("setMediaDrmSession failed");
- if (err == ERROR_DRM_SESSION_NOT_OPENED) {
- msg += ": session not opened";
- } else if (err == ERROR_UNSUPPORTED) {
- msg += ": not supported by this crypto scheme";
- } else if (err == NO_INIT) {
- msg += ": crypto plugin not initialized";
- } else if (err != OK) {
- msg.appendFormat(": general failure (%d)", err);
+ if (err != OK) {
+ String8 msg("setMediaDrmSession failed");
+ if (err == ERROR_DRM_SESSION_NOT_OPENED) {
+ msg += ": session not opened";
+ } else if (err == ERROR_UNSUPPORTED) {
+ msg += ": not supported by this crypto scheme";
+ } else if (err == NO_INIT) {
+ msg += ": crypto plugin not initialized";
+ } else {
+ msg.appendFormat(": general failure (%d)", err);
+ }
+ jniThrowException(env, "android/media/MediaCryptoException", msg.string());
}
- jniThrowException(env, "android/media/MediaCryptoException", msg.string());
}
static JNINativeMethod gMethods[] = {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 9c67278..d8041f4 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1045,7 +1045,7 @@ static JNINativeMethod gMethods[] = {
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
- {"_prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
+ {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 96b72a2..fdc586b 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -92,6 +92,7 @@ static jint translateError(int code) {
}
}
+static Mutex sLock;
// ----------------------------------------------------------------------------
static void effectCallback(int event, void* user, void *info) {
@@ -182,6 +183,32 @@ effectCallback_Exit:
}
// ----------------------------------------------------------------------------
+
+static sp<AudioEffect> getAudioEffect(JNIEnv* env, jobject thiz)
+{
+ Mutex::Autolock l(sLock);
+ AudioEffect* const ae =
+ (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+ return sp<AudioEffect>(ae);
+}
+
+static sp<AudioEffect> setAudioEffect(JNIEnv* env, jobject thiz,
+ const sp<AudioEffect>& ae)
+{
+ Mutex::Autolock l(sLock);
+ sp<AudioEffect> old =
+ (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+ if (ae.get()) {
+ ae->incStrong((void*)setAudioEffect);
+ }
+ if (old != 0) {
+ old->decStrong((void*)setAudioEffect);
+ }
+ env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)ae.get());
+ return old;
+}
+
+// ----------------------------------------------------------------------------
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in AudioEffect, which won't run until the
// first time an instance of this class is used.
@@ -257,7 +284,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
- AudioEffect* lpAudioEffect = NULL;
+ sp<AudioEffect> lpAudioEffect;
jint* nId = NULL;
const char *typeStr = NULL;
const char *uuidStr = NULL;
@@ -272,6 +299,8 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
ScopedUtfChars opPackageNameStr(env, opPackageName);
+ setAudioEffect(env, thiz, 0);
+
if (type != NULL) {
typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) { // Out of memory
@@ -324,7 +353,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
&lpJniStorage->mCallbackData,
sessionId,
0);
- if (lpAudioEffect == NULL) {
+ if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
}
@@ -394,7 +423,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
env->SetObjectArrayElement(javadesc, 0, jdesc);
- env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)lpAudioEffect);
+ setAudioEffect(env, thiz, lpAudioEffect);
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
@@ -407,12 +436,9 @@ setup_failure:
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
- if (lpAudioEffect) {
- delete lpAudioEffect;
- }
- env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
-
if (lpJniStorage) {
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
delete lpJniStorage;
}
env->SetLongField(thiz, fields.fidJniData, 0);
@@ -430,20 +456,20 @@ setup_failure:
// ----------------------------------------------------------------------------
-static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
- ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
-
- // delete the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
- if (lpAudioEffect) {
- ALOGV("deleting AudioEffect: %p\n", lpAudioEffect);
- delete lpAudioEffect;
+static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
+ sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
+ if (lpAudioEffect == 0) {
+ return;
}
// delete the JNI data
- AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetLongField(
- thiz, fields.fidJniData);
+ AudioEffectJniStorage* lpJniStorage =
+ (AudioEffectJniStorage *)env->GetLongField(thiz, fields.fidJniData);
+
+ // reset the native resources in the Java object so any attempt to access
+ // them after a call to release fails.
+ env->SetLongField(thiz, fields.fidJniData, 0);
+
if (lpJniStorage) {
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
delete lpJniStorage;
@@ -451,24 +477,16 @@ static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz
}
// ----------------------------------------------------------------------------
-static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
-
- // do everything a call to finalize would
- android_media_AudioEffect_native_finalize(env, thiz);
- // + reset the native resources in the Java object so any attempt to access
- // them after a call to release fails.
- env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
- env->SetLongField(thiz, fields.fidJniData, 0);
+static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
+ android_media_AudioEffect_native_release(env, thiz);
}
static jint
android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for enable()");
return AUDIOEFFECT_ERROR_NO_INIT;
@@ -480,11 +498,8 @@ android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean
static jboolean
android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getEnabled()");
return JNI_FALSE;
@@ -501,11 +516,8 @@ android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
static jboolean
android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for hasControl()");
return JNI_FALSE;
@@ -528,10 +540,8 @@ static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
effect_param_t *p;
int voffset;
- AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
- fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
@@ -591,10 +601,8 @@ android_media_AudioEffect_native_getParameter(JNIEnv *env,
effect_param_t *p;
int voffset;
- AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
- fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
@@ -657,11 +665,8 @@ static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
jbyte* pReplyData = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
- fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
+ sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+ if (lpAudioEffect == 0) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index abc681e..6098b4a 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -102,14 +102,14 @@ struct visualizer_callback_cookie {
};
// ----------------------------------------------------------------------------
-class visualizerJniStorage {
+class VisualizerJniStorage {
public:
visualizer_callback_cookie mCallbackData;
- visualizerJniStorage() {
+ VisualizerJniStorage() {
}
- ~visualizerJniStorage() {
+ ~VisualizerJniStorage() {
}
};
@@ -135,6 +135,7 @@ static jint translateError(int code) {
}
}
+static Mutex sLock;
// ----------------------------------------------------------------------------
static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
@@ -227,15 +228,30 @@ static void captureCallback(void* user,
}
}
-static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
+// ----------------------------------------------------------------------------
+
+static sp<Visualizer> getVisualizer(JNIEnv* env, jobject thiz)
{
- Visualizer *v = (Visualizer *)env->GetLongField(
- thiz, fields.fidNativeVisualizer);
- if (v == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve Visualizer pointer");
+ Mutex::Autolock l(sLock);
+ Visualizer* const v =
+ (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+ return sp<Visualizer>(v);
+}
+
+static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
+ const sp<Visualizer>& v)
+{
+ Mutex::Autolock l(sLock);
+ sp<Visualizer> old =
+ (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+ if (v.get()) {
+ v->incStrong((void*)setVisualizer);
+ }
+ if (old != 0) {
+ old->decStrong((void*)setVisualizer);
}
- return v;
+ env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)v.get());
+ return old;
}
// ----------------------------------------------------------------------------
@@ -318,7 +334,7 @@ static void android_media_visualizer_effect_callback(int32_t event,
void *info) {
if ((event == AudioEffect::EVENT_ERROR) &&
(*((status_t*)info) == DEAD_OBJECT)) {
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage*)user;
+ VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -336,14 +352,16 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
jint sessionId, jintArray jId, jstring opPackageName)
{
ALOGV("android_media_visualizer_native_setup");
- visualizerJniStorage* lpJniStorage = NULL;
+ VisualizerJniStorage* lpJniStorage = NULL;
int lStatus = VISUALIZER_ERROR_NO_MEMORY;
- Visualizer* lpVisualizer = NULL;
+ sp<Visualizer> lpVisualizer;
jint* nId = NULL;
ScopedUtfChars opPackageNameStr(env, opPackageName);
- lpJniStorage = new visualizerJniStorage();
+ setVisualizer(env, thiz, 0);
+
+ lpJniStorage = new VisualizerJniStorage();
if (lpJniStorage == NULL) {
ALOGE("setup: Error creating JNI Storage");
goto setup_failure;
@@ -371,7 +389,7 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
android_media_visualizer_effect_callback,
lpJniStorage,
sessionId);
- if (lpVisualizer == NULL) {
+ if (lpVisualizer == 0) {
ALOGE("Error creating Visualizer");
goto setup_failure;
}
@@ -392,7 +410,7 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
nId = NULL;
- env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)lpVisualizer);
+ setVisualizer(env, thiz, lpVisualizer);
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
@@ -405,12 +423,9 @@ setup_failure:
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
- if (lpVisualizer) {
- delete lpVisualizer;
- }
- env->SetLongField(thiz, fields.fidNativeVisualizer, 0);
-
if (lpJniStorage) {
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
delete lpJniStorage;
}
env->SetLongField(thiz, fields.fidJniData, 0);
@@ -419,49 +434,44 @@ setup_failure:
}
// ----------------------------------------------------------------------------
-static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
- ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
-
- // delete the Visualizer object
- Visualizer* lpVisualizer = (Visualizer *)env->GetLongField(
- thiz, fields.fidNativeVisualizer);
- if (lpVisualizer) {
- ALOGV("deleting Visualizer: %p\n", lpVisualizer);
- delete lpVisualizer;
+static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
+ sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
+ if (lpVisualizer == 0) {
+ return;
}
// delete the JNI data
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(
- thiz, fields.fidJniData);
+ VisualizerJniStorage* lpJniStorage =
+ (VisualizerJniStorage *)env->GetLongField(thiz, fields.fidJniData);
+
+ // reset the native resources in the Java object so any attempt to access
+ // them after a call to release fails.
+ env->SetLongField(thiz, fields.fidJniData, 0);
+
if (lpJniStorage) {
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
delete lpJniStorage;
}
}
-// ----------------------------------------------------------------------------
-static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
-
- // do everything a call to finalize would
- android_media_visualizer_native_finalize(env, thiz);
- // + reset the native resources in the Java object so any attempt to access
- // them after a call to release fails.
- env->SetLongField(thiz, fields.fidNativeVisualizer, 0);
- env->SetLongField(thiz, fields.fidJniData, 0);
+static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
+ android_media_visualizer_native_release(env, thiz);
}
+// ----------------------------------------------------------------------------
static jint
android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
jint retVal = translateError(lpVisualizer->setEnabled(enabled));
if (!enabled) {
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(
+ VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(
thiz, fields.fidJniData);
if (NULL != lpJniStorage)
@@ -474,8 +484,8 @@ android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean e
static jboolean
android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return JNI_FALSE;
}
@@ -507,8 +517,8 @@ android_media_visualizer_native_getMaxCaptureRate(JNIEnv* /* env */, jobject /*
static jint
android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -518,8 +528,8 @@ android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint s
static jint
android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return -1;
}
return (jint) lpVisualizer->getCaptureSize();
@@ -528,8 +538,8 @@ android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
static jint
android_media_visualizer_native_setScalingMode(JNIEnv *env, jobject thiz, jint mode)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -539,8 +549,8 @@ android_media_visualizer_native_setScalingMode(JNIEnv *env, jobject thiz, jint m
static jint
android_media_visualizer_native_getScalingMode(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return -1;
}
return (jint)lpVisualizer->getScalingMode();
@@ -549,8 +559,8 @@ android_media_visualizer_native_getScalingMode(JNIEnv *env, jobject thiz)
static jint
android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, jint mode)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
return translateError(lpVisualizer->setMeasurementMode(mode));
@@ -559,8 +569,8 @@ android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, ji
static jint
android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return MEASUREMENT_MODE_NONE;
}
return lpVisualizer->getMeasurementMode();
@@ -569,8 +579,8 @@ android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz)
static jint
android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return -1;
}
return (jint) lpVisualizer->getSamplingRate();
@@ -579,8 +589,8 @@ android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
static jint
android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -597,8 +607,8 @@ android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArra
static jint
android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
@@ -616,8 +626,8 @@ android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFf
static jint
android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jPeakRmsObj)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
int32_t measurements[2];
@@ -635,11 +645,11 @@ android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jP
static jint
android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
{
- Visualizer* lpVisualizer = getVisualizer(env, thiz);
- if (lpVisualizer == NULL) {
+ sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == 0) {
return VISUALIZER_ERROR_NO_INIT;
}
- visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(thiz,
+ VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(thiz,
fields.fidJniData);
if (lpJniStorage == NULL) {
return VISUALIZER_ERROR_NO_INIT;
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 2476056..2bc41b5 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -18,6 +18,6 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_MODULE:= libsoundpool
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunreachable-code
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 8038cdf..a705bcc 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -753,6 +753,7 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV
status = newTrack->initCheck();
if (status != NO_ERROR) {
ALOGE("Error creating AudioTrack");
+ // newTrack goes out of scope, so reference count drops to zero
goto exit;
}
// From now on, AudioTrack callbacks received with previous toggle value will be ignored.
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 15aa581..b0b389a 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -8,7 +8,8 @@
<application
android:label="@string/app_name">
- <service android:name="BluetoothMidiService">
+ <service android:name="BluetoothMidiService"
+ android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
<intent-filter>
<action android:name="android.media.midi.BluetoothMidiService" />
</intent-filter>
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 60c6570..e6d59e4 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -24,10 +24,11 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import android.media.midi.MidiReceiver;
-import android.media.midi.MidiManager;
-import android.media.midi.MidiDeviceServer;
import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
@@ -81,6 +82,18 @@ public final class BluetoothMidiDevice {
private final BluetoothPacketDecoder mPacketDecoder
= new BluetoothPacketDecoder(MAX_PACKET_SIZE);
+ private final MidiDeviceServer.Callback mDeviceServerCallback
+ = new MidiDeviceServer.Callback() {
+ @Override
+ public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+ }
+
+ @Override
+ public void onClose() {
+ close();
+ }
+ };
+
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
@@ -213,7 +226,7 @@ public final class BluetoothMidiDevice {
inputPortReceivers[0] = mEventScheduler.getReceiver();
mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
- null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, null);
+ null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
@@ -248,11 +261,12 @@ public final class BluetoothMidiDevice {
private void close() {
synchronized (mBluetoothDevice) {
- mEventScheduler.close();
+ mEventScheduler.close();
+ mService.deviceClosed(mBluetoothDevice);
+
if (mDeviceServer != null) {
IoUtils.closeQuietly(mDeviceServer);
mDeviceServer = null;
- mService.deviceClosed(mBluetoothDevice);
}
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
index 66ed933..bcfcbf3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/CodecTest.java
@@ -823,13 +823,20 @@ public class CodecTest {
duration = mMediaPlayer.getDuration();
// start to play
mMediaPlayer.start();
- waittime = duration - mMediaPlayer.getCurrentPosition();
- synchronized(onCompletion){
- try {
- onCompletion.wait(waittime + buffertime);
- }catch (Exception e) {
- Log.v(TAG, "playMediaSamples are interrupted");
- return false;
+ if (duration < 0) {
+ Log.w(TAG, filePath + " has unknown duration, waiting until playback completes");
+ while (mMediaPlayer.isPlaying()) {
+ SystemClock.sleep(1000);
+ }
+ } else {
+ waittime = duration - mMediaPlayer.getCurrentPosition();
+ synchronized(onCompletion){
+ try {
+ onCompletion.wait(waittime + buffertime);
+ } catch (Exception e) {
+ Log.v(TAG, "playMediaSamples are interrupted");
+ return false;
+ }
}
}
terminateMessageLooper();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 29c3c75..cd0e587 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -731,27 +731,31 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testReadWriteHighSpeedVideoConfiguration() {
- // int32 x 4 x 1
+ // int32 x 5 x 1
checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
new HighSpeedVideoConfiguration(
- /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200),
+ /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200,
+ /*batchSizeMax*/8),
/* width, height, fpsMin, fpsMax */
- toByteArray(1000, 255, 30, 200));
+ toByteArray(1000, 255, 30, 200, 8));
- // int32 x 4 x 3
+ // int32 x 5 x 3
checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
new HighSpeedVideoConfiguration[] {
new HighSpeedVideoConfiguration(
- /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120),
+ /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120,
+ /*batchSizeMax*/8),
new HighSpeedVideoConfiguration(
- /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200),
+ /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200,
+ /*batchSizeMax*/4),
new HighSpeedVideoConfiguration(
- /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60)
+ /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60,
+ /*batchSizeMax*/2)
},
toByteArray(
- 1280, 720, 60, 120,
- 123, 456, 1, 200,
- 4096, 2592, 30, 60
+ 1280, 720, 60, 120, 8,
+ 123, 456, 1, 200, 4,
+ 4096, 2592, 30, 60, 2
));
}
diff --git a/media/tests/audiotests/Android.mk b/media/tests/audiotests/Android.mk
index 794e7f22..3507434 100644
--- a/media/tests/audiotests/Android.mk
+++ b/media/tests/audiotests/Android.mk
@@ -18,6 +18,6 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunreachable-code
include $(BUILD_EXECUTABLE)