Merge "Fix classpath for AndroidLocale" into lmp-dev
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 7a1c988b..0de3f26 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -229,7 +229,7 @@
      * in the past or future will cease to work.
      *
      * The returned URL may have a {@link URLStreamHandler} explicitly set, which may not be the
-     * handler generated by the factory set with {@link java.net.URL.setURLStreamHandlerFactory}. To
+     * handler generated by the factory set with {@link java.net.URL#setURLStreamHandlerFactory}. To
      * affect the {@code URLStreamHandler}s of URLs returned by this method, call
      * {@link #setNetworkBoundURLFactory}.
      *
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 275d9b2..4a1c0ae 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -34,6 +34,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ReadOnlyBufferException;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -531,9 +532,13 @@
     public native final Surface createInputSurface();
 
     /**
-     * After successfully configuring the component, call start. On return
-     * you can query the component for its input/output buffers.
-     * @throws IllegalStateException if not in the Configured state.
+     * After successfully configuring the component, call {@code start}.
+     * <p>
+     * Call {@code start} also if the codec is configured in asynchronous mode,
+     * and it has just been flushed, to resume requesting input buffers.
+     * @throws IllegalStateException if not in the Configured state
+     *         or just after {@link #flush} for a codec that is configured
+     *         in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error. Note that some codec errors
      * for start may be attributed to future method calls.
      */
@@ -569,6 +574,15 @@
      * Flush both input and output ports of the component, all indices
      * previously returned in calls to {@link #dequeueInputBuffer} and
      * {@link #dequeueOutputBuffer} become invalid.
+     * <p>
+     * If codec is configured in asynchronous mode, call {@link #start}
+     * after {@code flush} has returned to resume codec operations. The
+     * codec will not request input buffers until this has happened.
+     * <p>
+     * If codec is configured in synchronous mode, codec will resume
+     * automatically if an input surface was created.  Otherwise, it
+     * will resume when {@link #dequeueInputBuffer} is called.
+     *
      * @throws IllegalStateException if not in the Executing state.
      * @throws MediaCodec.CodecException upon codec error.
      */
@@ -576,10 +590,8 @@
         synchronized(mBufferLock) {
             invalidateByteBuffers(mCachedInputBuffers);
             invalidateByteBuffers(mCachedOutputBuffers);
-            invalidateByteBuffers(mDequeuedInputBuffers);
-            invalidateByteBuffers(mDequeuedOutputBuffers);
-            freeImages(mDequeuedInputImages);
-            freeImages(mDequeuedOutputImages);
+            mDequeuedInputBuffers.clear();
+            mDequeuedOutputBuffers.clear();
         }
         native_flush();
     }
@@ -719,7 +731,7 @@
         throws CryptoException {
         synchronized(mBufferLock) {
             invalidateByteBuffer(mCachedInputBuffers, index);
-            updateDequeuedByteBuffer(mDequeuedInputBuffers, index, null);
+            mDequeuedInputBuffers.remove(index);
         }
         native_queueInputBuffer(
                 index, offset, size, presentationTimeUs, flags);
@@ -839,7 +851,7 @@
             int flags) throws CryptoException {
         synchronized(mBufferLock) {
             invalidateByteBuffer(mCachedInputBuffers, index);
-            updateDequeuedByteBuffer(mDequeuedInputBuffers, index, null);
+            mDequeuedInputBuffers.remove(index);
         }
         native_queueSecureInputBuffer(
                 index, offset, info, presentationTimeUs, flags);
@@ -859,7 +871,8 @@
      * for the availability of an input buffer if timeoutUs &lt; 0 or wait up
      * to "timeoutUs" microseconds if timeoutUs &gt; 0.
      * @param timeoutUs The timeout in microseconds, a negative timeout indicates "infinite".
-     * @throws IllegalStateException if not in the Executing state.
+     * @throws IllegalStateException if not in the Executing state,
+     *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
      */
     public final int dequeueInputBuffer(long timeoutUs) {
@@ -907,7 +920,8 @@
      * decoded or one of the INFO_* constants below.
      * @param info Will be filled with buffer meta data.
      * @param timeoutUs The timeout in microseconds, a negative timeout indicates "infinite".
-     * @throws IllegalStateException if not in the Executing state.
+     * @throws IllegalStateException if not in the Executing state,
+     *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
      */
     public final int dequeueOutputBuffer(
@@ -946,7 +960,7 @@
     public final void releaseOutputBuffer(int index, boolean render) {
         synchronized(mBufferLock) {
             invalidateByteBuffer(mCachedOutputBuffers, index);
-            updateDequeuedByteBuffer(mDequeuedOutputBuffers, index, null);
+            mDequeuedOutputBuffers.remove(index);
         }
         releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
     }
@@ -1003,7 +1017,7 @@
     public final void releaseOutputBuffer(int index, long renderTimestampNs) {
         synchronized(mBufferLock) {
             invalidateByteBuffer(mCachedOutputBuffers, index);
-            updateDequeuedByteBuffer(mDequeuedOutputBuffers, index, null);
+            mDequeuedOutputBuffers.remove(index);
         }
         releaseOutputBuffer(
                 index, true /* render */, true /* updatePTS */, renderTimestampNs);
@@ -1068,17 +1082,82 @@
 
     private native final Map<String, Object> getOutputFormatNative(int index);
 
+    // used to track dequeued buffers
+    private static class BufferMap {
+        // various returned representations of the codec buffer
+        private static class CodecBuffer {
+            private Image mImage;
+            private ByteBuffer mByteBuffer;
+
+            public void free() {
+                if (mByteBuffer != null) {
+                    // all of our ByteBuffers are direct
+                    java.nio.NioUtils.freeDirectBuffer(mByteBuffer);
+                    mByteBuffer = null;
+                }
+                if (mImage != null) {
+                    mImage.close();
+                    mImage = null;
+                }
+            }
+
+            public void setImage(Image image) {
+                free();
+                mImage = image;
+            }
+
+            public void setByteBuffer(ByteBuffer buffer) {
+                free();
+                mByteBuffer = buffer;
+            }
+        }
+
+        private final Map<Integer, CodecBuffer> mMap =
+            new HashMap<Integer, CodecBuffer>();
+
+        public void remove(int index) {
+            CodecBuffer buffer = mMap.get(index);
+            if (buffer != null) {
+                buffer.free();
+                mMap.remove(index);
+            }
+        }
+
+        public void put(int index, ByteBuffer newBuffer) {
+            CodecBuffer buffer = mMap.get(index);
+            if (buffer == null) { // likely
+                buffer = new CodecBuffer();
+                mMap.put(index, buffer);
+            }
+            buffer.setByteBuffer(newBuffer);
+        }
+
+        public void put(int index, Image newImage) {
+            CodecBuffer buffer = mMap.get(index);
+            if (buffer == null) { // likely
+                buffer = new CodecBuffer();
+                mMap.put(index, buffer);
+            }
+            buffer.setImage(newImage);
+        }
+
+        public void clear() {
+            for (CodecBuffer buffer: mMap.values()) {
+                buffer.free();
+            }
+            mMap.clear();
+        }
+    }
+
     private ByteBuffer[] mCachedInputBuffers;
     private ByteBuffer[] mCachedOutputBuffers;
-    private ByteBuffer[] mDequeuedInputBuffers;
-    private ByteBuffer[] mDequeuedOutputBuffers;
-    private Image[] mDequeuedInputImages;
-    private Image[] mDequeuedOutputImages;
+    private final BufferMap mDequeuedInputBuffers = new BufferMap();
+    private final BufferMap mDequeuedOutputBuffers = new BufferMap();
     final private Object mBufferLock;
 
     private final void invalidateByteBuffer(
             ByteBuffer[] buffers, int index) {
-        if (index >= 0 && index < buffers.length) {
+        if (buffers != null && index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
                 buffer.setAccessible(false);
@@ -1088,7 +1167,7 @@
 
     private final void validateInputByteBuffer(
             ByteBuffer[] buffers, int index) {
-        if (index >= 0 && index < buffers.length) {
+        if (buffers != null && index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
                 buffer.setAccessible(true);
@@ -1099,7 +1178,7 @@
 
     private final void validateOutputByteBuffer(
             ByteBuffer[] buffers, int index, BufferInfo info) {
-        if (index >= 0 && index < buffers.length) {
+        if (buffers != null && index >= 0 && index < buffers.length) {
             ByteBuffer buffer = buffers[index];
             if (buffer != null) {
                 buffer.setAccessible(true);
@@ -1133,46 +1212,29 @@
         }
     }
 
-    private final void freeImage(Image image) {
-        if (image != null) {
-            image.close();
-        }
-    }
-
-    private final void freeImages(Image[] images) {
-        if (images != null) {
-            for (Image image: images) {
-                freeImage(image);
-            }
-        }
-    }
-
     private final void freeAllTrackedBuffers() {
-        freeByteBuffers(mCachedInputBuffers);
-        freeByteBuffers(mCachedOutputBuffers);
-        freeImages(mDequeuedInputImages);
-        freeImages(mDequeuedOutputImages);
-        freeByteBuffers(mDequeuedInputBuffers);
-        freeByteBuffers(mDequeuedOutputBuffers);
-        mCachedInputBuffers = null;
-        mCachedOutputBuffers = null;
-        mDequeuedInputImages = null;
-        mDequeuedOutputImages = null;
-        mDequeuedInputBuffers = null;
-        mDequeuedOutputBuffers = null;
+        synchronized (mBufferLock) {
+            freeByteBuffers(mCachedInputBuffers);
+            freeByteBuffers(mCachedOutputBuffers);
+            mCachedInputBuffers = null;
+            mCachedOutputBuffers = null;
+            mDequeuedInputBuffers.clear();
+            mDequeuedOutputBuffers.clear();
+        }
     }
 
     private final void cacheBuffers(boolean input) {
-        ByteBuffer[] buffers = getBuffers(input);
-        invalidateByteBuffers(buffers);
+        ByteBuffer[] buffers = null;
+        try {
+            buffers = getBuffers(input);
+            invalidateByteBuffers(buffers);
+        } catch (IllegalStateException e) {
+            // we don't get buffers in async mode
+        }
         if (input) {
             mCachedInputBuffers = buffers;
-            mDequeuedInputImages = new Image[buffers.length];
-            mDequeuedInputBuffers = new ByteBuffer[buffers.length];
         } else {
             mCachedOutputBuffers = buffers;
-            mDequeuedOutputImages = new Image[buffers.length];
-            mDequeuedOutputBuffers = new ByteBuffer[buffers.length];
         }
     }
 
@@ -1188,7 +1250,8 @@
      * <b>Note:</b>As of API 21, dequeued input buffers are
      * automatically {@link java.nio.Buffer#clear cleared}.
      *
-     * @throws IllegalStateException if not in the Executing state.
+     * @throws IllegalStateException if not in the Executing state,
+     *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
      */
     public ByteBuffer[] getInputBuffers() {
@@ -1227,26 +1290,6 @@
         return mCachedOutputBuffers;
     }
 
-    private boolean updateDequeuedByteBuffer(
-            ByteBuffer[] buffers, int index, ByteBuffer newBuffer) {
-        if (index < 0 || index >= buffers.length) {
-            return false;
-        }
-        freeByteBuffer(buffers[index]);
-        buffers[index] = newBuffer;
-        return newBuffer != null;
-    }
-
-    private boolean updateDequeuedImage(
-            Image[] images, int index, Image newImage) {
-        if (index < 0 || index >= images.length) {
-            return false;
-        }
-        freeImage(images[index]);
-        images[index] = newImage;
-        return newImage != null;
-    }
-
     /**
      * Returns a {@link java.nio.Buffer#clear cleared}, writable ByteBuffer
      * object for a dequeued input buffer index to contain the input data.
@@ -1268,13 +1311,10 @@
     public ByteBuffer getInputBuffer(int index) {
         ByteBuffer newBuffer = getBuffer(true /* input */, index);
         synchronized(mBufferLock) {
-            if (updateDequeuedByteBuffer(mDequeuedInputBuffers, index, newBuffer)) {
-                updateDequeuedImage(mDequeuedInputImages, index, null);
-                invalidateByteBuffer(mCachedInputBuffers, index);
-                return newBuffer;
-            }
+            invalidateByteBuffer(mCachedInputBuffers, index);
+            mDequeuedInputBuffers.put(index, newBuffer);
         }
-        return null;
+        return newBuffer;
     }
 
     /**
@@ -1298,12 +1338,11 @@
      */
     public Image getInputImage(int index) {
         Image newImage = getImage(true /* input */, index);
-        if (updateDequeuedImage(mDequeuedInputImages, index, newImage)) {
-            updateDequeuedByteBuffer(mDequeuedInputBuffers, index, null);
+        synchronized(mBufferLock) {
             invalidateByteBuffer(mCachedInputBuffers, index);
-            return newImage;
+            mDequeuedInputBuffers.put(index, newImage);
         }
-        return null;
+        return newImage;
     }
 
     /**
@@ -1328,13 +1367,10 @@
     public ByteBuffer getOutputBuffer(int index) {
         ByteBuffer newBuffer = getBuffer(false /* input */, index);
         synchronized(mBufferLock) {
-            if (updateDequeuedByteBuffer(mDequeuedOutputBuffers, index, newBuffer)) {
-                updateDequeuedImage(mDequeuedOutputImages, index, null);
-                invalidateByteBuffer(mCachedOutputBuffers, index);
-                return newBuffer;
-            }
+            invalidateByteBuffer(mCachedOutputBuffers, index);
+            mDequeuedOutputBuffers.put(index, newBuffer);
         }
-        return null;
+        return newBuffer;
     }
 
     /**
@@ -1358,13 +1394,10 @@
     public Image getOutputImage(int index) {
         Image newImage = getImage(false /* input */, index);
         synchronized(mBufferLock) {
-            if (updateDequeuedImage(mDequeuedOutputImages, index, newImage)) {
-                updateDequeuedByteBuffer(mDequeuedOutputBuffers, index, null);
-                invalidateByteBuffer(mCachedOutputBuffers, index);
-                return newImage;
-            }
+            invalidateByteBuffer(mCachedOutputBuffers, index);
+            mDequeuedOutputBuffers.put(index, newImage);
         }
-        return null;
+        return newImage;
     }
 
     /**
@@ -1445,7 +1478,12 @@
      * a valid callback should be provided before {@link #configure} is called.
      *
      * When asynchronous callback is enabled, the client should not call
-     * {@link #dequeueInputBuffer(long)} or {@link #dequeueOutputBuffer(BufferInfo, long)}
+     * {@link #getInputBuffers}, {@link #getOutputBuffers},
+     * {@link #dequeueInputBuffer(long)} or {@link #dequeueOutputBuffer(BufferInfo, long)}.
+     * <p>
+     * Also, {@link #flush} behaves differently in asynchronous mode.  After calling
+     * {@code flush}, you must call {@link #start} to "resume" receiving input buffers,
+     * even if an input surface was created.
      *
      * @param cb The callback that will run.
      */
diff --git a/media/java/android/media/tv/TvStreamConfig.java b/media/java/android/media/tv/TvStreamConfig.java
index 243f864..a7e7e44 100644
--- a/media/java/android/media/tv/TvStreamConfig.java
+++ b/media/java/android/media/tv/TvStreamConfig.java
@@ -88,6 +88,16 @@
         return mGeneration;
     }
 
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder(128);
+        b.append("TvStreamConfig {");
+        b.append("mStreamId=").append(mStreamId).append(";");
+        b.append("mType=").append(mType).append(";");
+        b.append("mGeneration=").append(mGeneration).append("}");
+        return b.toString();
+    }
+
     // Parcelable
     @Override
     public int describeContents() {
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index c6213f9..e7dd82d 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -23,6 +23,8 @@
 import android.os.Message;
 import android.view.Surface;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import java.util.LinkedList;
 import java.util.Queue;
@@ -62,58 +64,70 @@
             int generation);
     private static native void nativeClose(long ptr);
 
+    private Object mLock = new Object();
     private long mPtr = 0;
     private final Callback mCallback;
     private final Handler mHandler;
-    private int mStreamConfigGeneration = 0;
-    private TvStreamConfig[] mStreamConfigs;
+    private SparseIntArray mStreamConfigGenerations = new SparseIntArray();
+    private SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();;
 
     public TvInputHal(Callback callback) {
         mCallback = callback;
         mHandler = new Handler(this);
     }
 
-    public synchronized void init() {
-        mPtr = nativeOpen();
-    }
-
-    public synchronized int addStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
-        if (mPtr == 0) {
-            return ERROR_NO_INIT;
-        }
-        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
-            return ERROR_STALE_CONFIG;
-        }
-        if (nativeAddStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
-            return SUCCESS;
-        } else {
-            return ERROR_UNKNOWN;
+    public void init() {
+        synchronized (mLock) {
+            mPtr = nativeOpen();
         }
     }
 
-    public synchronized int removeStream(int deviceId, TvStreamConfig streamConfig) {
-        if (mPtr == 0) {
-            return ERROR_NO_INIT;
-        }
-        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
-            return ERROR_STALE_CONFIG;
-        }
-        if (nativeRemoveStream(mPtr, deviceId, streamConfig.getStreamId()) == 0) {
-            return SUCCESS;
-        } else {
-            return ERROR_UNKNOWN;
+    public int addStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
+        synchronized (mLock) {
+            if (mPtr == 0) {
+                return ERROR_NO_INIT;
+            }
+            int generation = mStreamConfigGenerations.get(deviceId, 0);
+            if (generation != streamConfig.getGeneration()) {
+                return ERROR_STALE_CONFIG;
+            }
+            if (nativeAddStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
+                return SUCCESS;
+            } else {
+                return ERROR_UNKNOWN;
+            }
         }
     }
 
-    public synchronized void close() {
-        if (mPtr != 0l) {
-            nativeClose(mPtr);
+    public int removeStream(int deviceId, TvStreamConfig streamConfig) {
+        synchronized (mLock) {
+            if (mPtr == 0) {
+                return ERROR_NO_INIT;
+            }
+            int generation = mStreamConfigGenerations.get(deviceId, 0);
+            if (generation != streamConfig.getGeneration()) {
+                return ERROR_STALE_CONFIG;
+            }
+            if (nativeRemoveStream(mPtr, deviceId, streamConfig.getStreamId()) == 0) {
+                return SUCCESS;
+            } else {
+                return ERROR_UNKNOWN;
+            }
         }
     }
 
-    private synchronized void retrieveStreamConfigs(int deviceId) {
-        ++mStreamConfigGeneration;
-        mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration);
+    public void close() {
+        synchronized (mLock) {
+            if (mPtr != 0l) {
+                nativeClose(mPtr);
+            }
+        }
+    }
+
+    private void retrieveStreamConfigsLocked(int deviceId) {
+        int generation = mStreamConfigGenerations.get(deviceId, 0) + 1;
+        mStreamConfigs.put(deviceId, nativeGetStreamConfigs(mPtr, deviceId, generation));
+        mStreamConfigGenerations.put(deviceId, generation);
     }
 
     // Called from native
@@ -145,12 +159,16 @@
     public boolean handleMessage(Message msg) {
         switch (msg.what) {
             case EVENT_DEVICE_AVAILABLE: {
+                TvStreamConfig[] configs;
                 TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
-                retrieveStreamConfigs(info.getDeviceId());
-                if (DEBUG) {
-                    Slog.d(TAG, "EVENT_DEVICE_AVAILABLE: info = " + info);
+                synchronized (mLock) {
+                    retrieveStreamConfigsLocked(info.getDeviceId());
+                    if (DEBUG) {
+                        Slog.d(TAG, "EVENT_DEVICE_AVAILABLE: info = " + info);
+                    }
+                    configs = mStreamConfigs.get(info.getDeviceId());
                 }
-                mCallback.onDeviceAvailable(info, mStreamConfigs);
+                mCallback.onDeviceAvailable(info, configs);
                 break;
             }
 
@@ -164,12 +182,16 @@
             }
 
             case EVENT_STREAM_CONFIGURATION_CHANGED: {
+                TvStreamConfig[] configs;
                 int deviceId = msg.arg1;
-                if (DEBUG) {
-                    Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId);
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId);
+                    }
+                    retrieveStreamConfigsLocked(deviceId);
+                    configs = mStreamConfigs.get(deviceId);
                 }
-                retrieveStreamConfigs(deviceId);
-                mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs);
+                mCallback.onStreamConfigurationChanged(deviceId, configs);
                 break;
             }
 
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 41976ff..5eb627b 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -351,6 +351,8 @@
             ALOGE("Cannot find a config with given stream ID: %d", streamId);
             return BAD_VALUE;
         }
+        connection.mStreamType = configs[configIndex].type;
+
         tv_stream_t stream;
         stream.stream_id = configs[configIndex].stream_id;
         if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {