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