Home | History | Annotate | Download | only in usb
      1 /*
      2  * Copyright (C) 2011 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 
     17 package com.android.cts.verifier.usb;
     18 
     19 import com.android.cts.verifier.PassFailButtons;
     20 import com.android.cts.verifier.R;
     21 
     22 import android.app.AlertDialog;
     23 import android.app.Dialog;
     24 import android.app.PendingIntent;
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.DialogInterface;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.pm.PackageManager;
     31 import android.content.res.Configuration;
     32 import android.hardware.usb.UsbAccessory;
     33 import android.hardware.usb.UsbManager;
     34 import android.os.Bundle;
     35 import android.os.Handler;
     36 import android.os.Message;
     37 import android.os.ParcelFileDescriptor;
     38 import android.util.Log;
     39 import android.view.View;
     40 import android.widget.ArrayAdapter;
     41 import android.widget.ListView;
     42 import android.widget.Toast;
     43 
     44 import java.io.FileDescriptor;
     45 import java.io.FileInputStream;
     46 import java.io.FileOutputStream;
     47 import java.io.IOException;
     48 import java.io.InputStream;
     49 import java.io.OutputStream;
     50 
     51 /**
     52  * Test for USB accessories. The test activity interacts with a cts-usb-accessory program that
     53  * acts as an accessory by exchanging a series of messages.
     54  */
     55 public class UsbAccessoryTestActivity extends PassFailButtons.Activity {
     56 
     57     private static final String TAG = UsbAccessoryTestActivity.class.getSimpleName();
     58 
     59     private static final int FILE_DESCRIPTOR_PROBLEM_DIALOG_ID = 1;
     60 
     61     private static final String ACTION_USB_PERMISSION =
     62             "com.android.cts.verifier.usb.USB_PERMISSION";
     63 
     64     private ArrayAdapter<String> mReceivedMessagesAdapter;
     65     private ArrayAdapter<String> mSentMessagesAdapter;
     66     private MessageHandler mHandler;
     67 
     68     private UsbManager mUsbManager;
     69     private PendingIntent mPermissionIntent;
     70     private boolean mPermissionRequestPending;
     71     private UsbReceiver mUsbReceiver;
     72 
     73     private ParcelFileDescriptor mFileDescriptor;
     74 
     75     @Override
     76     protected void onCreate(Bundle savedInstanceState) {
     77         super.onCreate(savedInstanceState);
     78         setContentView(R.layout.usb_main);
     79         setInfoResources(R.string.usb_accessory_test, R.string.usb_accessory_test_info, -1);
     80         setPassFailButtonClickListeners();
     81 
     82         // Don't allow a test pass until the accessory and the Android device exchange messages...
     83         getPassButton().setEnabled(false);
     84 
     85         if (!hasUsbAccessorySupport()) {
     86             showNoUsbAccessoryDialog();
     87         }
     88 
     89         mReceivedMessagesAdapter = new ArrayAdapter<String>(this, R.layout.usb_message_row);
     90         mSentMessagesAdapter = new ArrayAdapter<String>(this, R.layout.usb_message_row);
     91         mHandler = new MessageHandler();
     92 
     93         mUsbManager = (UsbManager) getSystemService(USB_SERVICE);
     94         mPermissionIntent = PendingIntent.getBroadcast(this, 0,
     95                 new Intent(ACTION_USB_PERMISSION), 0);
     96 
     97         mUsbReceiver = new UsbReceiver();
     98         IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
     99         filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
    100         registerReceiver(mUsbReceiver, filter);
    101 
    102         setupListViews();
    103     }
    104 
    105     private boolean hasUsbAccessorySupport() {
    106         return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
    107     }
    108 
    109     private void showNoUsbAccessoryDialog() {
    110         new AlertDialog.Builder(this)
    111             .setIcon(android.R.drawable.ic_dialog_alert)
    112             .setTitle(R.string.usb_not_available_title)
    113             .setMessage(R.string.usb_not_available_message)
    114             .setCancelable(false)
    115             .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    116                 @Override
    117                 public void onClick(DialogInterface dialog, int which) {
    118                     finish();
    119                 }
    120             })
    121             .show();
    122     }
    123 
    124     private void setupListViews() {
    125         ListView sentMessages = (ListView) findViewById(R.id.usb_sent_messages);
    126         ListView receivedMessages = (ListView) findViewById(R.id.usb_received_messages);
    127 
    128         View emptySentView = findViewById(R.id.usb_empty_sent_messages);
    129         View emptyReceivedView = findViewById(R.id.usb_empty_received_messages);
    130         sentMessages.setEmptyView(emptySentView);
    131         receivedMessages.setEmptyView(emptyReceivedView);
    132 
    133         receivedMessages.setAdapter(mReceivedMessagesAdapter);
    134         sentMessages.setAdapter(mSentMessagesAdapter);
    135     }
    136 
    137     class UsbReceiver extends BroadcastReceiver {
    138         @Override
    139         public void onReceive(Context context, Intent intent) {
    140             if (ACTION_USB_PERMISSION.equals(intent.getAction())
    141                     || UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction())) {
    142                 UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
    143                 if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
    144                     openAccessory(accessory);
    145                 } else {
    146                     Log.i(TAG, "Permission denied...");
    147                 }
    148                 mPermissionRequestPending = false;
    149             }
    150         }
    151     }
    152 
    153     private void openAccessory(UsbAccessory accessory) {
    154         mFileDescriptor = mUsbManager.openAccessory(accessory);
    155         if (mFileDescriptor != null) {
    156             FileDescriptor fileDescriptor = mFileDescriptor.getFileDescriptor();
    157             FileInputStream inputStream = new FileInputStream(fileDescriptor);
    158             FileOutputStream outputStream = new FileOutputStream(fileDescriptor);
    159             new MessageThread(inputStream, outputStream, mHandler).start();
    160         } else {
    161             showDialog(FILE_DESCRIPTOR_PROBLEM_DIALOG_ID);
    162         }
    163     }
    164 
    165     static class MessageThread extends Thread {
    166 
    167         private final InputStream mInputStream;
    168 
    169         private final OutputStream mOutputStream;
    170 
    171         private final MessageHandler mHandler;
    172 
    173         private int mNextMessageNumber = 0;
    174 
    175         MessageThread(InputStream inputStream, OutputStream outputStream, MessageHandler handler) {
    176             this.mInputStream = inputStream;
    177             this.mOutputStream = outputStream;
    178             this.mHandler = handler;
    179         }
    180 
    181         @Override
    182         public void run() {
    183             mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_STARTING);
    184 
    185             try {
    186                 // Wait a bit or else the messages can appear to quick and be confusing...
    187                 Thread.sleep(2000);
    188                 sendMessage();
    189 
    190                 // Wait for response and send message acks...
    191                 int numRead = 0;
    192                 byte[] buffer = new byte[16384];
    193                 while (numRead >= 0) {
    194                     numRead = mInputStream.read(buffer);
    195                     if (numRead > 0) {
    196                         handleReceivedMessage(buffer, numRead);
    197                     }
    198                 }
    199             } catch (IOException e) {
    200                 Log.e(TAG, "Exception while reading from input stream", e);
    201                 mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_EXCEPTION);
    202             } catch (InterruptedException e) {
    203                 Log.e(TAG, "Exception while reading from input stream", e);
    204                 mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_EXCEPTION);
    205             }
    206             mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_ENDING);
    207         }
    208 
    209         private void handleReceivedMessage(byte[] buffer, int numRead) throws IOException {
    210             // TODO: Check the contents of the message?
    211             String text = new String(buffer, 0, numRead).trim();
    212             mHandler.sendReceivedMessage(text);
    213 
    214             // Send back a response..
    215             if (mNextMessageNumber <= 10) {
    216                 sendMessage();
    217             } else {
    218                 mHandler.sendEmptyMessage(MessageHandler.TEST_PASSED);
    219             }
    220         }
    221 
    222         private void sendMessage() throws IOException {
    223             String text = "Message from Android device #" + mNextMessageNumber++;
    224             mOutputStream.write(text.getBytes());
    225             mHandler.sendSentMessage(text);
    226         }
    227     }
    228 
    229     class MessageHandler extends Handler {
    230 
    231         static final int RECEIVED_MESSAGE = 1;
    232 
    233         static final int SENT_MESSAGE = 2;
    234 
    235         static final int MESSAGE_THREAD_STARTING = 3;
    236 
    237         static final int MESSAGE_THREAD_EXCEPTION = 4;
    238 
    239         static final int MESSAGE_THREAD_ENDING = 5;
    240 
    241         static final int TEST_PASSED = 6;
    242 
    243         @Override
    244         public void handleMessage(Message msg) {
    245             super.handleMessage(msg);
    246             switch (msg.what) {
    247                 case RECEIVED_MESSAGE:
    248                     mReceivedMessagesAdapter.add((String) msg.obj);
    249                     break;
    250 
    251                 case SENT_MESSAGE:
    252                     mSentMessagesAdapter.add((String) msg.obj);
    253                     break;
    254 
    255                 case MESSAGE_THREAD_STARTING:
    256                     showToast(R.string.usb_message_thread_started);
    257                     break;
    258 
    259                 case MESSAGE_THREAD_EXCEPTION:
    260                     showToast(R.string.usb_message_thread_exception);
    261                     break;
    262 
    263                 case MESSAGE_THREAD_ENDING:
    264                     showToast(R.string.usb_message_thread_ended);
    265                     break;
    266 
    267                 case TEST_PASSED:
    268                     showToast(R.string.usb_test_passed);
    269                     getPassButton().setEnabled(true);
    270                     break;
    271 
    272                 default:
    273                     throw new IllegalArgumentException("Bad message type: " + msg.what);
    274             }
    275         }
    276 
    277         private void showToast(int messageId) {
    278             Toast.makeText(UsbAccessoryTestActivity.this, messageId, Toast.LENGTH_SHORT).show();
    279         }
    280 
    281         void sendReceivedMessage(String text) {
    282             Message message = Message.obtain(this, RECEIVED_MESSAGE);
    283             message.obj = text;
    284             sendMessage(message);
    285         }
    286 
    287         void sendSentMessage(String text) {
    288             Message message = Message.obtain(this, SENT_MESSAGE);
    289             message.obj = text;
    290             sendMessage(message);
    291         }
    292     }
    293 
    294     @Override
    295     protected void onResume() {
    296         super.onResume();
    297         UsbAccessory[] accessories = mUsbManager.getAccessoryList();
    298         UsbAccessory accessory = accessories != null && accessories.length > 0
    299                 ? accessories[0]
    300                 : null;
    301         if (accessory != null) {
    302             if (mUsbManager.hasPermission(accessory)) {
    303                 openAccessory(accessory);
    304             } else {
    305                 if (!mPermissionRequestPending) {
    306                     mUsbManager.requestPermission(accessory, mPermissionIntent);
    307                     mPermissionRequestPending = true;
    308                 }
    309             }
    310         }
    311     }
    312 
    313     @Override
    314     protected void onPause() {
    315         super.onPause();
    316         if (mFileDescriptor != null) {
    317             try {
    318                 mFileDescriptor.close();
    319             } catch (IOException e) {
    320                 Log.e(TAG, "Exception while closing file descriptor", e);
    321             } finally {
    322                 mFileDescriptor = null;
    323             }
    324         }
    325     }
    326 
    327     @Override
    328     public void onConfigurationChanged(Configuration newConfig) {
    329         super.onConfigurationChanged(newConfig);
    330         setContentView(R.layout.usb_main);
    331         setupListViews();
    332     }
    333 
    334     @Override
    335     public Dialog onCreateDialog(int id, Bundle args) {
    336         switch (id) {
    337             case FILE_DESCRIPTOR_PROBLEM_DIALOG_ID:
    338                 return new AlertDialog.Builder(this)
    339                     .setIcon(android.R.drawable.ic_dialog_alert)
    340                     .setTitle(R.string.usb_accessory_test)
    341                     .setMessage(R.string.usb_file_descriptor_error)
    342                     .create();
    343 
    344             default:
    345                 return super.onCreateDialog(id, args);
    346         }
    347     }
    348 
    349     @Override
    350     protected void onDestroy() {
    351         super.onDestroy();
    352         unregisterReceiver(mUsbReceiver);
    353     }
    354 }
    355