1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.tools.sdkcontroller.lib;
18
19import android.os.Message;
20import android.util.Log;
21
22import com.android.tools.sdkcontroller.service.ControllerService;
23
24import java.io.IOException;
25import java.nio.ByteBuffer;
26import java.nio.ByteOrder;
27import java.util.ArrayList;
28import java.util.List;
29import java.util.concurrent.BlockingQueue;
30import java.util.concurrent.LinkedBlockingQueue;
31import java.util.concurrent.atomic.AtomicInteger;
32
33/**
34 * Encapsulates basics of a connection with the emulator.
35 * This class must be used as a base class for all the channelss that provide
36 * particular type of emulation (such as sensors, multi-touch, etc.)
37 * <p/>
38 * Essentially, Channel is an implementation of a particular emulated functionality,
39 * that defines logical format of the data transferred between the emulator and
40 * SDK controller. For instance, "sensors" is a channel that emulates sensors,
41 * and transfers sensor value changes from the device to the emulator. "Multi-touch"
42 * is a channel that supports multi-touch emulation, and transfers multi-touch
43 * events to the emulator, while receiving frame buffer updates from the emulator.
44 * <p/>
45 * Besides connection with the emulator, each channel may contain one or more UI
46 * components associated with it. This class provides some basics for UI support,
47 * including:
48 * <p/>
49 * - Providing a way to register / unregister a UI component with the channel.
50 * <p/>
51 * - Implementing posting of messages to emulator in opposite to direct message
52 * sent. This is due to requirement that UI threads are prohibited from doing
53 * network I/O.
54 */
55public abstract class Channel {
56
57    /**
58     * Encapsulates a message posted to be sent to the emulator from a worker
59     * thread. This class is used to describe a message that is posted in UI
60     * thread, and then picked up in the worker thread.
61     */
62    private class SdkControllerMessage {
63        /** Message type. */
64        private int mMessageType;
65        /** Message data (can be null). */
66        private byte[] mMessage;
67        /** Message data size */
68        private int mMessageSize;
69
70        /**
71         * Construct message from an array.
72         *
73         * @param type Message type.
74         * @param message Message data. Message data size is defined by size of
75         *            the array.
76         */
77        public SdkControllerMessage(int type, byte[] message) {
78            mMessageType = type;
79            mMessage = message;
80            mMessageSize = (message != null) ? message.length : 0;
81        }
82
83        /**
84         * Construct message from a ByteBuffer.
85         *
86         * @param type Message type.
87         * @param message Message data. Message data size is defined by
88         *            position() property of the ByteBuffer.
89         */
90        public SdkControllerMessage(int type, ByteBuffer message) {
91            mMessageType = type;
92            if (message != null) {
93                mMessage = message.array();
94                mMessageSize = message.position();
95            } else {
96                mMessage = null;
97                mMessageSize = 0;
98            }
99        }
100
101        /**
102         * Gets message type.
103
104         *
105         * @return Message type.
106         */
107        public int getMessageType() {
108            return mMessageType;
109        }
110
111        /**
112         * Gets message buffer.
113         *
114         * @return Message buffer.
115         */
116        public byte[] getMessage() {
117            return mMessage;
118        }
119
120        /**
121         * Gets message buffer size.
122         *
123         * @return Message buffer size.
124         */
125        public int getMessageSize() {
126            return mMessageSize;
127        }
128    } // SdkControllerMessage
129
130    /*
131     * Names for currently implemented SDK controller channels.
132     */
133
134    /** Name for a channel that handles sensors emulation */
135    public static final String SENSOR_CHANNEL = "sensors";
136    /** Name for a channel that handles multi-touch emulation */
137    public static final String MULTITOUCH_CHANNEL = "multi-touch";
138
139    /*
140     * Types of messages internally used by Channel class.
141     */
142
143    /** Service-side emulator is connected. */
144    private static final int MSG_CONNECTED = -1;
145    /** Service-side emulator is disconnected. */
146    private static final int MSG_DISCONNECTED = -2;
147    /** Service-side emulator is enabled. */
148    private static final int MSG_ENABLED = -3;
149    /** Service-side emulator is disabled. */
150    private static final int MSG_DISABLED = -4;
151
152    /** Tag for logging messages. */
153    private static final String TAG = "SdkControllerChannel";
154    /** Controls debug log. */
155    private static final boolean DEBUG = false;
156
157    /** Service that has created this object. */
158    protected ControllerService mService;
159
160    /*
161     * Socket stuff.
162     */
163
164    /** Socket to use to to communicate with the emulator. */
165    private Socket mSocket = null;
166    /** Channel name ("sensors", "multi-touch", etc.) */
167    private String mChannelName;
168    /** Endianness of data transferred in this channel. */
169    private ByteOrder mEndian;
170
171    /*
172     * Message posting support.
173     */
174
175    /** Total number of messages posted in this channel */
176    private final AtomicInteger mMsgCount = new AtomicInteger(0);
177    /** Flags whether or not message thread is running. */
178    private volatile boolean mRunMsgQueue = true;
179    /** Queue of messages pending transmission. */
180    private final BlockingQueue<SdkControllerMessage>
181            mMsgQueue = new LinkedBlockingQueue<SdkControllerMessage>();
182    /** Message thread */
183    private final Thread mMsgThread;
184
185    /*
186     * UI support.
187     */
188
189    /** Lists UI handlers attached to this channel. */
190    private final List<android.os.Handler> mUiHandlers = new ArrayList<android.os.Handler>();
191
192    /*
193     * Abstract methods.
194     */
195
196    /**
197     * This method is invoked when this channel is fully connected with its
198     * counterpart in the emulator.
199     */
200    public abstract void onEmulatorConnected();
201
202    /**
203     * This method is invoked when this channel loses connection with its
204     * counterpart in the emulator.
205     */
206    public abstract void onEmulatorDisconnected();
207
208    /**
209     * A message has been received from the emulator.
210     *
211     * @param msg_type Message type.
212     * @param msg_data Message data. Message data size is defined by the length
213     *            of the array wrapped by the ByteBuffer.
214     */
215    public abstract void onEmulatorMessage(int msg_type, ByteBuffer msg_data);
216
217    /**
218     * A query has been received from the emulator.
219     *
220     * @param query_id Identifies the query. This ID must be used when replying
221     *            to the query.
222     * @param query_type Query type.
223     * @param query_data Query data. Query data size is defined by the length of
224     *            the array wrapped by the ByteBuffer.
225     */
226    public abstract void onEmulatorQuery(int query_id, int query_type, ByteBuffer query_data);
227
228    /*
229     * Channel implementation.
230     */
231
232    /**
233     * Constructs Channel instance.
234     *
235     * @param name Channel name.
236     */
237    public Channel(ControllerService service, String name) {
238        mService = service;
239        mChannelName = name;
240        // Start the worker thread for posted messages.
241        mMsgThread = new Thread(new Runnable() {
242                @Override
243            public void run() {
244                if (DEBUG) Log.d(TAG, "MsgThread.started-" + mChannelName);
245                while (mRunMsgQueue) {
246                    try {
247                        SdkControllerMessage msg = mMsgQueue.take();
248                        if (msg != null) {
249                            sendMessage(
250                                    msg.getMessageType(), msg.getMessage(), msg.getMessageSize());
251                            mMsgCount.incrementAndGet();
252                        }
253                    } catch (InterruptedException e) {
254                        Log.e(TAG, "MsgThread-" + mChannelName, e);
255                    }
256                }
257                if (DEBUG) Log.d(TAG, "MsgThread.terminate-" + mChannelName);
258            }
259        }, "MsgThread-" + name);
260        mMsgThread.start();
261        if (DEBUG) Log.d(TAG, "Channel is constructed for " + mChannelName);
262    }
263
264    /**
265     * Gets name for this channel.
266     *
267     * @return Emulator name.
268     */
269    public String getChannelName() {
270        return mChannelName;
271    }
272
273    /**
274     * Gets endianness for this channel.
275     *
276     * @return Channel endianness.
277     */
278    public ByteOrder getEndian() {
279        return mEndian;
280    }
281
282    /**
283     * Gets number of messages sent via postMessage method.
284     *
285     * @return Number of messages sent via postMessage method.
286     */
287    public int getMsgSentCount() {
288        return mMsgCount.get();
289    }
290
291    /**
292     * Checks if this channel is connected with the emulator.
293     *
294     * @return true if this channel is connected with the emulator, or false if it is
295     *         not connected.
296     */
297    public boolean isConnected() {
298        // Use local copy of the socket, ensuring it's not going to NULL while
299        // we're working with it. If it gets closed, while we're in the middle
300        // of data transfer - it's OK, since it will produce an exception, and
301        // the caller will gracefully handle it.
302        //
303        // Same technique is used everywhere in this class where mSocket member
304        // is touched.
305        Socket socket = mSocket;
306        return socket != null && socket.isConnected();
307    }
308
309    /**
310     * Establishes connection with the emulator. This method is called by Connection
311     * object when emulator successfully connects to this channel, or this channel
312     * gets registered, and there is a pending socket connection for it.
313     *
314     * @param socket Channel connection socket.
315     */
316    public void connect(Socket socket) {
317        mSocket = socket;
318        mEndian = socket.getEndian();
319        Logv("Channel " + mChannelName + " is now connected with the emulator.");
320        // Notify the emulator that connection is established.
321        sendMessage(MSG_CONNECTED, (byte[]) null);
322
323        // Let the derived class know that emulator is connected, and start the
324        // I/O loop in which we will receive data from the emulator. Note that
325        // we start the loop after onEmulatorConnected call, since we don't want
326        // to start dispatching messages before the derived class could set
327        // itself up for receiving them.
328        onEmulatorConnected();
329        new Thread(new Runnable() {
330                @Override
331            public void run() {
332                runIOLooper();
333            }
334        }, "ChannelIoLoop").start();
335        mService.notifyStatusChanged();
336    }
337
338    /**
339     * Disconnects this channel from the emulator.
340     *
341     * @return true if this channel has been disconnected in this call, or false if
342     *         channel has been already disconnected when this method has been called.
343     */
344    public boolean disconnect() {
345        // This is the only place in this class where we will null the
346        // socket object. Since this method can be called concurrently from
347        // different threads, lets do this under the lock.
348        Socket socket;
349        synchronized (this) {
350            socket = mSocket;
351            mSocket = null;
352        }
353        if (socket != null) {
354            // Notify the emulator about channel disconnection before we close
355            // the communication socket.
356            try {
357                sendMessage(socket, MSG_DISCONNECTED, null, 0);
358            } catch (IOException e) {
359                // Ignore I/O exception at this point. We don't care about
360                // it, since the socket is being closed anyways.
361            }
362            // This will eventually stop I/O looper thread.
363            socket.close();
364            mService.notifyStatusChanged();
365        }
366        return socket != null;
367    }
368
369    /**
370     * Enables the emulation. Typically, this method is called for channels that are
371     * dependent on UI to handle the emulation. For instance, multi-touch emulation is
372     * disabled until at least one UI component is attached to the channel. So, for
373     * multi-touch emulation this method is called when UI gets attached to the channel.
374     */
375    public void enable() {
376        postMessage(MSG_ENABLED, (byte[]) null);
377        mService.notifyStatusChanged();
378    }
379
380    /**
381     * Disables the emulation. Just the opposite to enable(). For multi-touch this
382     * method is called when UI detaches from the channel.
383     */
384    public void disable() {
385        postMessage(MSG_DISABLED, (byte[]) null);
386        mService.notifyStatusChanged();
387    }
388
389    /**
390     * Sends message to the emulator.
391     *
392     * @param socket Socket to send the message to.
393     * @param msg_type Message type.
394     * @param msg Message data to send.
395     * @param len Byte size of message data.
396     * @throws IOException
397     */
398    private void sendMessage(Socket socket, int msg_type, byte[] msg, int len)
399            throws IOException {
400        // In async environment we must have message header and message data in
401        // one block to prevent messages from other threads getting between the
402        // header and the data. So, we can't sent header, and then the data. We
403        // must combine them in one data block instead.
404        ByteBuffer bb = ByteBuffer.allocate(ProtocolConstants.MESSAGE_HEADER_SIZE + len);
405        bb.order(mEndian);
406
407        // Initialize message header.
408        bb.putInt(ProtocolConstants.PACKET_SIGNATURE);
409        bb.putInt(ProtocolConstants.MESSAGE_HEADER_SIZE + len);
410        bb.putInt(ProtocolConstants.PACKET_TYPE_MESSAGE);
411        bb.putInt(msg_type);
412
413        // Save message data (if there is any).
414        if (len != 0) {
415            bb.put(msg, 0, len);
416        }
417
418        socket.send(bb.array());
419    }
420
421    /**
422     * Sends message to the emulator.
423     *
424     * @param msg_type Message type.
425     * @param msg Message data to send. Message size is defined by the size of
426     *            the array.
427     * @return true on success, or false if data transmission has failed.
428     */
429    public boolean sendMessage(int msg_type, byte[] msg, int msg_len) {
430        try {
431            Socket socket = mSocket;
432            if (socket != null) {
433                sendMessage(socket, msg_type, msg, msg_len);
434                return true;
435            } else {
436                Logw("sendMessage is called on disconnected Channel " + mChannelName);
437            }
438        } catch (IOException e) {
439            Loge("Exception " + e + " in sendMessage for Channel " + mChannelName);
440            onIoFailure();
441        }
442        return false;
443    }
444
445    /**
446     * Sends message to the emulator.
447     *
448     * @param msg_type Message type.
449     * @param msg Message data to send. Message size is defined by the size of
450     *            the array.
451     * @return true on success, or false if data transmission has failed.
452     */
453    public boolean sendMessage(int msg_type, byte[] msg) {
454        try {
455            Socket socket = mSocket;
456            if (socket != null) {
457                if (msg != null) {
458                    sendMessage(socket, msg_type, msg, msg.length);
459                } else {
460                    sendMessage(socket, msg_type, null, 0);
461                }
462                return true;
463            } else {
464                Logw("sendMessage is called on disconnected Channel " + mChannelName);
465            }
466        } catch (IOException e) {
467            Loge("Exception " + e + " in sendMessage for Channel " + mChannelName);
468            onIoFailure();
469        }
470        return false;
471    }
472
473    /**
474     * Sends message to the emulator.
475     *
476     * @param msg_type Message type.
477     * @param msg Message data to send. Message size is defined by the
478     *            position() property of the ByteBuffer.
479     * @return true on success, or false if data transmission has failed.
480     */
481    public boolean sendMessage(int msg_type, ByteBuffer msg) {
482        try {
483            Socket socket = mSocket;
484            if (socket != null) {
485                if (msg != null) {
486                    sendMessage(socket, msg_type, msg.array(), msg.position());
487                } else {
488                    sendMessage(socket, msg_type, null, 0);
489                }
490                return true;
491            } else {
492                Logw("sendMessage is called on disconnected Channel " + mChannelName);
493            }
494        } catch (IOException e) {
495            Loge("Exception " + e + " in sendMessage for Channel " + mChannelName);
496            onIoFailure();
497        }
498        return false;
499    }
500
501    /**
502     * Posts message to the emulator.
503     *
504     * @param msg_type Message type.
505     * @param msg Message data to post. Message size is defined by the size of
506     *            the array.
507     */
508    public void postMessage(int msg_type, byte[] msg) {
509        try {
510            mMsgQueue.put(new SdkControllerMessage(msg_type, msg));
511        } catch (InterruptedException e) {
512            Log.e(TAG, "mMessageQueue.put", e);
513        }
514    }
515
516    /**
517     * Posts message to the emulator.
518     *
519     * @param msg_type Message type.
520     * @param msg Message data to post. Message size is defined by the
521     *            position() property of the ByteBuffer.
522     */
523    public void postMessage(int msg_type, ByteBuffer msg) {
524        try {
525            mMsgQueue.put(new SdkControllerMessage(msg_type, msg));
526        } catch (InterruptedException e) {
527            Log.e(TAG, "mMessageQueue.put", e);
528        }
529    }
530
531    /**
532     * Sends query response to the emulator.
533     *
534     * @param query_id Query identifier.
535     * @param qresp Response to the query.
536     * @param len Byte size of query response data.
537     * @return true on success, or false if data transmission has failed.
538     */
539    public boolean sendQueryResponse(int query_id, byte[] qresp, int len) {
540        // Just like with messages, we must combine header and data in a single
541        // transmitting block.
542        ByteBuffer bb = ByteBuffer.allocate(ProtocolConstants.QUERY_RESP_HEADER_SIZE + len);
543        bb.order(mEndian);
544
545        // Initialize response header.
546        bb.putInt(ProtocolConstants.PACKET_SIGNATURE);
547        bb.putInt(ProtocolConstants.QUERY_RESP_HEADER_SIZE + len);
548        bb.putInt(ProtocolConstants.PACKET_TYPE_QUERY_RESPONSE);
549        bb.putInt(query_id);
550
551        // Save response data (if there is any).
552        if (qresp != null && len != 0) {
553            bb.put(qresp, 0, len);
554        }
555
556        // Send the response.
557        try {
558            Socket socket = mSocket;
559            if (socket != null) {
560                socket.send(bb.array());
561                return true;
562            } else {
563                Logw("sendQueryResponse is called on disconnected Channel "
564                        + mChannelName);
565            }
566        } catch (IOException e) {
567            Loge("Exception " + e + " in sendQueryResponse for Channel " + mChannelName);
568            onIoFailure();
569        }
570        return false;
571    }
572
573    /**
574     * Sends query response to the emulator.
575     *
576     * @param query_id Query identifier.
577     * @param qresp Response to the query. Query response size is defined by the
578     *            size of the array.
579     * @return true on success, or false if data transmission has failed.
580     */
581    public boolean sendQueryResponse(int query_id, byte[] qresp) {
582        return (qresp != null) ? sendQueryResponse(query_id, qresp, qresp.length) :
583                sendQueryResponse(query_id, null, 0);
584    }
585
586    /**
587     * Sends query response to the emulator.
588     *
589     * @param query_id Query identifier.
590     * @param qresp Response to the query. Query response size is defined by the
591     *            position() property of the ByteBuffer.
592     * @return true on success, or false if data transmission has failed.
593     */
594    public boolean sendQueryResponse(int query_id, ByteBuffer qresp) {
595        return (qresp != null) ? sendQueryResponse(query_id, qresp.array(), qresp.position()) :
596                sendQueryResponse(query_id, null, 0);
597    }
598
599    /**
600     * Handles an I/O failure occurred in the channel.
601     */
602    private void onIoFailure() {
603        // All I/O failures cause disconnection.
604        if (disconnect()) {
605            // Success of disconnect() indicates that I/O failure is not the
606            // result of a disconnection request, but is in deed an I/O
607            // failure. Report lost connection to the derived class.
608            Loge("Connection with the emulator has been lost in Channel " + mChannelName);
609            onEmulatorDisconnected();
610        }
611    }
612
613    /**
614     * Loops on the local socket, handling connection attempts.
615     */
616    private void runIOLooper() {
617        if (DEBUG) Log.d(TAG, "In I/O looper for Channel " + mChannelName);
618        // Initialize byte buffer large enough to receive packet header.
619        ByteBuffer header = ByteBuffer.allocate(ProtocolConstants.PACKET_HEADER_SIZE);
620        header.order(mEndian);
621        try {
622            // Since disconnection (which will null the mSocket) can be
623            // requested from outside of this thread, it's simpler just to make
624            // a copy of mSocket here, and work with that copy. Otherwise we
625            // will have to go through a complex synchronization algorithm that
626            // would decrease performance on normal runs. If socket gets closed
627            // while we're in the middle of transfer, an exception will occur,
628            // which we will catch and handle properly.
629            Socket socket = mSocket;
630            while (socket != null) {
631                // Reset header position.
632                header.position(0);
633                // This will receive total packet size + packet type.
634                socket.receive(header.array());
635                // First - signature.
636                final int signature = header.getInt();
637                assert signature == ProtocolConstants.PACKET_SIGNATURE;
638                // Next - packet size (including header).
639                int remains = header.getInt() - ProtocolConstants.PACKET_HEADER_SIZE;
640                // After the size comes packet type.
641                final int packet_type = header.getInt();
642
643                // Get the remainder of the data, and dispatch the packet to
644                // an appropriate handler.
645                switch (packet_type) {
646                    case ProtocolConstants.PACKET_TYPE_MESSAGE:
647                        // Read message header (one int: message type).
648                        final int ext = ProtocolConstants.MESSAGE_HEADER_SIZE - ProtocolConstants.PACKET_HEADER_SIZE;
649                        header.position(0);
650                        socket.receive(header.array(), ext);
651                        final int msg_type = header.getInt();
652
653                        // Read message data.
654                        remains -= ext;
655                        final ByteBuffer msg_data = ByteBuffer.allocate(remains);
656                        msg_data.order(mEndian);
657                        socket.receive(msg_data.array());
658
659                        // Dispatch message for handling.
660                        onEmulatorMessage(msg_type, msg_data);
661                        break;
662
663                    case ProtocolConstants.PACKET_TYPE_QUERY:
664                        // Read query ID and query type.
665                        final int extq = ProtocolConstants.QUERY_HEADER_SIZE - ProtocolConstants.PACKET_HEADER_SIZE;
666                        header.position(0);
667                        socket.receive(header.array(), extq);
668                        final int query_id = header.getInt();
669                        final int query_type = header.getInt();
670
671                        // Read query data.
672                        remains -= extq;
673                        final ByteBuffer query_data = ByteBuffer.allocate(remains);
674                        query_data.order(mEndian);
675                        socket.receive(query_data.array());
676
677                        // Dispatch query for handling.
678                        onEmulatorQuery(query_id, query_type, query_data);
679                        break;
680
681                    default:
682                        // Unknown packet type. Just discard the remainder
683                        // of the packet
684                        Loge("Unknown packet type " + packet_type + " in Channel "
685                                + mChannelName);
686                        final byte[] discard_data = new byte[remains];
687                        socket.receive(discard_data);
688                        break;
689                }
690                socket = mSocket;
691            }
692        } catch (IOException e) {
693            Loge("Exception " + e + " in I/O looper for Channel " + mChannelName);
694            onIoFailure();
695        }
696        if (DEBUG) Log.d(TAG, "Exiting I/O looper for Channel " + mChannelName);
697    }
698
699    /**
700     * Indicates any UI handler is currently registered with the channel. If no UI
701     * is displaying the channel's state, maybe the channel can skip UI related tasks.
702     *
703     * @return True if there's at least one UI handler registered.
704     */
705    public boolean hasUiHandler() {
706        return !mUiHandlers.isEmpty();
707    }
708
709    /**
710     * Registers a new UI handler.
711     *
712     * @param uiHandler A non-null UI handler to register. Ignored if the UI
713     *            handler is null or already registered.
714     */
715    public void addUiHandler(android.os.Handler uiHandler) {
716        assert uiHandler != null;
717        if (uiHandler != null) {
718            if (!mUiHandlers.contains(uiHandler)) {
719                mUiHandlers.add(uiHandler);
720            }
721        }
722    }
723
724    /**
725     * Unregisters an UI handler.
726     *
727     * @param uiHandler A non-null UI listener to unregister. Ignored if the
728     *            listener is null or already registered.
729     */
730    public void removeUiHandler(android.os.Handler uiHandler) {
731        assert uiHandler != null;
732        mUiHandlers.remove(uiHandler);
733    }
734
735    /**
736     * Protected method to be used by handlers to send an event to all UI
737     * handlers.
738     *
739     * @param event An integer event code with no specific parameters. To be
740     *            defined by the handler itself.
741     */
742    protected void notifyUiHandlers(int event) {
743        for (android.os.Handler uiHandler : mUiHandlers) {
744            uiHandler.sendEmptyMessage(event);
745        }
746    }
747
748    /**
749     * Protected method to be used by handlers to send an event to all UI
750     * handlers.
751     *
752     * @param msg An event with parameters. To be defined by the handler itself.
753     */
754    protected void notifyUiHandlers(Message msg) {
755        for (android.os.Handler uiHandler : mUiHandlers) {
756            uiHandler.sendMessage(msg);
757        }
758    }
759
760    /**
761     * A helper routine that expands ByteBuffer to contain given number of extra
762     * bytes.
763     *
764     * @param buff Buffer to expand.
765     * @param extra Number of bytes that are required to be available in the
766     *            buffer after current position()
767     * @return ByteBuffer, containing required number of available bytes.
768     */
769    public ByteBuffer ExpandIf(ByteBuffer buff, int extra) {
770        if (extra <= buff.remaining()) {
771            return buff;
772        }
773        ByteBuffer ret = ByteBuffer.allocate(buff.position() + extra);
774        ret.order(buff.order());
775        ret.put(buff.array(), 0, buff.position());
776        return ret;
777    }
778
779    /***************************************************************************
780     * Logging wrappers
781     **************************************************************************/
782
783    private void Loge(String log) {
784        mService.addError(log);
785        Log.e(TAG, log);
786    }
787
788    private void Logw(String log) {
789        Log.w(TAG, log);
790    }
791
792    private void Logv(String log) {
793        Log.v(TAG, log);
794    }
795}
796