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