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.google.android.car.usb.aoap.companion;
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 import android.view.View;
30 import android.widget.Button;
31 
32 import java.io.FileInputStream;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.util.Objects;
38 
39 import javax.annotation.concurrent.GuardedBy;
40 
41 /** Activity for AOAP phone test app. */
42 public class AoapPhoneCompanionActivity extends Activity {
43     private static final String TAG = AoapPhoneCompanionActivity.class.getSimpleName();
44     private static final boolean DBG = true;
45     private static final ByteOrder ORDER = ByteOrder.BIG_ENDIAN;
46 
47     private static final String ACTION_USB_ACCESSORY_PERMISSION =
48             "com.google.android.car.usb.aoap.companion.ACTION_USB_ACCESSORY_PERMISSION";
49 
50     private UsbManager mUsbManager;
51     private AccessoryReceiver mReceiver;
52     private ParcelFileDescriptor mFd;
53     private ProcessorThread mProcessorThread;
54     private UsbAccessory mAccessory;
55 
56     @Override
onCreate(Bundle savedInstanceState)57     protected void onCreate(Bundle savedInstanceState) {
58         super.onCreate(savedInstanceState);
59 
60         setContentView(R.layout.device);
61         Button exitButton = (Button) findViewById(R.id.exit);
62         exitButton.setOnClickListener(new View.OnClickListener() {
63                 @Override
64                 public void onClick(View view) {
65                     finish();
66                 }
67             });
68 
69         mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
70         configureReceiver();
71         handleIntent(getIntent());
72     }
73 
handleIntent(Intent intent)74     private void handleIntent(Intent intent) {
75         if (intent.getAction().equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
76             UsbAccessory accessory =
77                     (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
78             if (accessory != null) {
79                 onAccessoryAttached(accessory);
80             } else {
81                 throw new RuntimeException("USB accessory is null.");
82             }
83         } else {
84             finish();
85         }
86     }
87 
configureReceiver()88     private void configureReceiver() {
89         IntentFilter filter = new IntentFilter();
90         filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
91         filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
92         filter.addAction(ACTION_USB_ACCESSORY_PERMISSION);
93         mReceiver = new AccessoryReceiver();
94         registerReceiver(mReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
95     }
96 
97     @Override
onDestroy()98     protected void onDestroy() {
99         super.onDestroy();
100         unregisterReceiver(mReceiver);
101         // close quietly
102         if (mFd != null) {
103             try {
104                 mFd.close();
105             } catch (RuntimeException e) {
106                 throw e;
107             } catch (Exception e) {
108             }
109         }
110         if (mProcessorThread != null) {
111             mProcessorThread.requestToQuit();
112             try {
113                 mProcessorThread.join(1000);
114             } catch (InterruptedException e) {
115             }
116             if (mProcessorThread.isAlive()) { // reader thread stuck
117                 Log.w(TAG, "ProcessorThread still alive");
118             }
119         }
120     }
121 
onAccessoryAttached(UsbAccessory accessory)122     private void onAccessoryAttached(UsbAccessory accessory) {
123         Log.i(TAG, "Starting AOAP discovery protocol, accessory attached: " + accessory);
124         // Check whether we have permission to access the accessory.
125         if (!mUsbManager.hasPermission(accessory)) {
126             Log.i(TAG, "Prompting the user for access to the accessory.");
127             Intent intent = new Intent(ACTION_USB_ACCESSORY_PERMISSION);
128             intent.setPackage(getPackageName());
129             PendingIntent pendingIntent = PendingIntent.getBroadcast(
130                     this, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
131             mUsbManager.requestPermission(accessory, pendingIntent);
132             return;
133         }
134         mFd = mUsbManager.openAccessory(accessory);
135         if (mFd == null) {
136             Log.e(TAG, "UsbManager.openAccessory returned null");
137             finish();
138             return;
139         }
140         mAccessory = accessory;
141         mProcessorThread = new ProcessorThread(mFd);
142         mProcessorThread.start();
143     }
144 
onAccessoryDetached(UsbAccessory accessory)145     private void onAccessoryDetached(UsbAccessory accessory) {
146         Log.i(TAG, "Accessory detached: " + accessory);
147         finish();
148     }
149 
150     private class ProcessorThread extends Thread {
151 
152         private final Object mLock = new Object();
153 
154         @GuardedBy("mLock")
155         private boolean mShouldQuit = false;
156 
157         private final FileInputStream mInputStream;
158         private final FileOutputStream mOutputStream;
159         private final byte[] mBuffer = new byte[16384];
160 
ProcessorThread(ParcelFileDescriptor fd)161         private ProcessorThread(ParcelFileDescriptor fd) {
162             super("AOAP");
163             mInputStream = new FileInputStream(fd.getFileDescriptor());
164             mOutputStream = new FileOutputStream(fd.getFileDescriptor());
165         }
166 
requestToQuit()167         private void requestToQuit() {
168             synchronized (mLock) {
169                 mShouldQuit = true;
170             }
171         }
172 
shouldQuit()173         private boolean shouldQuit() {
174             synchronized (mLock) {
175                 return mShouldQuit;
176             }
177         }
178 
byteToInt(byte[] buffer)179         protected int byteToInt(byte[] buffer) {
180             return ByteBuffer.wrap(buffer).order(ORDER).getInt();
181         }
182 
183         @Override
run()184         public void run() {
185             while (!shouldQuit()) {
186                 int readBufferSize = 0;
187                 while (!shouldQuit()) {
188                     try {
189                         int read = mInputStream.read(mBuffer);
190                         if (read == 4 && readBufferSize == 0) {
191                             readBufferSize = byteToInt(mBuffer);
192                             continue;
193                         }
194                         Log.d(TAG, "Read " + read + " bytes");
195                         if (read < readBufferSize) {
196                             break;
197                         }
198                     } catch (IOException e) {
199                         Log.i(TAG, "ProcessorThread IOException", e);
200                         // AOAP App should release FD when IOException happens.
201                         // If FD is kept, device will not behave nicely on reset and multiple reset
202                         // can be required.
203                         finish();
204                         return;
205                     }
206                 }
207                 if (!shouldQuit()) {
208                     byte[] outBuffer = "DONE".getBytes();
209                     try {
210                         mOutputStream.write(outBuffer);
211                     } catch (IOException e) {
212                         Log.i(TAG, "ProcessorThread IOException", e);
213                         finish();
214                         return;
215                     }
216                 }
217             }
218         }
219     }
220 
221     private class AccessoryReceiver extends BroadcastReceiver {
222         @Override
onReceive(Context context, Intent intent)223         public void onReceive(Context context, Intent intent) {
224             UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
225             if (accessory != null) {
226                 String action = intent.getAction();
227                 if (action.equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
228                     onAccessoryAttached(accessory);
229                 } else if (action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)) {
230                     if (mAccessory != null && mAccessory.equals(accessory)) {
231                         onAccessoryDetached(accessory);
232                     }
233                 } else if (Objects.equals(action, ACTION_USB_ACCESSORY_PERMISSION)) {
234                     if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
235                         Log.i(TAG, "Accessory permission granted: " + accessory);
236                         onAccessoryAttached(accessory);
237                     } else {
238                         Log.e(TAG, "Accessory permission denied: " + accessory);
239                         finish();
240                     }
241                 }
242             }
243         }
244     }
245 }
246