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.sink;
188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport com.android.accessorydisplay.common.Logger;
208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.app.Activity;
228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.app.PendingIntent;
238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.content.BroadcastReceiver;
248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.content.Context;
258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.content.Intent;
268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.content.IntentFilter;
278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.usb.UsbConstants;
288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.usb.UsbDevice;
298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.usb.UsbDeviceConnection;
308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.usb.UsbEndpoint;
318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.usb.UsbInterface;
328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.usb.UsbManager;
338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaCodec;
348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaCodec.BufferInfo;
358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaFormat;
368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.os.Bundle;
378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.text.method.ScrollingMovementMethod;
388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.util.Log;
398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.MotionEvent;
408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.Surface;
418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.SurfaceHolder;
428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.SurfaceView;
438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.View;
448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.widget.TextView;
458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport java.nio.ByteBuffer;
478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport java.util.LinkedList;
488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport java.util.Map;
498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownpublic class SinkActivity extends Activity {
518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String TAG = "SinkActivity";
528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String ACTION_USB_DEVICE_PERMISSION =
548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            "com.android.accessorydisplay.sink.ACTION_USB_DEVICE_PERMISSION";
558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String MANUFACTURER = "Android";
578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String MODEL = "Accessory Display";
588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String DESCRIPTION = "Accessory Display Sink Test Application";
598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String VERSION = "1.0";
608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String URI = "http://www.android.com/";
618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String SERIAL = "0000000012345678";
628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int MULTITOUCH_DEVICE_ID = 0;
648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int MULTITOUCH_REPORT_ID = 1;
658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int MULTITOUCH_MAX_CONTACTS = 1;
668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbManager mUsbManager;
688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private DeviceReceiver mReceiver;
698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private TextView mLogTextView;
708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private TextView mFpsTextView;
718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private SurfaceView mSurfaceView;
728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private Logger mLogger;
738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private boolean mConnected;
758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private int mProtocolVersion;
768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbDevice mDevice;
778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbInterface mAccessoryInterface;
788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbDeviceConnection mAccessoryConnection;
798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbEndpoint mControlEndpoint;
808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbAccessoryBulkTransport mTransport;
818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private boolean mAttached;
838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private DisplaySinkService mDisplaySinkService;
848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final ByteBuffer mHidBuffer = ByteBuffer.allocate(4096);
868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbHid.Multitouch mMultitouch;
878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private boolean mMultitouchEnabled;
888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private UsbHid.Multitouch.Contact[] mMultitouchContacts;
898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    protected void onCreate(Bundle savedInstanceState) {
928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super.onCreate(savedInstanceState);
938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        setContentView(R.layout.sink_activity);
978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogTextView = (TextView) findViewById(R.id.logTextView);
998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
1008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger = new TextLogger();
1018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mFpsTextView = (TextView) findViewById(R.id.fpsTextView);
1038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
1058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
1068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            @Override
1078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            public boolean onTouch(View v, MotionEvent event) {
1088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                sendHidTouch(event);
1098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                return true;
1108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        });
1128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("Waiting for accessory display source to be attached to USB...");
1148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        IntentFilter filter = new IntentFilter();
1168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
1178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
1188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        filter.addAction(ACTION_USB_DEVICE_PERMISSION);
1198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mReceiver = new DeviceReceiver();
1208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        registerReceiver(mReceiver, filter);
1218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        Intent intent = getIntent();
1238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
1248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
1258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (device != null) {
1268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                onDeviceAttached(device);
1278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        } else {
1298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            Map<String, UsbDevice> devices = mUsbManager.getDeviceList();
1308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (devices != null) {
1318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                for (UsbDevice device : devices.values()) {
1328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    onDeviceAttached(device);
1338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
1348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
1398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    protected void onDestroy() {
1408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super.onDestroy();
1418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        unregisterReceiver(mReceiver);
1438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void onDeviceAttached(UsbDevice device) {
1468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("USB device attached: " + device);
1478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (!mConnected) {
1488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            connect(device);
1498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void onDeviceDetached(UsbDevice device) {
1538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("USB device detached: " + device);
1548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mConnected && device.equals(mDevice)) {
1558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            disconnect();
1568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void connect(UsbDevice device) {
1608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mConnected) {
1618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            disconnect();
1628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        // Check whether we have permission to access the device.
1658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (!mUsbManager.hasPermission(device)) {
1668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Prompting the user for access to the device.");
1678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            Intent intent = new Intent(ACTION_USB_DEVICE_PERMISSION);
1688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            intent.setPackage(getPackageName());
1698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            PendingIntent pendingIntent = PendingIntent.getBroadcast(
1708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
1718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mUsbManager.requestPermission(device, pendingIntent);
1728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
1738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        // Claim the device.
1768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        UsbDeviceConnection conn = mUsbManager.openDevice(device);
1778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (conn == null) {
1788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("Could not obtain device connection.");
1798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
1808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        UsbInterface iface = device.getInterface(0);
1828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        UsbEndpoint controlEndpoint = iface.getEndpoint(0);
1838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (!conn.claimInterface(iface, true)) {
1848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("Could not claim interface.");
1858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
1868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        try {
1888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            // If already in accessory mode, then connect to the device.
1898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (isAccessory(device)) {
1908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.log("Connecting to accessory...");
1918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                int protocolVersion = getProtocol(conn);
1938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (protocolVersion < 1) {
1948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mLogger.logError("Device does not support accessory protocol.");
1958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    return;
1968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
1978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.log("Protocol version: " + protocolVersion);
1988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                // Setup bulk endpoints.
2008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbEndpoint bulkIn = null;
2018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbEndpoint bulkOut = null;
2028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                for (int i = 0; i < iface.getEndpointCount(); i++) {
2038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    UsbEndpoint ep = iface.getEndpoint(i);
2048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
2058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        if (bulkIn == null) {
2068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            mLogger.log(String.format("Bulk IN endpoint: %d", i));
2078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            bulkIn = ep;
2088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        }
2098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    } else {
2108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        if (bulkOut == null) {
2118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            mLogger.log(String.format("Bulk OUT endpoint: %d", i));
2128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            bulkOut = ep;
2138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        }
2148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
2158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
2168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (bulkIn == null || bulkOut == null) {
2178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mLogger.logError("Unable to find bulk endpoints");
2188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    return;
2198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
2208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.log("Connected");
2228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mConnected = true;
2238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mDevice = device;
2248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mProtocolVersion = protocolVersion;
2258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mAccessoryInterface = iface;
2268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mAccessoryConnection = conn;
2278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mControlEndpoint = controlEndpoint;
2288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mTransport = new UsbAccessoryBulkTransport(mLogger, conn, bulkIn, bulkOut);
2298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (mProtocolVersion >= 2) {
2308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    registerHid();
2318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
2328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                startServices();
2338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mTransport.startReading();
2348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                return;
2358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            // Do accessory negotiation.
2388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Attempting to switch device to accessory mode...");
2398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            // Send get protocol.
2418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            int protocolVersion = getProtocol(conn);
2428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (protocolVersion < 1) {
2438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.logError("Device does not support accessory protocol.");
2448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                return;
2458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Protocol version: " + protocolVersion);
2478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            // Send identifying strings.
2498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MANUFACTURER, MANUFACTURER);
2508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MODEL, MODEL);
2518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_DESCRIPTION, DESCRIPTION);
2528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_VERSION, VERSION);
2538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_URI, URI);
2548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_SERIAL, SERIAL);
2558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            // Send start.
2578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            // The device should re-enumerate as an accessory.
2588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Sending accessory start request.");
2598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            int len = conn.controlTransfer(UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
2608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    UsbAccessoryConstants.ACCESSORY_START, 0, 0, null, 0, 10000);
2618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (len != 0) {
2628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.logError("Device refused to switch to accessory mode.");
2638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            } else {
2648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.log("Waiting for device to re-enumerate...");
2658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        } finally {
2678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (!mConnected) {
2688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                conn.releaseInterface(iface);
2698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void disconnect() {
2748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("Disconnecting from device: " + mDevice);
2758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        stopServices();
2768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        unregisterHid();
2778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("Disconnected.");
2798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mConnected = false;
2808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mDevice = null;
2818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mAccessoryConnection = null;
2828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mAccessoryInterface = null;
2838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mControlEndpoint = null;
2848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mTransport != null) {
2858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mTransport.close();
2868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mTransport = null;
2878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void registerHid() {
2918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("Registering HID multitouch device.");
2928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouch = new UsbHid.Multitouch(MULTITOUCH_REPORT_ID, MULTITOUCH_MAX_CONTACTS,
2948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mSurfaceView.getWidth(), mSurfaceView.getHeight());
2958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mHidBuffer.clear();
2978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouch.generateDescriptor(mHidBuffer);
2988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mHidBuffer.flip();
2998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("HID descriptor size: " + mHidBuffer.limit());
3018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("HID report size: " + mMultitouch.getReportSize());
3028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        final int maxPacketSize = mControlEndpoint.getMaxPacketSize();
3048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("Control endpoint max packet size: " + maxPacketSize);
3058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mMultitouch.getReportSize() > maxPacketSize) {
3068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("HID report is too big for this accessory.");
3078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
3088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        int len = mAccessoryConnection.controlTransfer(
3118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
3128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbAccessoryConstants.ACCESSORY_REGISTER_HID,
3138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                MULTITOUCH_DEVICE_ID, mHidBuffer.limit(), null, 0, 10000);
3148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (len != 0) {
3158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("Device rejected ACCESSORY_REGISTER_HID request.");
3168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
3178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        while (mHidBuffer.hasRemaining()) {
3208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            int position = mHidBuffer.position();
3218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            int count = Math.min(mHidBuffer.remaining(), maxPacketSize);
3228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            len = mAccessoryConnection.controlTransfer(
3238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
3248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    UsbAccessoryConstants.ACCESSORY_SET_HID_REPORT_DESC,
3258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    MULTITOUCH_DEVICE_ID, 0,
3268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mHidBuffer.array(), position, count, 10000);
3278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (len != count) {
3288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mLogger.logError("Device rejected ACCESSORY_SET_HID_REPORT_DESC request.");
3298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                return;
3308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
3318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mHidBuffer.position(position + count);
3328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mLogger.log("HID device registered.");
3358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouchEnabled = true;
3378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mMultitouchContacts == null) {
3388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mMultitouchContacts = new UsbHid.Multitouch.Contact[MULTITOUCH_MAX_CONTACTS];
3398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            for (int i = 0; i < MULTITOUCH_MAX_CONTACTS; i++) {
3408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mMultitouchContacts[i] = new UsbHid.Multitouch.Contact();
3418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
3428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
3448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void unregisterHid() {
3468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouch = null;
3478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouchContacts = null;
3488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouchEnabled = false;
3498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
3508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void sendHidTouch(MotionEvent event) {
3528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mMultitouchEnabled) {
3538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Sending touch event: " + event);
3548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            switch (event.getActionMasked()) {
3568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                case MotionEvent.ACTION_DOWN:
3578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                case MotionEvent.ACTION_MOVE: {
3588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int pointerCount =
3598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            Math.min(MULTITOUCH_MAX_CONTACTS, event.getPointerCount());
3608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int historySize = event.getHistorySize();
3618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    for (int p = 0; p < pointerCount; p++) {
3628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mMultitouchContacts[p].id = event.getPointerId(p);
3638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    for (int h = 0; h < historySize; h++) {
3658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        for (int p = 0; p < pointerCount; p++) {
3668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            mMultitouchContacts[p].x = (int)event.getHistoricalX(p, h);
3678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            mMultitouchContacts[p].y = (int)event.getHistoricalY(p, h);
3688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        }
3698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        sendHidTouchReport(pointerCount);
3708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    for (int p = 0; p < pointerCount; p++) {
3728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mMultitouchContacts[p].x = (int)event.getX(p);
3738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mMultitouchContacts[p].y = (int)event.getY(p);
3748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
3758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    sendHidTouchReport(pointerCount);
3768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    break;
3778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
3788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                case MotionEvent.ACTION_CANCEL:
3808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                case MotionEvent.ACTION_UP:
3818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    sendHidTouchReport(0);
3828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    break;
3838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
3848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
3858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
3868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void sendHidTouchReport(int contactCount) {
3888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mHidBuffer.clear();
3898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mMultitouch.generateReport(mHidBuffer, mMultitouchContacts, contactCount);
3908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mHidBuffer.flip();
3918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        int count = mHidBuffer.limit();
3938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        int len = mAccessoryConnection.controlTransfer(
3948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
3958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbAccessoryConstants.ACCESSORY_SEND_HID_EVENT,
3968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                MULTITOUCH_DEVICE_ID, 0,
3978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mHidBuffer.array(), 0, count, 10000);
3988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (len != count) {
3998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("Device rejected ACCESSORY_SEND_HID_EVENT request.");
4008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
4018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void startServices() {
4058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mDisplaySinkService = new DisplaySinkService(this, mTransport,
4068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                getResources().getConfiguration().densityDpi);
4078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mDisplaySinkService.start();
4088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mAttached) {
4108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mDisplaySinkService.setSurfaceView(mSurfaceView);
4118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void stopServices() {
4158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mDisplaySinkService != null) {
4168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mDisplaySinkService.stop();
4178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mDisplaySinkService = null;
4188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
4228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void onAttachedToWindow() {
4238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super.onAttachedToWindow();
4248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mAttached = true;
4268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mDisplaySinkService != null) {
4278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mDisplaySinkService.setSurfaceView(mSurfaceView);
4288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
4328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void onDetachedFromWindow() {
4338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super.onDetachedFromWindow();
4348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mAttached = false;
4368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mDisplaySinkService != null) {
4378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mDisplaySinkService.setSurfaceView(null);
4388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private int getProtocol(UsbDeviceConnection conn) {
4428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        byte buffer[] = new byte[2];
4438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        int len = conn.controlTransfer(
4448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR,
4458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbAccessoryConstants.ACCESSORY_GET_PROTOCOL, 0, 0, buffer, 2, 10000);
4468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (len != 2) {
4478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return -1;
4488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        return (buffer[1] << 8) | buffer[0];
4508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void sendString(UsbDeviceConnection conn, int index, String string) {
4538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        byte[] buffer = (string + "\0").getBytes();
4548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        int len = conn.controlTransfer(UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
4558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                UsbAccessoryConstants.ACCESSORY_SEND_STRING, 0, index,
4568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                buffer, buffer.length, 10000);
4578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (len != buffer.length) {
4588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.logError("Failed to send string " + index + ": \"" + string + "\"");
4598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        } else {
4608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogger.log("Sent string " + index + ": \"" + string + "\"");
4618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static boolean isAccessory(UsbDevice device) {
4658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        final int vid = device.getVendorId();
4668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        final int pid = device.getProductId();
4678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        return vid == UsbAccessoryConstants.USB_ACCESSORY_VENDOR_ID
4688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                && (pid == UsbAccessoryConstants.USB_ACCESSORY_PRODUCT_ID
4698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        || pid == UsbAccessoryConstants.USB_ACCESSORY_ADB_PRODUCT_ID);
4708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    class TextLogger extends Logger {
4738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        @Override
4748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void log(final String message) {
4758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            Log.d(TAG, message);
4768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mLogTextView.post(new Runnable() {
4788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                @Override
4798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                public void run() {
4808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mLogTextView.append(message);
4818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mLogTextView.append("\n");
4828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
4838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            });
4848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
4858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
4868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
4878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    class DeviceReceiver extends BroadcastReceiver {
4888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        @Override
4898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void onReceive(Context context, Intent intent) {
4908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
4918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (device != null) {
4928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                String action = intent.getAction();
4938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
4948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    onDeviceAttached(device);
4958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
4968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    onDeviceDetached(device);
4978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else if (action.equals(ACTION_USB_DEVICE_PERMISSION)) {
4988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
4998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mLogger.log("Device permission granted: " + device);
5008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        onDeviceAttached(device);
5018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    } else {
5028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        mLogger.logError("Device permission denied: " + device);
5038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
5048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
5058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
5068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
5078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
5088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown}
509