• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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 
hasUsbAccessorySupport()105     private boolean hasUsbAccessorySupport() {
106         return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
107     }
108 
showNoUsbAccessoryDialog()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 
setupListViews()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
onReceive(Context context, Intent intent)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 
openAccessory(UsbAccessory accessory)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 
MessageThread(InputStream inputStream, OutputStream outputStream, MessageHandler handler)175         MessageThread(InputStream inputStream, OutputStream outputStream, MessageHandler handler) {
176             this.mInputStream = inputStream;
177             this.mOutputStream = outputStream;
178             this.mHandler = handler;
179         }
180 
181         @Override
run()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 
handleReceivedMessage(byte[] buffer, int numRead)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 
sendMessage()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
handleMessage(Message msg)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 
showToast(int messageId)277         private void showToast(int messageId) {
278             Toast.makeText(UsbAccessoryTestActivity.this, messageId, Toast.LENGTH_SHORT).show();
279         }
280 
sendReceivedMessage(String text)281         void sendReceivedMessage(String text) {
282             Message message = Message.obtain(this, RECEIVED_MESSAGE);
283             message.obj = text;
284             sendMessage(message);
285         }
286 
sendSentMessage(String text)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
onResume()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
onPause()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
onConfigurationChanged(Configuration newConfig)328     public void onConfigurationChanged(Configuration newConfig) {
329         super.onConfigurationChanged(newConfig);
330         setContentView(R.layout.usb_main);
331         setupListViews();
332     }
333 
334     @Override
onCreateDialog(int id, Bundle args)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
onDestroy()350     protected void onDestroy() {
351         super.onDestroy();
352         unregisterReceiver(mUsbReceiver);
353     }
354 }
355