18f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown/*
28f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * Copyright (C) 2013 The Android Open Source Project
38f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *
48f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
58f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * you may not use this file except in compliance with the License.
68f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * You may obtain a copy of the License at
78f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *
88f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
98f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *
108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * Unless required by applicable law or agreed to in writing, software
118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * See the License for the specific language governing permissions and
148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * limitations under the License.
158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown */
168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownpackage com.android.accessorydisplay.common;
188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.os.Handler;
208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.os.Looper;
218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.os.Message;
228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.util.SparseArray;
238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport java.io.IOException;
258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport java.nio.ByteBuffer;
268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown/**
288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * A simple message transport.
298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * <p>
308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * This object's interface is thread-safe, however incoming messages
318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * are always delivered on the {@link Looper} thread on which the transport
328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * was created.
338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * </p>
348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown */
358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownpublic abstract class Transport {
368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int MAX_INPUT_BUFFERS = 8;
378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final Logger mLogger;
398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // The transport thread looper and handler.
418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final TransportHandler mHandler;
428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // Lock to guard all mutable state.
448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final Object mLock = new Object();
458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // The output buffer.  Set to null when the transport is closed.
478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private ByteBuffer mOutputBuffer;
488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // The input buffer pool.
508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private BufferPool mInputBufferPool;
518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // The reader thread.  Initialized when reading starts.
538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private ReaderThread mThread;
548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // The list of callbacks indexed by service id.
568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final SparseArray<Callback> mServices = new SparseArray<Callback>();
578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public Transport(Logger logger, int maxPacketSize) {
598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger = logger;
608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mHandler = new TransportHandler();
618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mOutputBuffer = ByteBuffer.allocate(maxPacketSize);
628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mInputBufferPool = new BufferPool(
638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                maxPacketSize, Protocol.MAX_ENVELOPE_SIZE, MAX_INPUT_BUFFERS);
648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Gets the logger for debugging.
688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public Logger getLogger() {
708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        return mLogger;
718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Gets the handler on the transport's thread.
758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public Handler getHandler() {
778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        return mHandler;
788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Closes the transport.
828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void close() {
848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        synchronized (mLock) {
858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (mOutputBuffer != null) {
868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (mThread == null) {
878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    ioClose();
888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else {
898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    // If the thread was started then it will be responsible for
908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    // closing the stream when it quits because it may currently
918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    // be in the process of reading from the stream so we can't simply
928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    // shut it down right now.
938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mThread.quit();
948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mOutputBuffer = null;
968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
1018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Sends a message.
1028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     *
1038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @param service The service to whom the message is addressed.
1048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @param what The message type.
1058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @param content The content, or null if there is none.
1068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @return True if the message was sent successfully, false if an error occurred.
1078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
1088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public boolean sendMessage(int service, int what, ByteBuffer content) {
1098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        checkServiceId(service);
1108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        checkMessageId(what);
1118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        try {
1138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            synchronized (mLock) {
1148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (mOutputBuffer == null) {
1158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mLogger.logError("Send message failed because transport was closed.");
1168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    return false;
1178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
1188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                final byte[] outputArray = mOutputBuffer.array();
1208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                final int capacity = mOutputBuffer.capacity();
1218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mOutputBuffer.clear();
1228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mOutputBuffer.putShort((short)service);
1238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mOutputBuffer.putShort((short)what);
1248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (content == null) {
1258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mOutputBuffer.putInt(0);
1268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else {
1278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int contentLimit = content.limit();
1288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    int contentPosition = content.position();
1298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    int contentRemaining = contentLimit - contentPosition;
1308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (contentRemaining > Protocol.MAX_CONTENT_SIZE) {
1318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        throw new IllegalArgumentException("Message content too large: "
1328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                                + contentRemaining + " > " + Protocol.MAX_CONTENT_SIZE);
1338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
1348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mOutputBuffer.putInt(contentRemaining);
1358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    while (contentRemaining != 0) {
1368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        final int outputAvailable = capacity - mOutputBuffer.position();
1378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        if (contentRemaining <= outputAvailable) {
1388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            mOutputBuffer.put(content);
1398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            break;
1408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        }
1418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        content.limit(contentPosition + outputAvailable);
1428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mOutputBuffer.put(content);
1438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        content.limit(contentLimit);
1448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        ioWrite(outputArray, 0, capacity);
1458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        contentPosition += outputAvailable;
1468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        contentRemaining -= outputAvailable;
1478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mOutputBuffer.clear();
1488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
1498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
1508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                ioWrite(outputArray, 0, mOutputBuffer.position());
1518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                return true;
1528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        } catch (IOException ex) {
1548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("Send message failed: " + ex);
1558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return false;
1568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
1608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Starts reading messages on a separate thread.
1618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
1628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void startReading() {
1638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        synchronized (mLock) {
1648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (mOutputBuffer == null) {
1658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                throw new IllegalStateException("Transport has been closed");
1668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mThread = new ReaderThread();
1698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mThread.start();
1708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
1748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Registers a service and provides a callback to receive messages.
1758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     *
1768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @param service The service id.
1778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @param callback The callback to use.
1788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
1798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void registerService(int service, Callback callback) {
1808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        checkServiceId(service);
1818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (callback == null) {
1828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            throw new IllegalArgumentException("callback must not be null");
1838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        synchronized (mLock) {
1868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mServices.put(service, callback);
1878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
1918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Unregisters a service.
1928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     *
1938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * @param service The service to unregister.
1948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
1958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void unregisterService(int service) {
1968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        checkServiceId(service);
1978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        synchronized (mLock) {
1998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mServices.remove(service);
2008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void dispatchMessageReceived(int service, int what, ByteBuffer content) {
2048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        final Callback callback;
2058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        synchronized (mLock) {
2068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            callback = mServices.get(service);
2078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (callback != null) {
2098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            callback.onMessageReceived(service, what, content);
2108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        } else {
2118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Discarding message " + what
2128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    + " for unregistered service " + service);
2138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static void checkServiceId(int service) {
2178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (service < 0 || service > 0xffff) {
2188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            throw new IllegalArgumentException("service id out of range: " + service);
2198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static void checkMessageId(int what) {
2238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (what < 0 || what > 0xffff) {
2248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            throw new IllegalArgumentException("message id out of range: " + what);
2258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // The IO methods must be safe to call on any thread.
2298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    // They may be called concurrently.
2308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    protected abstract void ioClose();
2318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    protected abstract int ioRead(byte[] buffer, int offset, int count)
2328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            throws IOException;
2338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    protected abstract void ioWrite(byte[] buffer, int offset, int count)
2348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            throws IOException;
2358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    /**
2378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     * Callback for services that handle received messages.
2388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown     */
2398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public interface Callback {
2408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        /**
2418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown         * Indicates that a message was received.
2428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown         *
2438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown         * @param service The service to whom the message is addressed.
2448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown         * @param what The message type.
2458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown         * @param content The content, or null if there is none.
2468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown         */
2478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void onMessageReceived(int service, int what, ByteBuffer content);
2488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    final class TransportHandler extends Handler {
2518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        @Override
2528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void handleMessage(Message msg) {
2538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            final ByteBuffer buffer = (ByteBuffer)msg.obj;
2548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            try {
2558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                final int limit = buffer.limit();
2568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                while (buffer.position() < limit) {
2578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int service = buffer.getShort() & 0xffff;
2588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int what = buffer.getShort() & 0xffff;
2598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int contentSize = buffer.getInt();
2608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (contentSize == 0) {
2618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        dispatchMessageReceived(service, what, null);
2628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    } else {
2638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        final int end = buffer.position() + contentSize;
2648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        buffer.limit(end);
2658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        dispatchMessageReceived(service, what, buffer);
2668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        buffer.limit(limit);
2678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        buffer.position(end);
2688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
2698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
2708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            } finally {
2718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mInputBufferPool.release(buffer);
2728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    final class ReaderThread extends Thread {
2778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        // Set to true when quitting.
2788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private volatile boolean mQuitting;
2798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public ReaderThread() {
2818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            super("Accessory Display Transport");
2828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        @Override
2858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void run() {
2868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            loop();
2878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            ioClose();
2888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private void loop() {
2918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            ByteBuffer buffer = null;
2928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            int length = Protocol.HEADER_SIZE;
2938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            int contentSize = -1;
2948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            outer: while (!mQuitting) {
2958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // Get a buffer.
2968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (buffer == null) {
2978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer = mInputBufferPool.acquire(length);
2988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else {
2998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer = mInputBufferPool.grow(buffer, length);
3008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // Read more data until needed number of bytes obtained.
3038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                int position = buffer.position();
3048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                int count;
3058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                try {
3068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    count = ioRead(buffer.array(), position, buffer.capacity() - position);
3078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (count < 0) {
3088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        break; // end of stream
3098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } catch (IOException ex) {
3118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mLogger.logError("Read failed: " + ex);
3128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    break; // error
3138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                position += count;
3158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                buffer.position(position);
3168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (contentSize < 0 && position >= Protocol.HEADER_SIZE) {
3178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    contentSize = buffer.getInt(4);
3188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (contentSize < 0 || contentSize > Protocol.MAX_CONTENT_SIZE) {
3198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mLogger.logError("Encountered invalid content size: " + contentSize);
3208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        break; // malformed stream
3218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    length += contentSize;
3238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (position < length) {
3258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    continue; // need more data
3268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // There is at least one complete message in the buffer.
3298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // Find the end of a contiguous chunk of complete messages.
3308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                int next = length;
3318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                int remaining;
3328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                for (;;) {
3338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    length = Protocol.HEADER_SIZE;
3348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    remaining = position - next;
3358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (remaining < length) {
3368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        contentSize = -1;
3378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        break; // incomplete header, need more data
3388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    contentSize = buffer.getInt(next + 4);
3408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (contentSize < 0 || contentSize > Protocol.MAX_CONTENT_SIZE) {
3418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mLogger.logError("Encountered invalid content size: " + contentSize);
3428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        break outer; // malformed stream
3438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    length += contentSize;
3458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (remaining < length) {
3468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        break; // incomplete content, need more data
3478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    next += length;
3498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // Post the buffer then don't modify it anymore.
3528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // Now this is kind of sneaky.  We know that no other threads will
3538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // be acquiring buffers from the buffer pool so we can keep on
3548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // referring to this buffer as long as we don't modify its contents.
3558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // This allows us to operate in a single-buffered mode if desired.
3568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                buffer.limit(next);
3578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                buffer.rewind();
3588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mHandler.obtainMessage(0, buffer).sendToTarget();
3598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // If there is an incomplete message at the end, then we will need
3618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // to copy it to a fresh buffer before continuing.  In the single-buffered
3628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // case, we may acquire the same buffer as before which is fine.
3638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (remaining == 0) {
3648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer = null;
3658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else {
3668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final ByteBuffer oldBuffer = buffer;
3678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer = mInputBufferPool.acquire(length);
3688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    System.arraycopy(oldBuffer.array(), next, buffer.array(), 0, remaining);
3698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer.position(remaining);
3708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
3728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (buffer != null) {
3748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mInputBufferPool.release(buffer);
3758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
3768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void quit() {
3798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mQuitting = true;
3808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
3828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown}
383