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 Bundle bundle = new Bundle(); 320 try { 321 mService.getDeviceList(bundle); 322 HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>(); 323 for (String name : bundle.keySet()) { 324 result.put(name, (UsbDevice)bundle.get(name)); 325 } 326 return result; 327 } catch (RemoteException e) { 328 throw e.rethrowFromSystemServer(); 329 } 330 } 331 332 /** 333 * Opens the device so it can be used to send and receive 334 * data using {@link android.hardware.usb.UsbRequest}. 335 * 336 * @param device the device to open 337 * @return a {@link UsbDeviceConnection}, or {@code null} if open failed 338 */ openDevice(UsbDevice device)339 public UsbDeviceConnection openDevice(UsbDevice device) { 340 try { 341 String deviceName = device.getDeviceName(); 342 ParcelFileDescriptor pfd = mService.openDevice(deviceName); 343 if (pfd != null) { 344 UsbDeviceConnection connection = new UsbDeviceConnection(device); 345 boolean result = connection.open(deviceName, pfd, mContext); 346 pfd.close(); 347 if (result) { 348 return connection; 349 } 350 } 351 } catch (Exception e) { 352 Log.e(TAG, "exception in UsbManager.openDevice", e); 353 } 354 return null; 355 } 356 357 /** 358 * Returns a list of currently attached USB accessories. 359 * (in the current implementation there can be at most one) 360 * 361 * @return list of USB accessories, or null if none are attached. 362 */ getAccessoryList()363 public UsbAccessory[] getAccessoryList() { 364 try { 365 UsbAccessory accessory = mService.getCurrentAccessory(); 366 if (accessory == null) { 367 return null; 368 } else { 369 return new UsbAccessory[] { accessory }; 370 } 371 } catch (RemoteException e) { 372 throw e.rethrowFromSystemServer(); 373 } 374 } 375 376 /** 377 * Opens a file descriptor for reading and writing data to the USB accessory. 378 * 379 * @param accessory the USB accessory to open 380 * @return file descriptor, or null if the accessor could not be opened. 381 */ openAccessory(UsbAccessory accessory)382 public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { 383 try { 384 return mService.openAccessory(accessory); 385 } catch (RemoteException e) { 386 throw e.rethrowFromSystemServer(); 387 } 388 } 389 390 /** 391 * Returns true if the caller has permission to access the device. 392 * Permission might have been granted temporarily via 393 * {@link #requestPermission(UsbDevice, PendingIntent)} or 394 * by the user choosing the caller as the default application for the device. 395 * 396 * @param device to check permissions for 397 * @return true if caller has permission 398 */ hasPermission(UsbDevice device)399 public boolean hasPermission(UsbDevice device) { 400 try { 401 return mService.hasDevicePermission(device); 402 } catch (RemoteException e) { 403 throw e.rethrowFromSystemServer(); 404 } 405 } 406 407 /** 408 * Returns true if the caller has permission to access the accessory. 409 * Permission might have been granted temporarily via 410 * {@link #requestPermission(UsbAccessory, PendingIntent)} or 411 * by the user choosing the caller as the default application for the accessory. 412 * 413 * @param accessory to check permissions for 414 * @return true if caller has permission 415 */ hasPermission(UsbAccessory accessory)416 public boolean hasPermission(UsbAccessory accessory) { 417 try { 418 return mService.hasAccessoryPermission(accessory); 419 } catch (RemoteException e) { 420 throw e.rethrowFromSystemServer(); 421 } 422 } 423 424 /** 425 * Requests temporary permission for the given package to access the device. 426 * This may result in a system dialog being displayed to the user 427 * if permission had not already been granted. 428 * Success or failure is returned via the {@link android.app.PendingIntent} pi. 429 * If successful, this grants the caller permission to access the device only 430 * until the device is disconnected. 431 * 432 * The following extras will be added to pi: 433 * <ul> 434 * <li> {@link #EXTRA_DEVICE} containing the device passed into this call 435 * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether 436 * permission was granted by the user 437 * </ul> 438 * 439 * @param device to request permissions for 440 * @param pi PendingIntent for returning result 441 */ requestPermission(UsbDevice device, PendingIntent pi)442 public void requestPermission(UsbDevice device, PendingIntent pi) { 443 try { 444 mService.requestDevicePermission(device, mContext.getPackageName(), pi); 445 } catch (RemoteException e) { 446 throw e.rethrowFromSystemServer(); 447 } 448 } 449 450 /** 451 * Requests temporary permission for the given package to access the accessory. 452 * This may result in a system dialog being displayed to the user 453 * if permission had not already been granted. 454 * Success or failure is returned via the {@link android.app.PendingIntent} pi. 455 * If successful, this grants the caller permission to access the accessory only 456 * until the device is disconnected. 457 * 458 * The following extras will be added to pi: 459 * <ul> 460 * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call 461 * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether 462 * permission was granted by the user 463 * </ul> 464 * 465 * @param accessory to request permissions for 466 * @param pi PendingIntent for returning result 467 */ requestPermission(UsbAccessory accessory, PendingIntent pi)468 public void requestPermission(UsbAccessory accessory, PendingIntent pi) { 469 try { 470 mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi); 471 } catch (RemoteException e) { 472 throw e.rethrowFromSystemServer(); 473 } 474 } 475 476 /** 477 * Grants permission for USB device without showing system dialog. 478 * Only system components can call this function. 479 * @param device to request permissions for 480 * 481 * {@hide} 482 */ grantPermission(UsbDevice device)483 public void grantPermission(UsbDevice device) { 484 grantPermission(device, Process.myUid()); 485 } 486 487 /** 488 * Grants permission for USB device to given uid without showing system dialog. 489 * Only system components can call this function. 490 * @param device to request permissions for 491 * @uid uid to give permission 492 * 493 * {@hide} 494 */ grantPermission(UsbDevice device, int uid)495 public void grantPermission(UsbDevice device, int uid) { 496 try { 497 mService.grantDevicePermission(device, uid); 498 } catch (RemoteException e) { 499 throw e.rethrowFromSystemServer(); 500 } 501 } 502 503 /** 504 * Grants permission to specified package for USB device without showing system dialog. 505 * Only system components can call this function, as it requires the MANAGE_USB permission. 506 * @param device to request permissions for 507 * @param packageName of package to grant permissions 508 * 509 * {@hide} 510 */ grantPermission(UsbDevice device, String packageName)511 public void grantPermission(UsbDevice device, String packageName) { 512 try { 513 int uid = mContext.getPackageManager() 514 .getPackageUidAsUser(packageName, mContext.getUserId()); 515 grantPermission(device, uid); 516 } catch (NameNotFoundException e) { 517 Log.e(TAG, "Package " + packageName + " not found.", e); 518 } 519 } 520 521 /** 522 * Returns true if the specified USB function is currently enabled when in device mode. 523 * <p> 524 * USB functions represent interfaces which are published to the host to access 525 * services offered by the device. 526 * </p> 527 * 528 * @param function name of the USB function 529 * @return true if the USB function is enabled 530 * 531 * {@hide} 532 */ isFunctionEnabled(String function)533 public boolean isFunctionEnabled(String function) { 534 try { 535 return mService.isFunctionEnabled(function); 536 } catch (RemoteException e) { 537 throw e.rethrowFromSystemServer(); 538 } 539 } 540 541 /** 542 * Sets the current USB function when in device mode. 543 * <p> 544 * USB functions represent interfaces which are published to the host to access 545 * services offered by the device. 546 * </p><p> 547 * This method is intended to select among primary USB functions. The system may 548 * automatically activate additional functions such as {@link #USB_FUNCTION_ADB} 549 * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states. 550 * </p><p> 551 * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE}, 552 * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, 553 * or {@link #USB_FUNCTION_RNDIS}. 554 * </p><p> 555 * Also sets whether USB data (for example, MTP exposed pictures) should be made available 556 * on the USB connection when in device mode. Unlocking usb data should only be done with 557 * user involvement, since exposing pictures or other data could leak sensitive 558 * user information. 559 * </p><p> 560 * Note: This function is asynchronous and may fail silently without applying 561 * the requested changes. 562 * </p> 563 * 564 * @param function name of the USB function, or null to restore the default function 565 * @param usbDataUnlocked whether user data is accessible 566 * 567 * {@hide} 568 */ setCurrentFunction(String function, boolean usbDataUnlocked)569 public void setCurrentFunction(String function, boolean usbDataUnlocked) { 570 try { 571 mService.setCurrentFunction(function, usbDataUnlocked); 572 } catch (RemoteException e) { 573 throw e.rethrowFromSystemServer(); 574 } 575 } 576 577 /** 578 * Returns a list of physical USB ports on the device. 579 * <p> 580 * This list is guaranteed to contain all dual-role USB Type C ports but it might 581 * be missing other ports depending on whether the kernel USB drivers have been 582 * updated to publish all of the device's ports through the new "dual_role_usb" 583 * device class (which supports all types of ports despite its name). 584 * </p> 585 * 586 * @return The list of USB ports, or null if none. 587 * 588 * @hide 589 */ getPorts()590 public UsbPort[] getPorts() { 591 try { 592 return mService.getPorts(); 593 } catch (RemoteException e) { 594 throw e.rethrowFromSystemServer(); 595 } 596 } 597 598 /** 599 * Gets the status of the specified USB port. 600 * 601 * @param port The port to query. 602 * @return The status of the specified USB port, or null if unknown. 603 * 604 * @hide 605 */ getPortStatus(UsbPort port)606 public UsbPortStatus getPortStatus(UsbPort port) { 607 Preconditions.checkNotNull(port, "port must not be null"); 608 609 try { 610 return mService.getPortStatus(port.getId()); 611 } catch (RemoteException e) { 612 throw e.rethrowFromSystemServer(); 613 } 614 } 615 616 /** 617 * Sets the desired role combination of the port. 618 * <p> 619 * The supported role combinations depend on what is connected to the port and may be 620 * determined by consulting 621 * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}. 622 * </p><p> 623 * Note: This function is asynchronous and may fail silently without applying 624 * the requested changes. If this function does cause a status change to occur then 625 * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent. 626 * </p> 627 * 628 * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE} 629 * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role. 630 * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST} 631 * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role. 632 * 633 * @hide 634 */ setPortRoles(UsbPort port, int powerRole, int dataRole)635 public void setPortRoles(UsbPort port, int powerRole, int dataRole) { 636 Preconditions.checkNotNull(port, "port must not be null"); 637 UsbPort.checkRoles(powerRole, dataRole); 638 639 try { 640 mService.setPortRoles(port.getId(), powerRole, dataRole); 641 } catch (RemoteException e) { 642 throw e.rethrowFromSystemServer(); 643 } 644 } 645 646 /** 647 * Sets the component that will handle USB device connection. 648 * <p> 649 * Setting component allows to specify external USB host manager to handle use cases, where 650 * selection dialog for an activity that will handle USB device is undesirable. 651 * Only system components can call this function, as it requires the MANAGE_USB permission. 652 * 653 * @param usbDeviceConnectionHandler The component to handle usb connections, 654 * {@code null} to unset. 655 * 656 * {@hide} 657 */ setUsbDeviceConnectionHandler(@ullable ComponentName usbDeviceConnectionHandler)658 public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) { 659 try { 660 mService.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler); 661 } catch (RemoteException e) { 662 throw e.rethrowFromSystemServer(); 663 } 664 } 665 666 /** @hide */ addFunction(String functions, String function)667 public static String addFunction(String functions, String function) { 668 if (USB_FUNCTION_NONE.equals(functions)) { 669 return function; 670 } 671 if (!containsFunction(functions, function)) { 672 if (functions.length() > 0) { 673 functions += ","; 674 } 675 functions += function; 676 } 677 return functions; 678 } 679 680 /** @hide */ removeFunction(String functions, String function)681 public static String removeFunction(String functions, String function) { 682 String[] split = functions.split(","); 683 for (int i = 0; i < split.length; i++) { 684 if (function.equals(split[i])) { 685 split[i] = null; 686 } 687 } 688 if (split.length == 1 && split[0] == null) { 689 return USB_FUNCTION_NONE; 690 } 691 StringBuilder builder = new StringBuilder(); 692 for (int i = 0; i < split.length; i++) { 693 String s = split[i]; 694 if (s != null) { 695 if (builder.length() > 0) { 696 builder.append(","); 697 } 698 builder.append(s); 699 } 700 } 701 return builder.toString(); 702 } 703 704 /** @hide */ containsFunction(String functions, String function)705 public static boolean containsFunction(String functions, String function) { 706 int index = functions.indexOf(function); 707 if (index < 0) return false; 708 if (index > 0 && functions.charAt(index - 1) != ',') return false; 709 int charAfter = index + function.length(); 710 if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; 711 return true; 712 } 713 } 714