blob: f71a575788a0bdca59ecc8b14f3a92ed2c12a851 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.car.usb.aoap.companion;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/** Activity for AOAP phone test app. */
public class AoapPhoneCompanionActivity extends Activity {
private static final String TAG = AoapPhoneCompanionActivity.class.getSimpleName();
private static final boolean DBG = true;
private static final ByteOrder ORDER = ByteOrder.BIG_ENDIAN;
private static final String ACTION_USB_ACCESSORY_PERMISSION =
"com.google.android.car.usb.aoap.companion.ACTION_USB_ACCESSORY_PERMISSION";
private UsbManager mUsbManager;
private AccessoryReceiver mReceiver;
private ParcelFileDescriptor mFd;
private ProcessorThread mProcessorThread;
private UsbAccessory mAccessory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.device);
Button exitButton = (Button) findViewById(R.id.exit);
exitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
configureReceiver();
handleIntent(getIntent());
}
private void handleIntent(Intent intent) {
if (intent.getAction().equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
UsbAccessory accessory =
(UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
onAccessoryAttached(accessory);
} else {
throw new RuntimeException("USB accessory is null.");
}
} else {
finish();
}
}
private void configureReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
filter.addAction(ACTION_USB_ACCESSORY_PERMISSION);
mReceiver = new AccessoryReceiver();
registerReceiver(mReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
// close quietly
if (mFd != null) {
try {
mFd.close();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
}
}
if (mProcessorThread != null) {
mProcessorThread.requestToQuit();
try {
mProcessorThread.join(1000);
} catch (InterruptedException e) {
}
if (mProcessorThread.isAlive()) { // reader thread stuck
Log.w(TAG, "ProcessorThread still alive");
}
}
}
private void onAccessoryAttached(UsbAccessory accessory) {
Log.i(TAG, "Starting AOAP discovery protocol, accessory attached: " + accessory);
// Check whether we have permission to access the accessory.
if (!mUsbManager.hasPermission(accessory)) {
Log.i(TAG, "Prompting the user for access to the accessory.");
Intent intent = new Intent(ACTION_USB_ACCESSORY_PERMISSION);
intent.setPackage(getPackageName());
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
mUsbManager.requestPermission(accessory, pendingIntent);
return;
}
mFd = mUsbManager.openAccessory(accessory);
if (mFd == null) {
Log.e(TAG, "UsbManager.openAccessory returned null");
finish();
return;
}
mAccessory = accessory;
mProcessorThread = new ProcessorThread(mFd);
mProcessorThread.start();
}
private void onAccessoryDetached(UsbAccessory accessory) {
Log.i(TAG, "Accessory detached: " + accessory);
finish();
}
private class ProcessorThread extends Thread {
private boolean mShouldQuit = false;
private final FileInputStream mInputStream;
private final FileOutputStream mOutputStream;
private final byte[] mBuffer = new byte[16384];
private ProcessorThread(ParcelFileDescriptor fd) {
super("AOAP");
mInputStream = new FileInputStream(fd.getFileDescriptor());
mOutputStream = new FileOutputStream(fd.getFileDescriptor());
}
private synchronized void requestToQuit() {
mShouldQuit = true;
}
private synchronized boolean shouldQuit() {
return mShouldQuit;
}
protected int byteToInt(byte[] buffer) {
return ByteBuffer.wrap(buffer).order(ORDER).getInt();
}
@Override
public void run() {
while (!shouldQuit()) {
int readBufferSize = 0;
while (!shouldQuit()) {
try {
int read = mInputStream.read(mBuffer);
if (read == 4 && readBufferSize == 0) {
readBufferSize = byteToInt(mBuffer);
continue;
}
Log.d(TAG, "Read " + read + " bytes");
if (read < readBufferSize) {
break;
}
} catch (IOException e) {
Log.i(TAG, "ProcessorThread IOException", e);
// AOAP App should release FD when IOException happens.
// If FD is kept, device will not behave nicely on reset and multiple reset
// can be required.
finish();
return;
}
}
if (!shouldQuit()) {
byte[] outBuffer = "DONE".getBytes();
try {
mOutputStream.write(outBuffer);
} catch (IOException e) {
Log.i(TAG, "ProcessorThread IOException", e);
finish();
return;
}
}
}
}
}
private class AccessoryReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
onAccessoryAttached(accessory);
} else if (action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)) {
if (mAccessory != null && mAccessory.equals(accessory)) {
onAccessoryDetached(accessory);
}
} else if (action.equals(ACTION_USB_ACCESSORY_PERMISSION)) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Log.i(TAG, "Accessory permission granted: " + accessory);
onAccessoryAttached(accessory);
} else {
Log.e(TAG, "Accessory permission denied: " + accessory);
finish();
}
}
}
}
}
}