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 an 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import android.app.PendingIntent; 20 import android.app.Notification; 21 import android.app.NotificationManager; 22 import android.content.BroadcastReceiver; 23 import android.content.ComponentName; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.PackageManager; 29 import android.content.res.Resources; 30 import android.database.ContentObserver; 31 import android.hardware.usb.UsbAccessory; 32 import android.hardware.usb.UsbManager; 33 import android.net.Uri; 34 import android.os.Binder; 35 import android.os.Bundle; 36 import android.os.FileUtils; 37 import android.os.Handler; 38 import android.os.HandlerThread; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.Parcelable; 42 import android.os.ParcelFileDescriptor; 43 import android.os.Process; 44 import android.os.storage.StorageManager; 45 import android.os.storage.StorageVolume; 46 import android.os.SystemProperties; 47 import android.os.UEventObserver; 48 import android.provider.Settings; 49 import android.util.Slog; 50 51 import java.io.File; 52 import java.io.FileDescriptor; 53 import java.io.FileNotFoundException; 54 import java.io.IOException; 55 import java.io.PrintWriter; 56 import java.util.ArrayList; 57 import java.util.List; 58 59 /** 60 * UsbDeviceManager manages USB state in device mode. 61 */ 62 public class UsbDeviceManager { 63 64 private static final String TAG = UsbDeviceManager.class.getSimpleName(); 65 private static final boolean DEBUG = false; 66 67 private static final String USB_STATE_MATCH = 68 "DEVPATH=/devices/virtual/android_usb/android0"; 69 private static final String ACCESSORY_START_MATCH = 70 "DEVPATH=/devices/virtual/misc/usb_accessory"; 71 private static final String FUNCTIONS_PATH = 72 "/sys/class/android_usb/android0/functions"; 73 private static final String STATE_PATH = 74 "/sys/class/android_usb/android0/state"; 75 private static final String MASS_STORAGE_FILE_PATH = 76 "/sys/class/android_usb/android0/f_mass_storage/lun/file"; 77 private static final String RNDIS_ETH_ADDR_PATH = 78 "/sys/class/android_usb/android0/f_rndis/ethaddr"; 79 80 private static final int MSG_UPDATE_STATE = 0; 81 private static final int MSG_ENABLE_ADB = 1; 82 private static final int MSG_SET_CURRENT_FUNCTION = 2; 83 private static final int MSG_SYSTEM_READY = 3; 84 private static final int MSG_BOOT_COMPLETED = 4; 85 86 // Delay for debouncing USB disconnects. 87 // We often get rapid connect/disconnect events when enabling USB functions, 88 // which need debouncing. 89 private static final int UPDATE_DELAY = 1000; 90 91 private UsbHandler mHandler; 92 private boolean mBootCompleted; 93 94 private final Context mContext; 95 private final ContentResolver mContentResolver; 96 private final UsbSettingsManager mSettingsManager; 97 private NotificationManager mNotificationManager; 98 private final boolean mHasUsbAccessory; 99 private boolean mUseUsbNotification; 100 private boolean mAdbEnabled; 101 102 private class AdbSettingsObserver extends ContentObserver { 103 public AdbSettingsObserver() { 104 super(null); 105 } 106 @Override 107 public void onChange(boolean selfChange) { 108 boolean enable = (Settings.Secure.getInt(mContentResolver, 109 Settings.Secure.ADB_ENABLED, 0) > 0); 110 mHandler.sendMessage(MSG_ENABLE_ADB, enable); 111 } 112 } 113 114 /* 115 * Listens for uevent messages from the kernel to monitor the USB state 116 */ 117 private final UEventObserver mUEventObserver = new UEventObserver() { 118 @Override 119 public void onUEvent(UEventObserver.UEvent event) { 120 if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); 121 122 String state = event.get("USB_STATE"); 123 String accessory = event.get("ACCESSORY"); 124 if (state != null) { 125 mHandler.updateState(state); 126 } else if ("START".equals(accessory)) { 127 if (DEBUG) Slog.d(TAG, "got accessory start"); 128 setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); 129 } 130 } 131 }; 132 133 public UsbDeviceManager(Context context, UsbSettingsManager settingsManager) { 134 mContext = context; 135 mContentResolver = context.getContentResolver(); 136 mSettingsManager = settingsManager; 137 PackageManager pm = mContext.getPackageManager(); 138 mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); 139 initRndisAddress(); 140 141 // create a thread for our Handler 142 HandlerThread thread = new HandlerThread("UsbDeviceManager", 143 Process.THREAD_PRIORITY_BACKGROUND); 144 thread.start(); 145 mHandler = new UsbHandler(thread.getLooper()); 146 147 if (nativeIsStartRequested()) { 148 if (DEBUG) Slog.d(TAG, "accessory attached at boot"); 149 setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); 150 } 151 } 152 153 public void systemReady() { 154 if (DEBUG) Slog.d(TAG, "systemReady"); 155 156 mNotificationManager = (NotificationManager) 157 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 158 159 // We do not show the USB notification if the primary volume supports mass storage. 160 // The legacy mass storage UI will be used instead. 161 boolean massStorageSupported = false; 162 StorageManager storageManager = (StorageManager) 163 mContext.getSystemService(Context.STORAGE_SERVICE); 164 StorageVolume[] volumes = storageManager.getVolumeList(); 165 if (volumes.length > 0) { 166 massStorageSupported = volumes[0].allowMassStorage(); 167 } 168 mUseUsbNotification = !massStorageSupported; 169 170 // make sure the ADB_ENABLED setting value matches the current state 171 Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, mAdbEnabled ? 1 : 0); 172 173 mHandler.sendEmptyMessage(MSG_SYSTEM_READY); 174 } 175 176 private static void initRndisAddress() { 177 // configure RNDIS ethernet address based on our serial number using the same algorithm 178 // we had been previously using in kernel board files 179 final int ETH_ALEN = 6; 180 int address[] = new int[ETH_ALEN]; 181 // first byte is 0x02 to signify a locally administered address 182 address[0] = 0x02; 183 184 String serial = SystemProperties.get("ro.serialno", "1234567890ABCDEF"); 185 int serialLength = serial.length(); 186 // XOR the USB serial across the remaining 5 bytes 187 for (int i = 0; i < serialLength; i++) { 188 address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i); 189 } 190 String addrString = String.format("%02X:%02X:%02X:%02X:%02X:%02X", 191 address[0], address[1], address[2], address[3], address[4], address[5]); 192 try { 193 FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString); 194 } catch (IOException e) { 195 Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH); 196 } 197 } 198 199 private static String addFunction(String functions, String function) { 200 if (!containsFunction(functions, function)) { 201 if (functions.length() > 0) { 202 functions += ","; 203 } 204 functions += function; 205 } 206 return functions; 207 } 208 209 private static String removeFunction(String functions, String function) { 210 String[] split = functions.split(","); 211 for (int i = 0; i < split.length; i++) { 212 if (function.equals(split[i])) { 213 split[i] = null; 214 } 215 } 216 StringBuilder builder = new StringBuilder(); 217 for (int i = 0; i < split.length; i++) { 218 String s = split[i]; 219 if (s != null) { 220 if (builder.length() > 0) { 221 builder.append(","); 222 } 223 builder.append(s); 224 } 225 } 226 return builder.toString(); 227 } 228 229 private static boolean containsFunction(String functions, String function) { 230 int index = functions.indexOf(function); 231 if (index < 0) return false; 232 if (index > 0 && functions.charAt(index - 1) != ',') return false; 233 int charAfter = index + function.length(); 234 if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; 235 return true; 236 } 237 238 private final class UsbHandler extends Handler { 239 240 // current USB state 241 private boolean mConnected; 242 private boolean mConfigured; 243 private String mCurrentFunctions; 244 private String mDefaultFunctions; 245 private UsbAccessory mCurrentAccessory; 246 private int mUsbNotificationId; 247 private boolean mAdbNotificationShown; 248 249 private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { 250 public void onReceive(Context context, Intent intent) { 251 if (DEBUG) Slog.d(TAG, "boot completed"); 252 mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); 253 } 254 }; 255 256 public UsbHandler(Looper looper) { 257 super(looper); 258 try { 259 // persist.sys.usb.config should never be unset. But if it is, set it to "adb" 260 // so we have a chance of debugging what happened. 261 mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb"); 262 // sanity check the sys.usb.config system property 263 // this may be necessary if we crashed while switching USB configurations 264 String config = SystemProperties.get("sys.usb.config", "none"); 265 if (!config.equals(mDefaultFunctions)) { 266 Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions); 267 SystemProperties.set("sys.usb.config", mDefaultFunctions); 268 } 269 270 mCurrentFunctions = mDefaultFunctions; 271 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); 272 updateState(state); 273 mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB); 274 275 // Upgrade step for previous versions that used persist.service.adb.enable 276 String value = SystemProperties.get("persist.service.adb.enable", ""); 277 if (value.length() > 0) { 278 char enable = value.charAt(0); 279 if (enable == '1') { 280 setAdbEnabled(true); 281 } else if (enable == '0') { 282 setAdbEnabled(false); 283 } 284 SystemProperties.set("persist.service.adb.enable", ""); 285 } 286 287 // register observer to listen for settings changes 288 mContentResolver.registerContentObserver( 289 Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED), 290 false, new AdbSettingsObserver()); 291 292 // Watch for USB configuration changes 293 mUEventObserver.startObserving(USB_STATE_MATCH); 294 mUEventObserver.startObserving(ACCESSORY_START_MATCH); 295 296 mContext.registerReceiver(mBootCompletedReceiver, 297 new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); 298 } catch (Exception e) { 299 Slog.e(TAG, "Error initializing UsbHandler", e); 300 } 301 } 302 303 public void sendMessage(int what, boolean arg) { 304 removeMessages(what); 305 Message m = Message.obtain(this, what); 306 m.arg1 = (arg ? 1 : 0); 307 sendMessage(m); 308 } 309 310 public void sendMessage(int what, Object arg) { 311 removeMessages(what); 312 Message m = Message.obtain(this, what); 313 m.obj = arg; 314 sendMessage(m); 315 } 316 317 public void sendMessage(int what, Object arg0, boolean arg1) { 318 removeMessages(what); 319 Message m = Message.obtain(this, what); 320 m.obj = arg0; 321 m.arg1 = (arg1 ? 1 : 0); 322 sendMessage(m); 323 } 324 325 public void updateState(String state) { 326 int connected, configured; 327 328 if ("DISCONNECTED".equals(state)) { 329 connected = 0; 330 configured = 0; 331 } else if ("CONNECTED".equals(state)) { 332 connected = 1; 333 configured = 0; 334 } else if ("CONFIGURED".equals(state)) { 335 connected = 1; 336 configured = 1; 337 } else { 338 Slog.e(TAG, "unknown state " + state); 339 return; 340 } 341 removeMessages(MSG_UPDATE_STATE); 342 Message msg = Message.obtain(this, MSG_UPDATE_STATE); 343 msg.arg1 = connected; 344 msg.arg2 = configured; 345 // debounce disconnects to avoid problems bringing up USB tethering 346 sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0); 347 } 348 349 private boolean waitForState(String state) { 350 // wait for the transition to complete. 351 // give up after 1 second. 352 for (int i = 0; i < 20; i++) { 353 // State transition is done when sys.usb.state is set to the new configuration 354 if (state.equals(SystemProperties.get("sys.usb.state"))) return true; 355 try { 356 // try again in 50ms 357 Thread.sleep(50); 358 } catch (InterruptedException e) { 359 } 360 } 361 Slog.e(TAG, "waitForState(" + state + ") FAILED"); 362 return false; 363 } 364 365 private boolean setUsbConfig(String config) { 366 if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); 367 // set the new configuration 368 SystemProperties.set("sys.usb.config", config); 369 return waitForState(config); 370 } 371 372 private void setAdbEnabled(boolean enable) { 373 if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); 374 if (enable != mAdbEnabled) { 375 mAdbEnabled = enable; 376 // Due to the persist.sys.usb.config property trigger, changing adb state requires 377 // switching to default function 378 setEnabledFunctions(mDefaultFunctions, true); 379 updateAdbNotification(); 380 } 381 } 382 383 private void setEnabledFunctions(String functions, boolean makeDefault) { 384 if (functions != null && makeDefault) { 385 if (mAdbEnabled) { 386 functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); 387 } else { 388 functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); 389 } 390 if (!mDefaultFunctions.equals(functions)) { 391 if (!setUsbConfig("none")) { 392 Slog.e(TAG, "Failed to disable USB"); 393 // revert to previous configuration if we fail 394 setUsbConfig(mCurrentFunctions); 395 return; 396 } 397 // setting this property will also change the current USB state 398 // via a property trigger 399 SystemProperties.set("persist.sys.usb.config", functions); 400 if (waitForState(functions)) { 401 mCurrentFunctions = functions; 402 mDefaultFunctions = functions; 403 } else { 404 Slog.e(TAG, "Failed to switch persistent USB config to " + functions); 405 // revert to previous configuration if we fail 406 SystemProperties.set("persist.sys.usb.config", mDefaultFunctions); 407 } 408 } 409 } else { 410 if (functions == null) { 411 functions = mDefaultFunctions; 412 } 413 if (mAdbEnabled) { 414 functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); 415 } else { 416 functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); 417 } 418 if (!mCurrentFunctions.equals(functions)) { 419 if (!setUsbConfig("none")) { 420 Slog.e(TAG, "Failed to disable USB"); 421 // revert to previous configuration if we fail 422 setUsbConfig(mCurrentFunctions); 423 return; 424 } 425 if (setUsbConfig(functions)) { 426 mCurrentFunctions = functions; 427 } else { 428 Slog.e(TAG, "Failed to switch USB config to " + functions); 429 // revert to previous configuration if we fail 430 setUsbConfig(mCurrentFunctions); 431 } 432 } 433 } 434 } 435 436 private void updateCurrentAccessory() { 437 if (!mHasUsbAccessory) return; 438 439 if (mConfigured) { 440 String[] strings = nativeGetAccessoryStrings(); 441 if (strings != null) { 442 mCurrentAccessory = new UsbAccessory(strings); 443 Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); 444 // defer accessoryAttached if system is not ready 445 if (mBootCompleted) { 446 mSettingsManager.accessoryAttached(mCurrentAccessory); 447 } // else handle in mBootCompletedReceiver 448 } else { 449 Slog.e(TAG, "nativeGetAccessoryStrings failed"); 450 } 451 } else if (!mConnected) { 452 // make sure accessory mode is off 453 // and restore default functions 454 Slog.d(TAG, "exited USB accessory mode"); 455 setEnabledFunctions(mDefaultFunctions, false); 456 457 if (mCurrentAccessory != null) { 458 if (mBootCompleted) { 459 mSettingsManager.accessoryDetached(mCurrentAccessory); 460 } 461 mCurrentAccessory = null; 462 } 463 } 464 } 465 466 private void updateUsbState() { 467 // send a sticky broadcast containing current USB state 468 Intent intent = new Intent(UsbManager.ACTION_USB_STATE); 469 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 470 intent.putExtra(UsbManager.USB_CONNECTED, mConnected); 471 intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); 472 473 if (mCurrentFunctions != null) { 474 String[] functions = mCurrentFunctions.split(","); 475 for (int i = 0; i < functions.length; i++) { 476 intent.putExtra(functions[i], true); 477 } 478 } 479 480 mContext.sendStickyBroadcast(intent); 481 } 482 483 @Override 484 public void handleMessage(Message msg) { 485 switch (msg.what) { 486 case MSG_UPDATE_STATE: 487 mConnected = (msg.arg1 == 1); 488 mConfigured = (msg.arg2 == 1); 489 updateUsbNotification(); 490 updateAdbNotification(); 491 if (containsFunction(mCurrentFunctions, 492 UsbManager.USB_FUNCTION_ACCESSORY)) { 493 updateCurrentAccessory(); 494 } 495 496 if (!mConnected) { 497 // restore defaults when USB is disconnected 498 setEnabledFunctions(mDefaultFunctions, false); 499 } 500 if (mBootCompleted) { 501 updateUsbState(); 502 } 503 break; 504 case MSG_ENABLE_ADB: 505 setAdbEnabled(msg.arg1 == 1); 506 break; 507 case MSG_SET_CURRENT_FUNCTION: 508 String function = (String)msg.obj; 509 boolean makeDefault = (msg.arg1 == 1); 510 setEnabledFunctions(function, makeDefault); 511 break; 512 case MSG_SYSTEM_READY: 513 updateUsbNotification(); 514 updateAdbNotification(); 515 updateUsbState(); 516 break; 517 case MSG_BOOT_COMPLETED: 518 mBootCompleted = true; 519 if (mCurrentAccessory != null) { 520 mSettingsManager.accessoryAttached(mCurrentAccessory); 521 } 522 break; 523 } 524 } 525 526 public UsbAccessory getCurrentAccessory() { 527 return mCurrentAccessory; 528 } 529 530 private void updateUsbNotification() { 531 if (mNotificationManager == null || !mUseUsbNotification) return; 532 int id = 0; 533 Resources r = mContext.getResources(); 534 if (mConnected) { 535 if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { 536 id = com.android.internal.R.string.usb_mtp_notification_title; 537 } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { 538 id = com.android.internal.R.string.usb_ptp_notification_title; 539 } else if (containsFunction(mCurrentFunctions, 540 UsbManager.USB_FUNCTION_MASS_STORAGE)) { 541 id = com.android.internal.R.string.usb_cd_installer_notification_title; 542 } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { 543 id = com.android.internal.R.string.usb_accessory_notification_title; 544 } else { 545 // There is a different notification for USB tethering so we don't need one here 546 if (!containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_RNDIS)) { 547 Slog.e(TAG, "No known USB function in updateUsbNotification"); 548 } 549 } 550 } 551 if (id != mUsbNotificationId) { 552 // clear notification if title needs changing 553 if (mUsbNotificationId != 0) { 554 mNotificationManager.cancel(mUsbNotificationId); 555 mUsbNotificationId = 0; 556 } 557 if (id != 0) { 558 CharSequence message = r.getText( 559 com.android.internal.R.string.usb_notification_message); 560 CharSequence title = r.getText(id); 561 562 Notification notification = new Notification(); 563 notification.icon = com.android.internal.R.drawable.stat_sys_data_usb; 564 notification.when = 0; 565 notification.flags = Notification.FLAG_ONGOING_EVENT; 566 notification.tickerText = title; 567 notification.defaults = 0; // please be quiet 568 notification.sound = null; 569 notification.vibrate = null; 570 571 Intent intent = Intent.makeRestartActivityTask( 572 new ComponentName("com.android.settings", 573 "com.android.settings.UsbSettings")); 574 PendingIntent pi = PendingIntent.getActivity(mContext, 0, 575 intent, 0); 576 notification.setLatestEventInfo(mContext, title, message, pi); 577 mNotificationManager.notify(id, notification); 578 mUsbNotificationId = id; 579 } 580 } 581 } 582 583 private void updateAdbNotification() { 584 if (mNotificationManager == null) return; 585 final int id = com.android.internal.R.string.adb_active_notification_title; 586 if (mAdbEnabled && mConnected) { 587 if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; 588 589 if (!mAdbNotificationShown) { 590 Resources r = mContext.getResources(); 591 CharSequence title = r.getText(id); 592 CharSequence message = r.getText( 593 com.android.internal.R.string.adb_active_notification_message); 594 595 Notification notification = new Notification(); 596 notification.icon = com.android.internal.R.drawable.stat_sys_adb; 597 notification.when = 0; 598 notification.flags = Notification.FLAG_ONGOING_EVENT; 599 notification.tickerText = title; 600 notification.defaults = 0; // please be quiet 601 notification.sound = null; 602 notification.vibrate = null; 603 604 Intent intent = Intent.makeRestartActivityTask( 605 new ComponentName("com.android.settings", 606 "com.android.settings.DevelopmentSettings")); 607 PendingIntent pi = PendingIntent.getActivity(mContext, 0, 608 intent, 0); 609 notification.setLatestEventInfo(mContext, title, message, pi); 610 mAdbNotificationShown = true; 611 mNotificationManager.notify(id, notification); 612 } 613 } else if (mAdbNotificationShown) { 614 mAdbNotificationShown = false; 615 mNotificationManager.cancel(id); 616 } 617 } 618 619 public void dump(FileDescriptor fd, PrintWriter pw) { 620 pw.println(" USB Device State:"); 621 pw.println(" Current Functions: " + mCurrentFunctions); 622 pw.println(" Default Functions: " + mDefaultFunctions); 623 pw.println(" mConnected: " + mConnected); 624 pw.println(" mConfigured: " + mConfigured); 625 pw.println(" mCurrentAccessory: " + mCurrentAccessory); 626 try { 627 pw.println(" Kernel state: " 628 + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim()); 629 pw.println(" Kernel function list: " 630 + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim()); 631 pw.println(" Mass storage backing file: " 632 + FileUtils.readTextFile(new File(MASS_STORAGE_FILE_PATH), 0, null).trim()); 633 } catch (IOException e) { 634 pw.println("IOException: " + e); 635 } 636 } 637 } 638 639 /* returns the currently attached USB accessory */ 640 public UsbAccessory getCurrentAccessory() { 641 return mHandler.getCurrentAccessory(); 642 } 643 644 /* opens the currently attached USB accessory */ 645 public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { 646 UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); 647 if (currentAccessory == null) { 648 throw new IllegalArgumentException("no accessory attached"); 649 } 650 if (!currentAccessory.equals(accessory)) { 651 String error = accessory.toString() 652 + " does not match current accessory " 653 + currentAccessory; 654 throw new IllegalArgumentException(error); 655 } 656 mSettingsManager.checkPermission(accessory); 657 return nativeOpenAccessory(); 658 } 659 660 public void setCurrentFunction(String function, boolean makeDefault) { 661 if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault); 662 mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault); 663 } 664 665 public void setMassStorageBackingFile(String path) { 666 if (path == null) path = ""; 667 try { 668 FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path); 669 } catch (IOException e) { 670 Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH); 671 } 672 } 673 674 public void dump(FileDescriptor fd, PrintWriter pw) { 675 if (mHandler != null) { 676 mHandler.dump(fd, pw); 677 } 678 } 679 680 private native String[] nativeGetAccessoryStrings(); 681 private native ParcelFileDescriptor nativeOpenAccessory(); 682 private native boolean nativeIsStartRequested(); 683 } 684