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  */
16 package com.android.hardware.usb.aoapdevicetest;
17 
18 import android.app.Activity;
19 import android.app.PendingIntent;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.hardware.usb.UsbAccessory;
25 import android.hardware.usb.UsbManager;
26 import android.os.Bundle;
27 import android.os.ParcelFileDescriptor;
28 import android.util.Log;
29 
30 import java.io.FileInputStream;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 
34 import libcore.io.IoUtils;
35 
36 public 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
onCreate(Bundle savedInstanceState)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
onDestroy()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 
onAccessoryAttached(UsbAccessory accessory)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 
onAccessoryDetached(UsbAccessory accessory)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 
ReaderThread(ParcelFileDescriptor fd)128         private ReaderThread(ParcelFileDescriptor fd) {
129             super("AOAP");
130             mInputStream = new FileInputStream(fd.getFileDescriptor());
131             mOutputStream = new FileOutputStream(fd.getFileDescriptor());
132         }
133 
requestToQuit()134         private synchronized void requestToQuit() {
135             mShouldQuit = true;
136         }
137 
shouldQuit()138         private synchronized boolean shouldQuit() {
139             return mShouldQuit;
140         }
141 
142         @Override
run()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
onReceive(Context context, Intent intent)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