Add USB MissleLauncher sample program

This is sample code that controls a USB missle launcher using USB host APIs

Change-Id: Ic4201faccc4562bf114c70d50b0cab6a00d28a98
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/build/sdk.atree b/build/sdk.atree
index d098400..e5da0a2 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -177,6 +177,8 @@
 development/samples/SpinnerTest                samples/${PLATFORM_NAME}/SpinnerTest
 development/samples/TicTacToeLib               samples/${PLATFORM_NAME}/TicTacToeLib
 development/samples/TicTacToeMain              samples/${PLATFORM_NAME}/TicTacToeMain
+development/samples/USB/MissileLauncher        samples/${PLATFORM_NAME}/USB/MissileLauncher
+development/samples/USB/AdbTest                samples/${PLATFORM_NAME}/USB/AdbTest
 development/samples/VoiceRecognitionService    samples/${PLATFORM_NAME}/VoiceRecognitionService
 development/samples/WeatherListWidget          samples/${PLATFORM_NAME}/WeatherListWidget
 development/apps/WidgetPreview                 samples/${PLATFORM_NAME}/WidgetPreview
diff --git a/samples/USB/Android.mk b/samples/USB/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/samples/USB/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/samples/USB/MissileLauncher/Android.mk b/samples/USB/MissileLauncher/Android.mk
new file mode 100644
index 0000000..daabb0c
--- /dev/null
+++ b/samples/USB/MissileLauncher/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := MissileLauncher
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/samples/USB/MissileLauncher/AndroidManifest.xml b/samples/USB/MissileLauncher/AndroidManifest.xml
new file mode 100644
index 0000000..b1c2c2b
--- /dev/null
+++ b/samples/USB/MissileLauncher/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.missilelauncher">
+
+    <uses-feature android:name="android.hardware.usb.host" />
+    <uses-sdk android:minSdkVersion="12" />
+
+    <application>
+        <activity android:name="MissileLauncherActivity"
+            android:label="Missile Launcher"
+            android:screenOrientation="nosensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+            </intent-filter>
+
+            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+                android:resource="@xml/device_filter" />
+        </activity>
+    </application>
+</manifest>
diff --git a/samples/USB/MissileLauncher/README.txt b/samples/USB/MissileLauncher/README.txt
new file mode 100644
index 0000000..863baad
--- /dev/null
+++ b/samples/USB/MissileLauncher/README.txt
@@ -0,0 +1,13 @@
+MissileLauncher is a simple program that controls Dream Cheeky USB missile launchers.
+You control the left/right/up/down orientation of the launcher using the accelerometer.
+Tilt the tablet to change the direction of the launcher.
+Pressing the "Fire" button will fire one missile.
+
+This program serves as an example of the following USB host features:
+
+- filtering for multiple devices based on vendor and product IDs (see device_filter.xml)
+
+- Sending control requests on endpoint zero that contain data
+
+- Receiving packets on an interrupt endpoint using a thread that calls
+  UsbRequest.queue and UsbDeviceConnection.requestWait()
diff --git a/samples/USB/MissileLauncher/_index.html b/samples/USB/MissileLauncher/_index.html
new file mode 100644
index 0000000..99a5cf4
--- /dev/null
+++ b/samples/USB/MissileLauncher/_index.html
@@ -0,0 +1,11 @@
+<p>MissileLauncher is a simple program that controls Dream Cheeky USB missile launchers.
+You control the left/right/up/down orientation of the launcher using the accelerometer.
+Tilt the tablet to change the direction of the launcher. Pressing the <strong>Fire</strong> button will fire one missile.</p>
+
+<p>This program serves as an example of the following USB host features:</p>
+<ul>
+<li>filtering for multiple devices based on vendor and product IDs (see <code>device_filter.xml</code>)</li>
+<li>Sending control requests on endpoint zero that contain data</li>
+<li>Receiving packets on an interrupt endpoint using a thread that calls
+  {@link android.hardware.usb.UsbRequest#queue queue()} and {@link android.hardware.usb.UsbDeviceConnection#requestWait requestWait()}.</li>
+</p>
\ No newline at end of file
diff --git a/samples/USB/MissileLauncher/default.properties b/samples/USB/MissileLauncher/default.properties
new file mode 100644
index 0000000..3ac2523
--- /dev/null
+++ b/samples/USB/MissileLauncher/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-12
diff --git a/samples/USB/MissileLauncher/res/layout/launcher.xml b/samples/USB/MissileLauncher/res/layout/launcher.xml
new file mode 100644
index 0000000..1e488f7
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/layout/launcher.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    >
+
+        <Button android:id="@+id/fire"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="25dp"
+            android:layout_marginBottom="25dp"
+            android:layout_marginLeft="25dp"
+            android:layout_marginRight="25dp"
+            android:textSize="36sp"
+            android:text="@string/fire">
+        </Button>
+
+
+</LinearLayout>
+
+
diff --git a/samples/USB/MissileLauncher/res/values/strings.xml b/samples/USB/MissileLauncher/res/values/strings.xml
new file mode 100644
index 0000000..3f7f85d
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <string name="fire">Fire!</string>
+
+</resources>
+
diff --git a/samples/USB/MissileLauncher/res/xml/device_filter.xml b/samples/USB/MissileLauncher/res/xml/device_filter.xml
new file mode 100644
index 0000000..391a7f1
--- /dev/null
+++ b/samples/USB/MissileLauncher/res/xml/device_filter.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- vendor and product ID for Dream Cheeky USB Missle Launcher -->
+    <usb-device vendor-id="2689" product-id="1793" />
+    <!-- vendor and product ID for Dream Cheeky Wireless USB Missle Launcher -->
+    <usb-device vendor-id="2689" product-id="65281" />
+</resources>
diff --git a/samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java b/samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java
new file mode 100644
index 0000000..75e191c
--- /dev/null
+++ b/samples/USB/MissileLauncher/src/com/android/missilelauncher/MissileLauncherActivity.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.missilelauncher;
+
+import java.nio.ByteBuffer;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbRequest;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+public class MissileLauncherActivity extends Activity
+        implements View.OnClickListener, Runnable {
+
+    private static final String TAG = "MissileLauncherActivity";
+
+    private Button mFire;
+    private UsbManager mUsbManager;
+    private UsbDevice mDevice;
+    private UsbDeviceConnection mConnection;
+    private UsbEndpoint mEndpointIntr;
+    private SensorManager mSensorManager;
+    private Sensor mGravitySensor;
+
+    // USB control commands
+    private static final int COMMAND_UP = 1;
+    private static final int COMMAND_DOWN = 2;
+    private static final int COMMAND_RIGHT = 4;
+    private static final int COMMAND_LEFT = 8;
+    private static final int COMMAND_FIRE = 16;
+    private static final int COMMAND_STOP = 32;
+    private static final int COMMAND_STATUS = 64;
+
+    // constants for accelerometer orientation
+    private static final int TILT_LEFT = 1;
+    private static final int TILT_RIGHT = 2;
+    private static final int TILT_UP = 4;
+    private static final int TILT_DOWN = 8;
+    private static final double THRESHOLD = 5.0;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.launcher);
+        mFire = (Button)findViewById(R.id.fire);
+        mFire.setOnClickListener(this);
+
+        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+        mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
+        mGravitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mSensorManager.unregisterListener(mGravityListener);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mSensorManager.registerListener(mGravityListener, mGravitySensor,
+                SensorManager.SENSOR_DELAY_NORMAL);
+
+        Intent intent = getIntent();
+        Log.d(TAG, "intent: " + intent);
+        String action = intent.getAction();
+
+        UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+        if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+            setDevice(device);
+        } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+            if (mDevice != null && mDevice.equals(device)) {
+                setDevice(null);
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    private void setDevice(UsbDevice device) {
+        Log.d(TAG, "setDevice " + device);
+        if (device.getInterfaceCount() != 1) {
+            Log.e(TAG, "could not find interface");
+            return;
+        }
+        UsbInterface intf = device.getInterface(0);
+        // device should have one endpoint
+        if (intf.getEndpointCount() != 1) {
+            Log.e(TAG, "could not find endpoint");
+            return;
+        }
+        // endpoint should be of type interrupt
+        UsbEndpoint ep = intf.getEndpoint(0);
+        if (ep.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
+            Log.e(TAG, "endpoint is not interrupt type");
+            return;
+        }
+        mDevice = device;
+        mEndpointIntr = ep;
+        if (device != null) {
+            UsbDeviceConnection connection = mUsbManager.openDevice(device);
+            if (connection != null && connection.claimInterface(intf, true)) {
+                Log.d(TAG, "open SUCCESS");
+                mConnection = connection;
+                Thread thread = new Thread(this);
+                thread.start();
+
+            } else {
+                Log.d(TAG, "open FAIL");
+                mConnection = null;
+            }
+         }
+    }
+
+    private void sendCommand(int control) {
+        synchronized (this) {
+            if (control != COMMAND_STATUS) {
+                Log.d(TAG, "sendMove " + control);
+            }
+            if (mConnection != null) {
+                byte[] message = new byte[1];
+                message[0] = (byte)control;
+                // Send command via a control request on endpoint zero
+                mConnection.controlTransfer(0x21, 0x9, 0x200, 0, message, message.length, 0);
+            }
+        }
+    }
+
+    public void onClick(View v) {
+        if (v == mFire) {
+            sendCommand(COMMAND_FIRE);
+        }
+    }
+
+    private int mLastValue = 0;
+
+    SensorEventListener mGravityListener = new SensorEventListener() {
+        public void onSensorChanged(SensorEvent event) {
+
+            // compute current tilt
+            int value = 0;
+            if (event.values[0] < -THRESHOLD) {
+                value += TILT_LEFT;
+            } else if (event.values[0] > THRESHOLD) {
+                value += TILT_RIGHT;
+            }
+            if (event.values[1] < -THRESHOLD) {
+                value += TILT_UP;
+            } else if (event.values[1] > THRESHOLD) {
+                value += TILT_DOWN;
+            }
+
+            if (value != mLastValue) {
+                mLastValue = value;
+                // send motion command if the tilt changed
+                switch (value) {
+                    case TILT_LEFT:
+                        sendCommand(COMMAND_LEFT);
+                        break;
+                    case TILT_RIGHT:
+                       sendCommand(COMMAND_RIGHT);
+                        break;
+                    case TILT_UP:
+                        sendCommand(COMMAND_UP);
+                        break;
+                    case TILT_DOWN:
+                        sendCommand(COMMAND_DOWN);
+                        break;
+                    default:
+                        sendCommand(COMMAND_STOP);
+                        break;
+                }
+            }
+        }
+
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // ignore
+        }
+    };
+
+    @Override
+    public void run() {
+        ByteBuffer buffer = ByteBuffer.allocate(1);
+        UsbRequest request = new UsbRequest();
+        request.initialize(mConnection, mEndpointIntr);
+        byte status = -1;
+        while (true) {
+            // queue a request on the interrupt endpoint
+            request.queue(buffer, 1);
+            // send poll status command
+            sendCommand(COMMAND_STATUS);
+            // wait for status event
+            if (mConnection.requestWait() == request) {
+                byte newStatus = buffer.get(0);
+                if (newStatus != status) {
+                    Log.d(TAG, "got status " + newStatus);
+                    status = newStatus;
+                    if ((status & COMMAND_FIRE) != 0) {
+                        // stop firing
+                        sendCommand(COMMAND_STOP);
+                    }
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
+            } else {
+                Log.e(TAG, "requestWait failed, exiting");
+                break;
+            }
+        }
+    }
+}
+
+
diff --git a/samples/USB/_index.html b/samples/USB/_index.html
new file mode 100644
index 0000000..f82011d
--- /dev/null
+++ b/samples/USB/_index.html
@@ -0,0 +1 @@
+<p>A set of samples that demonstrate how to use various features of the USB APIs.</p>
\ No newline at end of file