1badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park/*
2badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * Copyright (C) 2016 The Android Open Source Project
3badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park *
4badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * Licensed under the Apache License, Version 2.0 (the "License");
5badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * you may not use this file except in compliance with the License.
6badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * You may obtain a copy of the License at
7badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park *
8badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park *      http://www.apache.org/licenses/LICENSE-2.0
9badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park *
10badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * Unless required by applicable law or agreed to in writing, software
11badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * distributed under the License is distributed on an "AS IS" BASIS,
12badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * See the License for the specific language governing permissions and
14badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park * limitations under the License.
15badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park */
16badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkpackage com.android.hardware.usb.aoapdevicetest;
17badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
18badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.app.Activity;
19badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.app.PendingIntent;
20badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.content.BroadcastReceiver;
21badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.content.Context;
22badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.content.Intent;
23badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.content.IntentFilter;
24badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.hardware.usb.UsbAccessory;
25badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.hardware.usb.UsbManager;
26badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.os.Bundle;
27badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.os.ParcelFileDescriptor;
28badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport android.util.Log;
29badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
30badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport java.io.FileInputStream;
31badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport java.io.FileOutputStream;
32badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport java.io.IOException;
33badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
34badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkimport libcore.io.IoUtils;
35badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
36badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Parkpublic class UsbAoapDeviceTestActivity extends Activity {
37badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private static final String TAG = UsbAoapDeviceTestActivity.class.getSimpleName();
38badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private static final boolean DBG = true;
39badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
40badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private static final String ACTION_USB_ACCESSORY_PERMISSION =
41badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            "com.android.hardware.usb.aoapdevicetest.ACTION_USB_ACCESSORY_PERMISSION";
42badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
43badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private UsbManager mUsbManager;
44badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private AccessoryReceiver mReceiver;
45badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private ParcelFileDescriptor mFd;
46badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private ReaderThread mReaderThread;
47badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private UsbAccessory mAccessory;
48badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
49badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    @Override
50badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    protected void onCreate(Bundle savedInstanceState) {
51badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        super.onCreate(savedInstanceState);
52badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
53badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        setContentView(R.layout.device);
54badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
55badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
56badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        IntentFilter filter = new IntentFilter();
57badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
58badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
59badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        filter.addAction(ACTION_USB_ACCESSORY_PERMISSION);
60badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        mReceiver = new AccessoryReceiver();
61badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        registerReceiver(mReceiver, filter);
62badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
63badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        Intent intent = getIntent();
64badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        if (intent.getAction().equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
65badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            UsbAccessory accessory =
66badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
67badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            if (accessory != null) {
68badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                onAccessoryAttached(accessory);
69badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            } else {
70badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                throw new RuntimeException("USB accessory is null.");
71badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            }
72badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        } else {
73badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            finish();
74badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
75badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    }
76badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
77badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    @Override
78badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    protected void onDestroy() {
79badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        super.onDestroy();
80badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        unregisterReceiver(mReceiver);
81badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        IoUtils.closeQuietly(mFd);
82badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        if (mReaderThread != null) {
83badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            mReaderThread.requestToQuit();
84badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            try {
85badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                mReaderThread.join(1000);
86badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            } catch (InterruptedException e) {
87badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            }
88badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            if (mReaderThread.isAlive()) { // reader thread stuck
89badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                Log.w(TAG, "ReaderThread still alive");
90badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            }
91badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
92badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    }
93badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
94badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private void onAccessoryAttached(UsbAccessory accessory) {
95badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        Log.i(TAG, "Starting AOAP discovery protocol, accessory attached: " + accessory);
96badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        // Check whether we have permission to access the accessory.
97badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        if (!mUsbManager.hasPermission(accessory)) {
98badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            Log.i(TAG, "Prompting the user for access to the accessory.");
99badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            Intent intent = new Intent(ACTION_USB_ACCESSORY_PERMISSION);
100badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            intent.setPackage(getPackageName());
101badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            PendingIntent pendingIntent = PendingIntent.getBroadcast(
102badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
103badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            mUsbManager.requestPermission(accessory, pendingIntent);
104badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            return;
105badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
106badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        mFd = mUsbManager.openAccessory(accessory);
107badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        if (mFd == null) {
108badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            Log.e(TAG, "UsbManager.openAccessory returned null");
109badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            finish();
110badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            return;
111badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
112badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        mAccessory = accessory;
113badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        mReaderThread = new ReaderThread(mFd);
114badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        mReaderThread.start();
115badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    }
116badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
117badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private void onAccessoryDetached(UsbAccessory accessory) {
118badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        Log.i(TAG, "Accessory detached: " + accessory);
119badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        finish();
120badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    }
121badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
122badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private class ReaderThread extends Thread {
123badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private boolean mShouldQuit = false;
124badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private final FileInputStream mInputStream;
125badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private final FileOutputStream mOutputStream;
126badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private final byte[] mBuffer = new byte[16384];
127badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
128badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private ReaderThread(ParcelFileDescriptor fd) {
129badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            super("AOAP");
130badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            mInputStream = new FileInputStream(fd.getFileDescriptor());
131badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            mOutputStream = new FileOutputStream(fd.getFileDescriptor());
132badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
133badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
134badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private synchronized void requestToQuit() {
135badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            mShouldQuit = true;
136badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
137badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
138badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        private synchronized boolean shouldQuit() {
139badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            return mShouldQuit;
140badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
141badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
142badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        @Override
143badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        public void run() {
144badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            while (!shouldQuit()) {
145badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                try {
146badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    int read = mInputStream.read(mBuffer);
147badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                } catch (IOException e) {
148badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    Log.i(TAG, "ReaderThread IOException", e);
149badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    // AOAP App should release FD when IOException happens.
150badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    // If FD is kept, device will not behave nicely on reset and multiple reset
151badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    // can be required.
152badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    finish();
153badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    return;
154badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                }
155badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            }
156badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
157badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    }
158badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park
159badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    private class AccessoryReceiver extends BroadcastReceiver {
160badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        @Override
161badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        public void onReceive(Context context, Intent intent) {
162badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
163badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            if (accessory != null) {
164badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                String action = intent.getAction();
165badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                if (action.equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
166badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    onAccessoryAttached(accessory);
167badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                } else if (action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)) {
168badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    if (mAccessory != null && mAccessory.equals(accessory)) {
169badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                        onAccessoryDetached(accessory);
170badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    }
171badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                } else if (action.equals(ACTION_USB_ACCESSORY_PERMISSION)) {
172badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
173badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                        Log.i(TAG, "Accessory permission granted: " + accessory);
174badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                        onAccessoryAttached(accessory);
175badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    } else {
176badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                        Log.e(TAG, "Accessory permission denied: " + accessory);
177badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                        finish();
178badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                    }
179badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park                }
180badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park            }
181badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park        }
182badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park    }
183badbbae6fa2846415778b2a152a0758acbf0eb74Keun-young Park}
184