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