1 /* 2 * Copyright (C) 2010 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 18 package android.hardware.usb; 19 20 import android.annotation.Nullable; 21 import android.annotation.SdkConstant; 22 import android.annotation.SystemService; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.app.PendingIntent; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.os.Bundle; 29 import android.os.ParcelFileDescriptor; 30 import android.os.Process; 31 import android.os.RemoteException; 32 import android.util.Log; 33 34 import com.android.internal.util.Preconditions; 35 36 import java.util.HashMap; 37 38 /** 39 * This class allows you to access the state of USB and communicate with USB devices. 40 * Currently only host mode is supported in the public API. 41 * 42 * <div class="special reference"> 43 * <h3>Developer Guides</h3> 44 * <p>For more information about communicating with USB hardware, read the 45 * <a href="{@docRoot}guide/topics/connectivity/usb/index.html">USB developer guide</a>.</p> 46 * </div> 47 */ 48 @SystemService(Context.USB_SERVICE) 49 public class UsbManager { 50 private static final String TAG = "UsbManager"; 51 52 /** 53 * Broadcast Action: A sticky broadcast for USB state change events when in device mode. 54 * 55 * This is a sticky broadcast for clients that includes USB connected/disconnected state, 56 * <ul> 57 * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected. 58 * <li> {@link #USB_HOST_CONNECTED} boolean indicating whether USB is connected or 59 * disconnected as host. 60 * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured. 61 * currently zero if not configured, one for configured. 62 * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the 63 * adb function is enabled 64 * <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the 65 * RNDIS ethernet function is enabled 66 * <li> {@link #USB_FUNCTION_MTP} boolean extra indicating whether the 67 * MTP function is enabled 68 * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the 69 * PTP function is enabled 70 * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the 71 * accessory function is enabled 72 * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the 73 * audio source function is enabled 74 * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the 75 * MIDI function is enabled 76 * </ul> 77 * If the sticky intent has not been found, that indicates USB is disconnected, 78 * USB is not configued, MTP function is enabled, and all the other functions are disabled. 79 * 80 * {@hide} 81 */ 82 public static final String ACTION_USB_STATE = 83 "android.hardware.usb.action.USB_STATE"; 84 85 /** 86 * Broadcast Action: A broadcast for USB port changes. 87 * 88 * This intent is sent when a USB port is added, removed, or changes state. 89 * <ul> 90 * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort} 91 * for the port. 92 * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus} 93 * for the port, or null if the port has been removed 94 * </ul> 95 * 96 * @hide 97 */ 98 public static final String ACTION_USB_PORT_CHANGED = 99 "android.hardware.usb.action.USB_PORT_CHANGED"; 100 101 /** 102 * Broadcast Action: A broadcast for USB device attached event. 103 * 104 * This intent is sent when a USB device is attached to the USB bus when in host mode. 105 * <ul> 106 * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice} 107 * for the attached device 108 * </ul> 109 */ 110 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 111 public static final String ACTION_USB_DEVICE_ATTACHED = 112 "android.hardware.usb.action.USB_DEVICE_ATTACHED"; 113 114 /** 115 * Broadcast Action: A broadcast for USB device detached event. 116 * 117 * This intent is sent when a USB device is detached from the USB bus when in host mode. 118 * <ul> 119 * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice} 120 * for the detached device 121 * </ul> 122 */ 123 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 124 public static final String ACTION_USB_DEVICE_DETACHED = 125 "android.hardware.usb.action.USB_DEVICE_DETACHED"; 126 127 /** 128 * Broadcast Action: A broadcast for USB accessory attached event. 129 * 130 * This intent is sent when a USB accessory is attached. 131 * <ul> 132 * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory} 133 * for the attached accessory 134 * </ul> 135 */ 136 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 137 public static final String ACTION_USB_ACCESSORY_ATTACHED = 138 "android.hardware.usb.action.USB_ACCESSORY_ATTACHED"; 139 140 /** 141 * Broadcast Action: A broadcast for USB accessory detached event. 142 * 143 * This intent is sent when a USB accessory is detached. 144 * <ul> 145 * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory} 146 * for the attached accessory that was detached 147 * </ul> 148 */ 149 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 150 public static final String ACTION_USB_ACCESSORY_DETACHED = 151 "android.hardware.usb.action.USB_ACCESSORY_DETACHED"; 152 153 /** 154 * Boolean extra indicating whether USB is connected or disconnected. 155 * Used in extras for the {@link #ACTION_USB_STATE} broadcast. 156 * 157 * {@hide} 158 */ 159 public static final String USB_CONNECTED = "connected"; 160 161 /** 162 * Boolean extra indicating whether USB is connected or disconnected as host. 163 * Used in extras for the {@link #ACTION_USB_STATE} broadcast. 164 * 165 * {@hide} 166 */ 167 public static final String USB_HOST_CONNECTED = "host_connected"; 168 169 /** 170 * Boolean extra indicating whether USB is configured. 171 * Used in extras for the {@link #ACTION_USB_STATE} broadcast. 172 * 173 * {@hide} 174 */ 175 public static final String USB_CONFIGURED = "configured"; 176 177 /** 178 * Boolean extra indicating whether confidential user data, such as photos, should be 179 * made available on the USB connection. This variable will only be set when the user 180 * has explicitly asked for this data to be unlocked. 181 * Used in extras for the {@link #ACTION_USB_STATE} broadcast. 182 * 183 * {@hide} 184 */ 185 public static final String USB_DATA_UNLOCKED = "unlocked"; 186 187 /** 188 * Boolean extra indicating whether the intent represents a change in the usb 189 * configuration (as opposed to a state update). 190 * 191 * {@hide} 192 */ 193 public static final String USB_CONFIG_CHANGED = "config_changed"; 194 195 /** 196 * A placeholder indicating that no USB function is being specified. 197 * Used to distinguish between selecting no function vs. the default function in 198 * {@link #setCurrentFunction(String)}. 199 * 200 * {@hide} 201 */ 202 public static final String USB_FUNCTION_NONE = "none"; 203 204 /** 205 * Name of the adb USB function. 206 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 207 * 208 * {@hide} 209 */ 210 public static final String USB_FUNCTION_ADB = "adb"; 211 212 /** 213 * Name of the RNDIS ethernet USB function. 214 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 215 * 216 * {@hide} 217 */ 218 public static final String USB_FUNCTION_RNDIS = "rndis"; 219 220 /** 221 * Name of the MTP USB function. 222 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 223 * 224 * {@hide} 225 */ 226 public static final String USB_FUNCTION_MTP = "mtp"; 227 228 /** 229 * Name of the PTP USB function. 230 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 231 * 232 * {@hide} 233 */ 234 public static final String USB_FUNCTION_PTP = "ptp"; 235 236 /** 237 * Name of the audio source USB function. 238 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 239 * 240 * {@hide} 241 */ 242 public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source"; 243 244 /** 245 * Name of the MIDI USB function. 246 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 247 * 248 * {@hide} 249 */ 250 public static final String USB_FUNCTION_MIDI = "midi"; 251 252 /** 253 * Name of the Accessory USB function. 254 * Used in extras for the {@link #ACTION_USB_STATE} broadcast 255 * 256 * {@hide} 257 */ 258 public static final String USB_FUNCTION_ACCESSORY = "accessory"; 259 260 /** 261 * Name of extra for {@link #ACTION_USB_PORT_CHANGED} 262 * containing the {@link UsbPort} object for the port. 263 * 264 * @hide 265 */ 266 public static final String EXTRA_PORT = "port"; 267 268 /** 269 * Name of extra for {@link #ACTION_USB_PORT_CHANGED} 270 * containing the {@link UsbPortStatus} object for the port, or null if the port 271 * was removed. 272 * 273 * @hide 274 */ 275 public static final String EXTRA_PORT_STATUS = "portStatus"; 276 277 /** 278 * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and 279 * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts 280 * containing the {@link UsbDevice} object for the device. 281 */ 282 public static final String EXTRA_DEVICE = "device"; 283 284 /** 285 * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and 286 * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts 287 * containing the {@link UsbAccessory} object for the accessory. 288 */ 289 public static final String EXTRA_ACCESSORY = "accessory"; 290 291 /** 292 * Name of extra added to the {@link android.app.PendingIntent} 293 * passed into {@link #requestPermission(UsbDevice, PendingIntent)} 294 * or {@link #requestPermission(UsbAccessory, PendingIntent)} 295 * containing a boolean value indicating whether the user granted permission or not. 296 */ 297 public static final String EXTRA_PERMISSION_GRANTED = "permission"; 298 299 private final Context mContext; 300 private final IUsbManager mService; 301 302 /** 303 * {@hide} 304 */ UsbManager(Context context, IUsbManager service)305 public UsbManager(Context context, IUsbManager service) { 306 mContext = context; 307 mService = service; 308 } 309 310 /** 311 * Returns a HashMap containing all USB devices currently attached. 312 * USB device name is the key for the returned HashMap. 313 * The result will be empty if no devices are attached, or if 314 * USB host mode is inactive or unsupported. 315 * 316 * @return HashMap containing all connected USB devices. 317 */ getDeviceList()318 public HashMap<String,UsbDevice> getDeviceList() { 319 HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>(); 320 if (mService == null) { 321 return result; 322 } 323 Bundle bundle = new Bundle(); 324 try { 325 mService.getDeviceList(bundle); 326 for (String name : bundle.keySet()) { 327 result.put(name, (UsbDevice)bundle.get(name)); 328 } 329 return result; 330 } catch (RemoteException e) { 331 throw e.rethrowFromSystemServer(); 332 } 333 } 334 335 /** 336 * Opens the device so it can be used to send and receive 337 * data using {@link android.hardware.usb.UsbRequest}. 338 * 339 * @param device the device to open 340 * @return a {@link UsbDeviceConnection}, or {@code null} if open failed 341 */ openDevice(UsbDevice device)342 public UsbDeviceConnection openDevice(UsbDevice device) { 343 try { 344 String deviceName = device.getDeviceName(); 345 ParcelFileDescriptor pfd = mService.openDevice(deviceName); 346 if (pfd != null) { 347 UsbDeviceConnection connection = new UsbDeviceConnection(device); 348 boolean result = connection.open(deviceName, pfd, mContext); 349 pfd.close(); 350 if (result) { 351 return connection; 352 } 353 } 354 } catch (Exception e) { 355 Log.e(TAG, "exception in UsbManager.openDevice", e); 356 } 357 return null; 358 } 359 360 /** 361 * Returns a list of currently attached USB accessories. 362 * (in the current implementation there can be at most one) 363 * 364 * @return list of USB accessories, or null if none are attached. 365 */ getAccessoryList()366 public UsbAccessory[] getAccessoryList() { 367 if (mService == null) { 368 return null; 369 } 370 try { 371 UsbAccessory accessory = mService.getCurrentAccessory(); 372 if (accessory == null) { 373 return null; 374 } else { 375 return new UsbAccessory[] { accessory }; 376 } 377 } catch (RemoteException e) { 378 throw e.rethrowFromSystemServer(); 379 } 380 } 381 382 /** 383 * Opens a file descriptor for reading and writing data to the USB accessory. 384 * 385 * @param accessory the USB accessory to open 386 * @return file descriptor, or null if the accessor could not be opened. 387 */ openAccessory(UsbAccessory accessory)388 public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { 389 try { 390 return mService.openAccessory(accessory); 391 } catch (RemoteException e) { 392 throw e.rethrowFromSystemServer(); 393 } 394 } 395 396 /** 397 * Returns true if the caller has permission to access the device. 398 * Permission might have been granted temporarily via 399 * {@link #requestPermission(UsbDevice, PendingIntent)} or 400 * by the user choosing the caller as the default application for the device. 401 * 402 * @param device to check permissions for 403 * @return true if caller has permission 404 */ hasPermission(UsbDevice device)405 public boolean hasPermission(UsbDevice device) { 406 if (mService == null) { 407 return false; 408 } 409 try { 410 return mService.hasDevicePermission(device); 411 } catch (RemoteException e) { 412 throw e.rethrowFromSystemServer(); 413 } 414 } 415 416 /** 417 * Returns true if the caller has permission to access the accessory. 418 * Permission might have been granted temporarily via 419 * {@link #requestPermission(UsbAccessory, PendingIntent)} or 420 * by the user choosing the caller as the default application for the accessory. 421 * 422 * @param accessory to check permissions for 423 * @return true if caller has permission 424 */ hasPermission(UsbAccessory accessory)425 public boolean hasPermission(UsbAccessory accessory) { 426 if (mService == null) { 427 return false; 428 } 429 try { 430 return mService.hasAccessoryPermission(accessory); 431 } catch (RemoteException e) { 432 throw e.rethrowFromSystemServer(); 433 } 434 } 435 436 /** 437 * Requests temporary permission for the given package to access the device. 438 * This may result in a system dialog being displayed to the user 439 * if permission had not already been granted. 440 * Success or failure is returned via the {@link android.app.PendingIntent} pi. 441 * If successful, this grants the caller permission to access the device only 442 * until the device is disconnected. 443 * 444 * The following extras will be added to pi: 445 * <ul> 446 * <li> {@link #EXTRA_DEVICE} containing the device passed into this call 447 * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether 448 * permission was granted by the user 449 * </ul> 450 * 451 * @param device to request permissions for 452 * @param pi PendingIntent for returning result 453 */ requestPermission(UsbDevice device, PendingIntent pi)454 public void requestPermission(UsbDevice device, PendingIntent pi) { 455 try { 456 mService.requestDevicePermission(device, mContext.getPackageName(), pi); 457 } catch (RemoteException e) { 458 throw e.rethrowFromSystemServer(); 459 } 460 } 461 462 /** 463 * Requests temporary permission for the given package to access the accessory. 464 * This may result in a system dialog being displayed to the user 465 * if permission had not already been granted. 466 * Success or failure is returned via the {@link android.app.PendingIntent} pi. 467 * If successful, this grants the caller permission to access the accessory only 468 * until the device is disconnected. 469 * 470 * The following extras will be added to pi: 471 * <ul> 472 * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call 473 * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether 474 * permission was granted by the user 475 * </ul> 476 * 477 * @param accessory to request permissions for 478 * @param pi PendingIntent for returning result 479 */ requestPermission(UsbAccessory accessory, PendingIntent pi)480 public void requestPermission(UsbAccessory accessory, PendingIntent pi) { 481 try { 482 mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi); 483 } catch (RemoteException e) { 484 throw e.rethrowFromSystemServer(); 485 } 486 } 487 488 /** 489 * Grants permission for USB device without showing system dialog. 490 * Only system components can call this function. 491 * @param device to request permissions for 492 * 493 * {@hide} 494 */ grantPermission(UsbDevice device)495 public void grantPermission(UsbDevice device) { 496 grantPermission(device, Process.myUid()); 497 } 498 499 /** 500 * Grants permission for USB device to given uid without showing system dialog. 501 * Only system components can call this function. 502 * @param device to request permissions for 503 * @uid uid to give permission 504 * 505 * {@hide} 506 */ grantPermission(UsbDevice device, int uid)507 public void grantPermission(UsbDevice device, int uid) { 508 try { 509 mService.grantDevicePermission(device, uid); 510 } catch (RemoteException e) { 511 throw e.rethrowFromSystemServer(); 512 } 513 } 514 515 /** 516 * Grants permission to specified package for USB device without showing system dialog. 517 * Only system components can call this function, as it requires the MANAGE_USB permission. 518 * @param device to request permissions for 519 * @param packageName of package to grant permissions 520 * 521 * {@hide} 522 */ grantPermission(UsbDevice device, String packageName)523 public void grantPermission(UsbDevice device, String packageName) { 524 try { 525 int uid = mContext.getPackageManager() 526 .getPackageUidAsUser(packageName, mContext.getUserId()); 527 grantPermission(device, uid); 528 } catch (NameNotFoundException e) { 529 Log.e(TAG, "Package " + packageName + " not found.", e); 530 } 531 } 532 533 /** 534 * Returns true if the specified USB function is currently enabled when in device mode. 535 * <p> 536 * USB functions represent interfaces which are published to the host to access 537 * services offered by the device. 538 * </p> 539 * 540 * @param function name of the USB function 541 * @return true if the USB function is enabled 542 * 543 * {@hide} 544 */ isFunctionEnabled(String function)545 public boolean isFunctionEnabled(String function) { 546 if (mService == null) { 547 return false; 548 } 549 try { 550 return mService.isFunctionEnabled(function); 551 } catch (RemoteException e) { 552 throw e.rethrowFromSystemServer(); 553 } 554 } 555 556 /** 557 * Sets the current USB function when in device mode. 558 * <p> 559 * USB functions represent interfaces which are published to the host to access 560 * services offered by the device. 561 * </p><p> 562 * This method is intended to select among primary USB functions. The system may 563 * automatically activate additional functions such as {@link #USB_FUNCTION_ADB} 564 * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states. 565 * </p><p> 566 * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE}, 567 * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, 568 * or {@link #USB_FUNCTION_RNDIS}. 569 * </p><p> 570 * Also sets whether USB data (for example, MTP exposed pictures) should be made available 571 * on the USB connection when in device mode. Unlocking usb data should only be done with 572 * user involvement, since exposing pictures or other data could leak sensitive 573 * user information. 574 * </p><p> 575 * Note: This function is asynchronous and may fail silently without applying 576 * the requested changes. 577 * </p> 578 * 579 * @param function name of the USB function, or null to restore the default function 580 * @param usbDataUnlocked whether user data is accessible 581 * 582 * {@hide} 583 */ setCurrentFunction(String function, boolean usbDataUnlocked)584 public void setCurrentFunction(String function, boolean usbDataUnlocked) { 585 try { 586 mService.setCurrentFunction(function, usbDataUnlocked); 587 } catch (RemoteException e) { 588 throw e.rethrowFromSystemServer(); 589 } 590 } 591 592 /** 593 * Returns a list of physical USB ports on the device. 594 * <p> 595 * This list is guaranteed to contain all dual-role USB Type C ports but it might 596 * be missing other ports depending on whether the kernel USB drivers have been 597 * updated to publish all of the device's ports through the new "dual_role_usb" 598 * device class (which supports all types of ports despite its name). 599 * </p> 600 * 601 * @return The list of USB ports, or null if none. 602 * 603 * @hide 604 */ getPorts()605 public UsbPort[] getPorts() { 606 if (mService == null) { 607 return null; 608 } 609 try { 610 return mService.getPorts(); 611 } catch (RemoteException e) { 612 throw e.rethrowFromSystemServer(); 613 } 614 } 615 616 /** 617 * Gets the status of the specified USB port. 618 * 619 * @param port The port to query. 620 * @return The status of the specified USB port, or null if unknown. 621 * 622 * @hide 623 */ getPortStatus(UsbPort port)624 public UsbPortStatus getPortStatus(UsbPort port) { 625 Preconditions.checkNotNull(port, "port must not be null"); 626 627 try { 628 return mService.getPortStatus(port.getId()); 629 } catch (RemoteException e) { 630 throw e.rethrowFromSystemServer(); 631 } 632 } 633 634 /** 635 * Sets the desired role combination of the port. 636 * <p> 637 * The supported role combinations depend on what is connected to the port and may be 638 * determined by consulting 639 * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}. 640 * </p><p> 641 * Note: This function is asynchronous and may fail silently without applying 642 * the requested changes. If this function does cause a status change to occur then 643 * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent. 644 * </p> 645 * 646 * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE} 647 * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role. 648 * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST} 649 * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role. 650 * 651 * @hide 652 */ setPortRoles(UsbPort port, int powerRole, int dataRole)653 public void setPortRoles(UsbPort port, int powerRole, int dataRole) { 654 Preconditions.checkNotNull(port, "port must not be null"); 655 UsbPort.checkRoles(powerRole, dataRole); 656 657 Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName()); 658 try { 659 mService.setPortRoles(port.getId(), powerRole, dataRole); 660 } catch (RemoteException e) { 661 throw e.rethrowFromSystemServer(); 662 } 663 } 664 665 /** 666 * Sets the component that will handle USB device connection. 667 * <p> 668 * Setting component allows to specify external USB host manager to handle use cases, where 669 * selection dialog for an activity that will handle USB device is undesirable. 670 * Only system components can call this function, as it requires the MANAGE_USB permission. 671 * 672 * @param usbDeviceConnectionHandler The component to handle usb connections, 673 * {@code null} to unset. 674 * 675 * {@hide} 676 */ setUsbDeviceConnectionHandler(@ullable ComponentName usbDeviceConnectionHandler)677 public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) { 678 try { 679 mService.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler); 680 } catch (RemoteException e) { 681 throw e.rethrowFromSystemServer(); 682 } 683 } 684 685 /** @hide */ addFunction(String functions, String function)686 public static String addFunction(String functions, String function) { 687 if (USB_FUNCTION_NONE.equals(functions)) { 688 return function; 689 } 690 if (!containsFunction(functions, function)) { 691 if (functions.length() > 0) { 692 functions += ","; 693 } 694 functions += function; 695 } 696 return functions; 697 } 698 699 /** @hide */ removeFunction(String functions, String function)700 public static String removeFunction(String functions, String function) { 701 String[] split = functions.split(","); 702 for (int i = 0; i < split.length; i++) { 703 if (function.equals(split[i])) { 704 split[i] = null; 705 } 706 } 707 if (split.length == 1 && split[0] == null) { 708 return USB_FUNCTION_NONE; 709 } 710 StringBuilder builder = new StringBuilder(); 711 for (int i = 0; i < split.length; i++) { 712 String s = split[i]; 713 if (s != null) { 714 if (builder.length() > 0) { 715 builder.append(","); 716 } 717 builder.append(s); 718 } 719 } 720 return builder.toString(); 721 } 722 723 /** @hide */ containsFunction(String functions, String function)724 public static boolean containsFunction(String functions, String function) { 725 int index = functions.indexOf(function); 726 if (index < 0) return false; 727 if (index > 0 && functions.charAt(index - 1) != ',') return false; 728 int charAfter = index + function.length(); 729 if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; 730 return true; 731 } 732 } 733