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.server.usb; 18 19 import android.app.PendingIntent; 20 import android.content.ActivityNotFoundException; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ActivityInfo; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.content.pm.ResolveInfo; 30 import android.content.res.XmlResourceParser; 31 import android.hardware.usb.UsbAccessory; 32 import android.hardware.usb.UsbDevice; 33 import android.hardware.usb.UsbInterface; 34 import android.hardware.usb.UsbManager; 35 import android.os.Binder; 36 import android.os.Environment; 37 import android.os.Process; 38 import android.os.UserHandle; 39 import android.util.AtomicFile; 40 import android.util.Log; 41 import android.util.Slog; 42 import android.util.SparseBooleanArray; 43 import android.util.Xml; 44 45 import com.android.internal.content.PackageMonitor; 46 import com.android.internal.util.FastXmlSerializer; 47 import com.android.internal.util.IndentingPrintWriter; 48 import com.android.internal.util.XmlUtils; 49 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 import org.xmlpull.v1.XmlSerializer; 53 54 import java.io.File; 55 import java.io.FileInputStream; 56 import java.io.FileNotFoundException; 57 import java.io.FileOutputStream; 58 import java.io.IOException; 59 import java.nio.charset.StandardCharsets; 60 import java.util.ArrayList; 61 import java.util.HashMap; 62 import java.util.List; 63 64 import libcore.io.IoUtils; 65 66 class UsbSettingsManager { 67 private static final String TAG = "UsbSettingsManager"; 68 private static final boolean DEBUG = false; 69 70 /** Legacy settings file, before multi-user */ 71 private static final File sSingleUserSettingsFile = new File( 72 "/data/system/usb_device_manager.xml"); 73 74 private final UserHandle mUser; 75 private final AtomicFile mSettingsFile; 76 private final boolean mDisablePermissionDialogs; 77 78 private final Context mContext; 79 private final Context mUserContext; 80 private final PackageManager mPackageManager; 81 82 // Temporary mapping USB device name to list of UIDs with permissions for the device 83 private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = 84 new HashMap<String, SparseBooleanArray>(); 85 // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory 86 private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = 87 new HashMap<UsbAccessory, SparseBooleanArray>(); 88 // Maps DeviceFilter to user preferred application package 89 private final HashMap<DeviceFilter, String> mDevicePreferenceMap = 90 new HashMap<DeviceFilter, String>(); 91 // Maps AccessoryFilter to user preferred application package 92 private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap = 93 new HashMap<AccessoryFilter, String>(); 94 95 private final Object mLock = new Object(); 96 97 // This class is used to describe a USB device. 98 // When used in HashMaps all values must be specified, 99 // but wildcards can be used for any of the fields in 100 // the package meta-data. 101 private static class DeviceFilter { 102 // USB Vendor ID (or -1 for unspecified) 103 public final int mVendorId; 104 // USB Product ID (or -1 for unspecified) 105 public final int mProductId; 106 // USB device or interface class (or -1 for unspecified) 107 public final int mClass; 108 // USB device subclass (or -1 for unspecified) 109 public final int mSubclass; 110 // USB device protocol (or -1 for unspecified) 111 public final int mProtocol; 112 // USB device manufacturer name string (or null for unspecified) 113 public final String mManufacturerName; 114 // USB device product name string (or null for unspecified) 115 public final String mProductName; 116 // USB device serial number string (or null for unspecified) 117 public final String mSerialNumber; 118 DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum)119 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, 120 String manufacturer, String product, String serialnum) { 121 mVendorId = vid; 122 mProductId = pid; 123 mClass = clasz; 124 mSubclass = subclass; 125 mProtocol = protocol; 126 mManufacturerName = manufacturer; 127 mProductName = product; 128 mSerialNumber = serialnum; 129 } 130 DeviceFilter(UsbDevice device)131 public DeviceFilter(UsbDevice device) { 132 mVendorId = device.getVendorId(); 133 mProductId = device.getProductId(); 134 mClass = device.getDeviceClass(); 135 mSubclass = device.getDeviceSubclass(); 136 mProtocol = device.getDeviceProtocol(); 137 mManufacturerName = device.getManufacturerName(); 138 mProductName = device.getProductName(); 139 mSerialNumber = device.getSerialNumber(); 140 } 141 read(XmlPullParser parser)142 public static DeviceFilter read(XmlPullParser parser) 143 throws XmlPullParserException, IOException { 144 int vendorId = -1; 145 int productId = -1; 146 int deviceClass = -1; 147 int deviceSubclass = -1; 148 int deviceProtocol = -1; 149 String manufacturerName = null; 150 String productName = null; 151 String serialNumber = null; 152 153 int count = parser.getAttributeCount(); 154 for (int i = 0; i < count; i++) { 155 String name = parser.getAttributeName(i); 156 String value = parser.getAttributeValue(i); 157 // Attribute values are ints or strings 158 if ("manufacturer-name".equals(name)) { 159 manufacturerName = value; 160 } else if ("product-name".equals(name)) { 161 productName = value; 162 } else if ("serial-number".equals(name)) { 163 serialNumber = value; 164 } else { 165 int intValue = -1; 166 int radix = 10; 167 if (value != null && value.length() > 2 && value.charAt(0) == '0' && 168 (value.charAt(1) == 'x' || value.charAt(1) == 'X')) { 169 // allow hex values starting with 0x or 0X 170 radix = 16; 171 value = value.substring(2); 172 } 173 try { 174 intValue = Integer.parseInt(value, radix); 175 } catch (NumberFormatException e) { 176 Slog.e(TAG, "invalid number for field " + name, e); 177 continue; 178 } 179 if ("vendor-id".equals(name)) { 180 vendorId = intValue; 181 } else if ("product-id".equals(name)) { 182 productId = intValue; 183 } else if ("class".equals(name)) { 184 deviceClass = intValue; 185 } else if ("subclass".equals(name)) { 186 deviceSubclass = intValue; 187 } else if ("protocol".equals(name)) { 188 deviceProtocol = intValue; 189 } 190 } 191 } 192 return new DeviceFilter(vendorId, productId, 193 deviceClass, deviceSubclass, deviceProtocol, 194 manufacturerName, productName, serialNumber); 195 } 196 write(XmlSerializer serializer)197 public void write(XmlSerializer serializer) throws IOException { 198 serializer.startTag(null, "usb-device"); 199 if (mVendorId != -1) { 200 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); 201 } 202 if (mProductId != -1) { 203 serializer.attribute(null, "product-id", Integer.toString(mProductId)); 204 } 205 if (mClass != -1) { 206 serializer.attribute(null, "class", Integer.toString(mClass)); 207 } 208 if (mSubclass != -1) { 209 serializer.attribute(null, "subclass", Integer.toString(mSubclass)); 210 } 211 if (mProtocol != -1) { 212 serializer.attribute(null, "protocol", Integer.toString(mProtocol)); 213 } 214 if (mManufacturerName != null) { 215 serializer.attribute(null, "manufacturer-name", mManufacturerName); 216 } 217 if (mProductName != null) { 218 serializer.attribute(null, "product-name", mProductName); 219 } 220 if (mSerialNumber != null) { 221 serializer.attribute(null, "serial-number", mSerialNumber); 222 } 223 serializer.endTag(null, "usb-device"); 224 } 225 matches(int clasz, int subclass, int protocol)226 private boolean matches(int clasz, int subclass, int protocol) { 227 return ((mClass == -1 || clasz == mClass) && 228 (mSubclass == -1 || subclass == mSubclass) && 229 (mProtocol == -1 || protocol == mProtocol)); 230 } 231 matches(UsbDevice device)232 public boolean matches(UsbDevice device) { 233 if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; 234 if (mProductId != -1 && device.getProductId() != mProductId) return false; 235 if (mManufacturerName != null && device.getManufacturerName() == null) return false; 236 if (mProductName != null && device.getProductName() == null) return false; 237 if (mSerialNumber != null && device.getSerialNumber() == null) return false; 238 if (mManufacturerName != null && device.getManufacturerName() != null && 239 !mManufacturerName.equals(device.getManufacturerName())) return false; 240 if (mProductName != null && device.getProductName() != null && 241 !mProductName.equals(device.getProductName())) return false; 242 if (mSerialNumber != null && device.getSerialNumber() != null && 243 !mSerialNumber.equals(device.getSerialNumber())) return false; 244 245 // check device class/subclass/protocol 246 if (matches(device.getDeviceClass(), device.getDeviceSubclass(), 247 device.getDeviceProtocol())) return true; 248 249 // if device doesn't match, check the interfaces 250 int count = device.getInterfaceCount(); 251 for (int i = 0; i < count; i++) { 252 UsbInterface intf = device.getInterface(i); 253 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), 254 intf.getInterfaceProtocol())) return true; 255 } 256 257 return false; 258 } 259 matches(DeviceFilter f)260 public boolean matches(DeviceFilter f) { 261 if (mVendorId != -1 && f.mVendorId != mVendorId) return false; 262 if (mProductId != -1 && f.mProductId != mProductId) return false; 263 if (f.mManufacturerName != null && mManufacturerName == null) return false; 264 if (f.mProductName != null && mProductName == null) return false; 265 if (f.mSerialNumber != null && mSerialNumber == null) return false; 266 if (mManufacturerName != null && f.mManufacturerName != null && 267 !mManufacturerName.equals(f.mManufacturerName)) return false; 268 if (mProductName != null && f.mProductName != null && 269 !mProductName.equals(f.mProductName)) return false; 270 if (mSerialNumber != null && f.mSerialNumber != null && 271 !mSerialNumber.equals(f.mSerialNumber)) return false; 272 273 // check device class/subclass/protocol 274 return matches(f.mClass, f.mSubclass, f.mProtocol); 275 } 276 277 @Override equals(Object obj)278 public boolean equals(Object obj) { 279 // can't compare if we have wildcard strings 280 if (mVendorId == -1 || mProductId == -1 || 281 mClass == -1 || mSubclass == -1 || mProtocol == -1) { 282 return false; 283 } 284 if (obj instanceof DeviceFilter) { 285 DeviceFilter filter = (DeviceFilter)obj; 286 287 if (filter.mVendorId != mVendorId || 288 filter.mProductId != mProductId || 289 filter.mClass != mClass || 290 filter.mSubclass != mSubclass || 291 filter.mProtocol != mProtocol) { 292 return(false); 293 } 294 if ((filter.mManufacturerName != null && 295 mManufacturerName == null) || 296 (filter.mManufacturerName == null && 297 mManufacturerName != null) || 298 (filter.mProductName != null && 299 mProductName == null) || 300 (filter.mProductName == null && 301 mProductName != null) || 302 (filter.mSerialNumber != null && 303 mSerialNumber == null) || 304 (filter.mSerialNumber == null && 305 mSerialNumber != null)) { 306 return(false); 307 } 308 if ((filter.mManufacturerName != null && 309 mManufacturerName != null && 310 !mManufacturerName.equals(filter.mManufacturerName)) || 311 (filter.mProductName != null && 312 mProductName != null && 313 !mProductName.equals(filter.mProductName)) || 314 (filter.mSerialNumber != null && 315 mSerialNumber != null && 316 !mSerialNumber.equals(filter.mSerialNumber))) { 317 return(false); 318 } 319 return(true); 320 } 321 if (obj instanceof UsbDevice) { 322 UsbDevice device = (UsbDevice)obj; 323 if (device.getVendorId() != mVendorId || 324 device.getProductId() != mProductId || 325 device.getDeviceClass() != mClass || 326 device.getDeviceSubclass() != mSubclass || 327 device.getDeviceProtocol() != mProtocol) { 328 return(false); 329 } 330 if ((mManufacturerName != null && device.getManufacturerName() == null) || 331 (mManufacturerName == null && device.getManufacturerName() != null) || 332 (mProductName != null && device.getProductName() == null) || 333 (mProductName == null && device.getProductName() != null) || 334 (mSerialNumber != null && device.getSerialNumber() == null) || 335 (mSerialNumber == null && device.getSerialNumber() != null)) { 336 return(false); 337 } 338 if ((device.getManufacturerName() != null && 339 !mManufacturerName.equals(device.getManufacturerName())) || 340 (device.getProductName() != null && 341 !mProductName.equals(device.getProductName())) || 342 (device.getSerialNumber() != null && 343 !mSerialNumber.equals(device.getSerialNumber()))) { 344 return(false); 345 } 346 return true; 347 } 348 return false; 349 } 350 351 @Override hashCode()352 public int hashCode() { 353 return (((mVendorId << 16) | mProductId) ^ 354 ((mClass << 16) | (mSubclass << 8) | mProtocol)); 355 } 356 357 @Override toString()358 public String toString() { 359 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + 360 ",mClass=" + mClass + ",mSubclass=" + mSubclass + 361 ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + 362 ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + 363 "]"; 364 } 365 } 366 367 // This class is used to describe a USB accessory. 368 // When used in HashMaps all values must be specified, 369 // but wildcards can be used for any of the fields in 370 // the package meta-data. 371 private static class AccessoryFilter { 372 // USB accessory manufacturer (or null for unspecified) 373 public final String mManufacturer; 374 // USB accessory model (or null for unspecified) 375 public final String mModel; 376 // USB accessory version (or null for unspecified) 377 public final String mVersion; 378 AccessoryFilter(String manufacturer, String model, String version)379 public AccessoryFilter(String manufacturer, String model, String version) { 380 mManufacturer = manufacturer; 381 mModel = model; 382 mVersion = version; 383 } 384 AccessoryFilter(UsbAccessory accessory)385 public AccessoryFilter(UsbAccessory accessory) { 386 mManufacturer = accessory.getManufacturer(); 387 mModel = accessory.getModel(); 388 mVersion = accessory.getVersion(); 389 } 390 read(XmlPullParser parser)391 public static AccessoryFilter read(XmlPullParser parser) 392 throws XmlPullParserException, IOException { 393 String manufacturer = null; 394 String model = null; 395 String version = null; 396 397 int count = parser.getAttributeCount(); 398 for (int i = 0; i < count; i++) { 399 String name = parser.getAttributeName(i); 400 String value = parser.getAttributeValue(i); 401 402 if ("manufacturer".equals(name)) { 403 manufacturer = value; 404 } else if ("model".equals(name)) { 405 model = value; 406 } else if ("version".equals(name)) { 407 version = value; 408 } 409 } 410 return new AccessoryFilter(manufacturer, model, version); 411 } 412 write(XmlSerializer serializer)413 public void write(XmlSerializer serializer)throws IOException { 414 serializer.startTag(null, "usb-accessory"); 415 if (mManufacturer != null) { 416 serializer.attribute(null, "manufacturer", mManufacturer); 417 } 418 if (mModel != null) { 419 serializer.attribute(null, "model", mModel); 420 } 421 if (mVersion != null) { 422 serializer.attribute(null, "version", mVersion); 423 } 424 serializer.endTag(null, "usb-accessory"); 425 } 426 matches(UsbAccessory acc)427 public boolean matches(UsbAccessory acc) { 428 if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false; 429 if (mModel != null && !acc.getModel().equals(mModel)) return false; 430 if (mVersion != null && !acc.getVersion().equals(mVersion)) return false; 431 return true; 432 } 433 matches(AccessoryFilter f)434 public boolean matches(AccessoryFilter f) { 435 if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false; 436 if (mModel != null && !f.mModel.equals(mModel)) return false; 437 if (mVersion != null && !f.mVersion.equals(mVersion)) return false; 438 return true; 439 } 440 441 @Override equals(Object obj)442 public boolean equals(Object obj) { 443 // can't compare if we have wildcard strings 444 if (mManufacturer == null || mModel == null || mVersion == null) { 445 return false; 446 } 447 if (obj instanceof AccessoryFilter) { 448 AccessoryFilter filter = (AccessoryFilter)obj; 449 return (mManufacturer.equals(filter.mManufacturer) && 450 mModel.equals(filter.mModel) && 451 mVersion.equals(filter.mVersion)); 452 } 453 if (obj instanceof UsbAccessory) { 454 UsbAccessory accessory = (UsbAccessory)obj; 455 return (mManufacturer.equals(accessory.getManufacturer()) && 456 mModel.equals(accessory.getModel()) && 457 mVersion.equals(accessory.getVersion())); 458 } 459 return false; 460 } 461 462 @Override hashCode()463 public int hashCode() { 464 return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ 465 (mModel == null ? 0 : mModel.hashCode()) ^ 466 (mVersion == null ? 0 : mVersion.hashCode())); 467 } 468 469 @Override toString()470 public String toString() { 471 return "AccessoryFilter[mManufacturer=\"" + mManufacturer + 472 "\", mModel=\"" + mModel + 473 "\", mVersion=\"" + mVersion + "\"]"; 474 } 475 } 476 477 private class MyPackageMonitor extends PackageMonitor { 478 @Override onPackageAdded(String packageName, int uid)479 public void onPackageAdded(String packageName, int uid) { 480 handlePackageUpdate(packageName); 481 } 482 483 @Override onPackageChanged(String packageName, int uid, String[] components)484 public boolean onPackageChanged(String packageName, int uid, String[] components) { 485 handlePackageUpdate(packageName); 486 return false; 487 } 488 489 @Override onPackageRemoved(String packageName, int uid)490 public void onPackageRemoved(String packageName, int uid) { 491 clearDefaults(packageName); 492 } 493 } 494 495 MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); 496 497 private final MtpNotificationManager mMtpNotificationManager; 498 UsbSettingsManager(Context context, UserHandle user)499 public UsbSettingsManager(Context context, UserHandle user) { 500 if (DEBUG) Slog.v(TAG, "Creating settings for " + user); 501 502 try { 503 mUserContext = context.createPackageContextAsUser("android", 0, user); 504 } catch (NameNotFoundException e) { 505 throw new RuntimeException("Missing android package"); 506 } 507 508 mContext = context; 509 mPackageManager = mUserContext.getPackageManager(); 510 511 mUser = user; 512 mSettingsFile = new AtomicFile(new File( 513 Environment.getUserSystemDirectory(user.getIdentifier()), 514 "usb_device_manager.xml")); 515 516 mDisablePermissionDialogs = context.getResources().getBoolean( 517 com.android.internal.R.bool.config_disableUsbPermissionDialogs); 518 519 synchronized (mLock) { 520 if (UserHandle.SYSTEM.equals(user)) { 521 upgradeSingleUserLocked(); 522 } 523 readSettingsLocked(); 524 } 525 526 mPackageMonitor.register(mUserContext, null, true); 527 mMtpNotificationManager = new MtpNotificationManager( 528 context, 529 new MtpNotificationManager.OnOpenInAppListener() { 530 @Override 531 public void onOpenInApp(UsbDevice device) { 532 resolveActivity(createDeviceAttachedIntent(device), device); 533 } 534 }); 535 } 536 readPreference(XmlPullParser parser)537 private void readPreference(XmlPullParser parser) 538 throws XmlPullParserException, IOException { 539 String packageName = null; 540 int count = parser.getAttributeCount(); 541 for (int i = 0; i < count; i++) { 542 if ("package".equals(parser.getAttributeName(i))) { 543 packageName = parser.getAttributeValue(i); 544 break; 545 } 546 } 547 XmlUtils.nextElement(parser); 548 if ("usb-device".equals(parser.getName())) { 549 DeviceFilter filter = DeviceFilter.read(parser); 550 mDevicePreferenceMap.put(filter, packageName); 551 } else if ("usb-accessory".equals(parser.getName())) { 552 AccessoryFilter filter = AccessoryFilter.read(parser); 553 mAccessoryPreferenceMap.put(filter, packageName); 554 } 555 XmlUtils.nextElement(parser); 556 } 557 558 /** 559 * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}. 560 * Should only by called by owner. 561 */ upgradeSingleUserLocked()562 private void upgradeSingleUserLocked() { 563 if (sSingleUserSettingsFile.exists()) { 564 mDevicePreferenceMap.clear(); 565 mAccessoryPreferenceMap.clear(); 566 567 FileInputStream fis = null; 568 try { 569 fis = new FileInputStream(sSingleUserSettingsFile); 570 XmlPullParser parser = Xml.newPullParser(); 571 parser.setInput(fis, StandardCharsets.UTF_8.name()); 572 573 XmlUtils.nextElement(parser); 574 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 575 final String tagName = parser.getName(); 576 if ("preference".equals(tagName)) { 577 readPreference(parser); 578 } else { 579 XmlUtils.nextElement(parser); 580 } 581 } 582 } catch (IOException e) { 583 Log.wtf(TAG, "Failed to read single-user settings", e); 584 } catch (XmlPullParserException e) { 585 Log.wtf(TAG, "Failed to read single-user settings", e); 586 } finally { 587 IoUtils.closeQuietly(fis); 588 } 589 590 writeSettingsLocked(); 591 592 // Success or failure, we delete single-user file 593 sSingleUserSettingsFile.delete(); 594 } 595 } 596 readSettingsLocked()597 private void readSettingsLocked() { 598 if (DEBUG) Slog.v(TAG, "readSettingsLocked()"); 599 600 mDevicePreferenceMap.clear(); 601 mAccessoryPreferenceMap.clear(); 602 603 FileInputStream stream = null; 604 try { 605 stream = mSettingsFile.openRead(); 606 XmlPullParser parser = Xml.newPullParser(); 607 parser.setInput(stream, StandardCharsets.UTF_8.name()); 608 609 XmlUtils.nextElement(parser); 610 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 611 String tagName = parser.getName(); 612 if ("preference".equals(tagName)) { 613 readPreference(parser); 614 } else { 615 XmlUtils.nextElement(parser); 616 } 617 } 618 } catch (FileNotFoundException e) { 619 if (DEBUG) Slog.d(TAG, "settings file not found"); 620 } catch (Exception e) { 621 Slog.e(TAG, "error reading settings file, deleting to start fresh", e); 622 mSettingsFile.delete(); 623 } finally { 624 IoUtils.closeQuietly(stream); 625 } 626 } 627 writeSettingsLocked()628 private void writeSettingsLocked() { 629 if (DEBUG) Slog.v(TAG, "writeSettingsLocked()"); 630 631 FileOutputStream fos = null; 632 try { 633 fos = mSettingsFile.startWrite(); 634 635 FastXmlSerializer serializer = new FastXmlSerializer(); 636 serializer.setOutput(fos, StandardCharsets.UTF_8.name()); 637 serializer.startDocument(null, true); 638 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 639 serializer.startTag(null, "settings"); 640 641 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { 642 serializer.startTag(null, "preference"); 643 serializer.attribute(null, "package", mDevicePreferenceMap.get(filter)); 644 filter.write(serializer); 645 serializer.endTag(null, "preference"); 646 } 647 648 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { 649 serializer.startTag(null, "preference"); 650 serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter)); 651 filter.write(serializer); 652 serializer.endTag(null, "preference"); 653 } 654 655 serializer.endTag(null, "settings"); 656 serializer.endDocument(); 657 658 mSettingsFile.finishWrite(fos); 659 } catch (IOException e) { 660 Slog.e(TAG, "Failed to write settings", e); 661 if (fos != null) { 662 mSettingsFile.failWrite(fos); 663 } 664 } 665 } 666 667 // Checks to see if a package matches a device or accessory. 668 // Only one of device and accessory should be non-null. packageMatchesLocked(ResolveInfo info, String metaDataName, UsbDevice device, UsbAccessory accessory)669 private boolean packageMatchesLocked(ResolveInfo info, String metaDataName, 670 UsbDevice device, UsbAccessory accessory) { 671 ActivityInfo ai = info.activityInfo; 672 673 XmlResourceParser parser = null; 674 try { 675 parser = ai.loadXmlMetaData(mPackageManager, metaDataName); 676 if (parser == null) { 677 Slog.w(TAG, "no meta-data for " + info); 678 return false; 679 } 680 681 XmlUtils.nextElement(parser); 682 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 683 String tagName = parser.getName(); 684 if (device != null && "usb-device".equals(tagName)) { 685 DeviceFilter filter = DeviceFilter.read(parser); 686 if (filter.matches(device)) { 687 return true; 688 } 689 } 690 else if (accessory != null && "usb-accessory".equals(tagName)) { 691 AccessoryFilter filter = AccessoryFilter.read(parser); 692 if (filter.matches(accessory)) { 693 return true; 694 } 695 } 696 XmlUtils.nextElement(parser); 697 } 698 } catch (Exception e) { 699 Slog.w(TAG, "Unable to load component info " + info.toString(), e); 700 } finally { 701 if (parser != null) parser.close(); 702 } 703 return false; 704 } 705 getDeviceMatchesLocked(UsbDevice device, Intent intent)706 private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) { 707 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); 708 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent, 709 PackageManager.GET_META_DATA); 710 int count = resolveInfos.size(); 711 for (int i = 0; i < count; i++) { 712 ResolveInfo resolveInfo = resolveInfos.get(i); 713 if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) { 714 matches.add(resolveInfo); 715 } 716 } 717 return matches; 718 } 719 getAccessoryMatchesLocked( UsbAccessory accessory, Intent intent)720 private final ArrayList<ResolveInfo> getAccessoryMatchesLocked( 721 UsbAccessory accessory, Intent intent) { 722 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); 723 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent, 724 PackageManager.GET_META_DATA); 725 int count = resolveInfos.size(); 726 for (int i = 0; i < count; i++) { 727 ResolveInfo resolveInfo = resolveInfos.get(i); 728 if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) { 729 matches.add(resolveInfo); 730 } 731 } 732 return matches; 733 } 734 deviceAttached(UsbDevice device)735 public void deviceAttached(UsbDevice device) { 736 final Intent intent = createDeviceAttachedIntent(device); 737 738 // Send broadcast to running activity with registered intent 739 mUserContext.sendBroadcast(intent); 740 741 if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) { 742 // Show notification if the device is MTP storage. 743 mMtpNotificationManager.showNotification(device); 744 } else { 745 resolveActivity(intent, device); 746 } 747 } 748 resolveActivity(Intent intent, UsbDevice device)749 private void resolveActivity(Intent intent, UsbDevice device) { 750 ArrayList<ResolveInfo> matches; 751 String defaultPackage; 752 synchronized (mLock) { 753 matches = getDeviceMatchesLocked(device, intent); 754 // Launch our default activity directly, if we have one. 755 // Otherwise we will start the UsbResolverActivity to allow the user to choose. 756 defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device)); 757 } 758 759 // Start activity with registered intent 760 resolveActivity(intent, matches, defaultPackage, device, null); 761 } 762 deviceDetached(UsbDevice device)763 public void deviceDetached(UsbDevice device) { 764 // clear temporary permissions for the device 765 mDevicePermissionMap.remove(device.getDeviceName()); 766 767 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); 768 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 769 if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent); 770 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 771 772 mMtpNotificationManager.hideNotification(device.getDeviceId()); 773 } 774 accessoryAttached(UsbAccessory accessory)775 public void accessoryAttached(UsbAccessory accessory) { 776 Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); 777 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 778 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 779 780 ArrayList<ResolveInfo> matches; 781 String defaultPackage; 782 synchronized (mLock) { 783 matches = getAccessoryMatchesLocked(accessory, intent); 784 // Launch our default activity directly, if we have one. 785 // Otherwise we will start the UsbResolverActivity to allow the user to choose. 786 defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)); 787 } 788 789 resolveActivity(intent, matches, defaultPackage, null, accessory); 790 } 791 accessoryDetached(UsbAccessory accessory)792 public void accessoryDetached(UsbAccessory accessory) { 793 // clear temporary permissions for the accessory 794 mAccessoryPermissionMap.remove(accessory); 795 796 Intent intent = new Intent( 797 UsbManager.ACTION_USB_ACCESSORY_DETACHED); 798 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 799 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 800 } 801 resolveActivity(Intent intent, ArrayList<ResolveInfo> matches, String defaultPackage, UsbDevice device, UsbAccessory accessory)802 private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches, 803 String defaultPackage, UsbDevice device, UsbAccessory accessory) { 804 int count = matches.size(); 805 806 // don't show the resolver activity if there are no choices available 807 if (count == 0) { 808 if (accessory != null) { 809 String uri = accessory.getUri(); 810 if (uri != null && uri.length() > 0) { 811 // display URI to user 812 // start UsbResolverActivity so user can choose an activity 813 Intent dialogIntent = new Intent(); 814 dialogIntent.setClassName("com.android.systemui", 815 "com.android.systemui.usb.UsbAccessoryUriActivity"); 816 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 817 dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 818 dialogIntent.putExtra("uri", uri); 819 try { 820 mUserContext.startActivityAsUser(dialogIntent, mUser); 821 } catch (ActivityNotFoundException e) { 822 Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); 823 } 824 } 825 } 826 827 // do nothing 828 return; 829 } 830 831 ResolveInfo defaultRI = null; 832 if (count == 1 && defaultPackage == null) { 833 // Check to see if our single choice is on the system partition. 834 // If so, treat it as our default without calling UsbResolverActivity 835 ResolveInfo rInfo = matches.get(0); 836 if (rInfo.activityInfo != null && 837 rInfo.activityInfo.applicationInfo != null && 838 (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 839 defaultRI = rInfo; 840 } 841 842 if (mDisablePermissionDialogs) { 843 // bypass dialog and launch the only matching activity 844 rInfo = matches.get(0); 845 if (rInfo.activityInfo != null) { 846 defaultPackage = rInfo.activityInfo.packageName; 847 } 848 } 849 } 850 851 if (defaultRI == null && defaultPackage != null) { 852 // look for default activity 853 for (int i = 0; i < count; i++) { 854 ResolveInfo rInfo = matches.get(i); 855 if (rInfo.activityInfo != null && 856 defaultPackage.equals(rInfo.activityInfo.packageName)) { 857 defaultRI = rInfo; 858 break; 859 } 860 } 861 } 862 863 if (defaultRI != null) { 864 // grant permission for default activity 865 if (device != null) { 866 grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid); 867 } else if (accessory != null) { 868 grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid); 869 } 870 871 // start default activity directly 872 try { 873 intent.setComponent( 874 new ComponentName(defaultRI.activityInfo.packageName, 875 defaultRI.activityInfo.name)); 876 mUserContext.startActivityAsUser(intent, mUser); 877 } catch (ActivityNotFoundException e) { 878 Slog.e(TAG, "startActivity failed", e); 879 } 880 } else { 881 Intent resolverIntent = new Intent(); 882 resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 883 884 if (count == 1) { 885 // start UsbConfirmActivity if there is only one choice 886 resolverIntent.setClassName("com.android.systemui", 887 "com.android.systemui.usb.UsbConfirmActivity"); 888 resolverIntent.putExtra("rinfo", matches.get(0)); 889 890 if (device != null) { 891 resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device); 892 } else { 893 resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 894 } 895 } else { 896 // start UsbResolverActivity so user can choose an activity 897 resolverIntent.setClassName("com.android.systemui", 898 "com.android.systemui.usb.UsbResolverActivity"); 899 resolverIntent.putParcelableArrayListExtra("rlist", matches); 900 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); 901 } 902 try { 903 mUserContext.startActivityAsUser(resolverIntent, mUser); 904 } catch (ActivityNotFoundException e) { 905 Slog.e(TAG, "unable to start activity " + resolverIntent); 906 } 907 } 908 } 909 clearCompatibleMatchesLocked(String packageName, DeviceFilter filter)910 private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) { 911 boolean changed = false; 912 for (DeviceFilter test : mDevicePreferenceMap.keySet()) { 913 if (filter.matches(test)) { 914 mDevicePreferenceMap.remove(test); 915 changed = true; 916 } 917 } 918 return changed; 919 } 920 clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter)921 private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) { 922 boolean changed = false; 923 for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) { 924 if (filter.matches(test)) { 925 mAccessoryPreferenceMap.remove(test); 926 changed = true; 927 } 928 } 929 return changed; 930 } 931 handlePackageUpdateLocked(String packageName, ActivityInfo aInfo, String metaDataName)932 private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo, 933 String metaDataName) { 934 XmlResourceParser parser = null; 935 boolean changed = false; 936 937 try { 938 parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName); 939 if (parser == null) return false; 940 941 XmlUtils.nextElement(parser); 942 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 943 String tagName = parser.getName(); 944 if ("usb-device".equals(tagName)) { 945 DeviceFilter filter = DeviceFilter.read(parser); 946 if (clearCompatibleMatchesLocked(packageName, filter)) { 947 changed = true; 948 } 949 } 950 else if ("usb-accessory".equals(tagName)) { 951 AccessoryFilter filter = AccessoryFilter.read(parser); 952 if (clearCompatibleMatchesLocked(packageName, filter)) { 953 changed = true; 954 } 955 } 956 XmlUtils.nextElement(parser); 957 } 958 } catch (Exception e) { 959 Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); 960 } finally { 961 if (parser != null) parser.close(); 962 } 963 return changed; 964 } 965 966 // Check to see if the package supports any USB devices or accessories. 967 // If so, clear any non-matching preferences for matching devices/accessories. handlePackageUpdate(String packageName)968 private void handlePackageUpdate(String packageName) { 969 synchronized (mLock) { 970 PackageInfo info; 971 boolean changed = false; 972 973 try { 974 info = mPackageManager.getPackageInfo(packageName, 975 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); 976 } catch (NameNotFoundException e) { 977 Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); 978 return; 979 } 980 981 ActivityInfo[] activities = info.activities; 982 if (activities == null) return; 983 for (int i = 0; i < activities.length; i++) { 984 // check for meta-data, both for devices and accessories 985 if (handlePackageUpdateLocked(packageName, activities[i], 986 UsbManager.ACTION_USB_DEVICE_ATTACHED)) { 987 changed = true; 988 } 989 if (handlePackageUpdateLocked(packageName, activities[i], 990 UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { 991 changed = true; 992 } 993 } 994 995 if (changed) { 996 writeSettingsLocked(); 997 } 998 } 999 } 1000 hasPermission(UsbDevice device)1001 public boolean hasPermission(UsbDevice device) { 1002 synchronized (mLock) { 1003 int uid = Binder.getCallingUid(); 1004 if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { 1005 return true; 1006 } 1007 SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); 1008 if (uidList == null) { 1009 return false; 1010 } 1011 return uidList.get(uid); 1012 } 1013 } 1014 hasPermission(UsbAccessory accessory)1015 public boolean hasPermission(UsbAccessory accessory) { 1016 synchronized (mLock) { 1017 int uid = Binder.getCallingUid(); 1018 if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { 1019 return true; 1020 } 1021 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 1022 if (uidList == null) { 1023 return false; 1024 } 1025 return uidList.get(uid); 1026 } 1027 } 1028 checkPermission(UsbDevice device)1029 public void checkPermission(UsbDevice device) { 1030 if (!hasPermission(device)) { 1031 throw new SecurityException("User has not given permission to device " + device); 1032 } 1033 } 1034 checkPermission(UsbAccessory accessory)1035 public void checkPermission(UsbAccessory accessory) { 1036 if (!hasPermission(accessory)) { 1037 throw new SecurityException("User has not given permission to accessory " + accessory); 1038 } 1039 } 1040 requestPermissionDialog(Intent intent, String packageName, PendingIntent pi)1041 private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { 1042 final int uid = Binder.getCallingUid(); 1043 1044 // compare uid with packageName to foil apps pretending to be someone else 1045 try { 1046 ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0); 1047 if (aInfo.uid != uid) { 1048 throw new IllegalArgumentException("package " + packageName + 1049 " does not match caller's uid " + uid); 1050 } 1051 } catch (PackageManager.NameNotFoundException e) { 1052 throw new IllegalArgumentException("package " + packageName + " not found"); 1053 } 1054 1055 long identity = Binder.clearCallingIdentity(); 1056 intent.setClassName("com.android.systemui", 1057 "com.android.systemui.usb.UsbPermissionActivity"); 1058 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1059 intent.putExtra(Intent.EXTRA_INTENT, pi); 1060 intent.putExtra("package", packageName); 1061 intent.putExtra(Intent.EXTRA_UID, uid); 1062 try { 1063 mUserContext.startActivityAsUser(intent, mUser); 1064 } catch (ActivityNotFoundException e) { 1065 Slog.e(TAG, "unable to start UsbPermissionActivity"); 1066 } finally { 1067 Binder.restoreCallingIdentity(identity); 1068 } 1069 } 1070 requestPermission(UsbDevice device, String packageName, PendingIntent pi)1071 public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { 1072 Intent intent = new Intent(); 1073 1074 // respond immediately if permission has already been granted 1075 if (hasPermission(device)) { 1076 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 1077 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 1078 try { 1079 pi.send(mUserContext, 0, intent); 1080 } catch (PendingIntent.CanceledException e) { 1081 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 1082 } 1083 return; 1084 } 1085 1086 // start UsbPermissionActivity so user can choose an activity 1087 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 1088 requestPermissionDialog(intent, packageName, pi); 1089 } 1090 requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi)1091 public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) { 1092 Intent intent = new Intent(); 1093 1094 // respond immediately if permission has already been granted 1095 if (hasPermission(accessory)) { 1096 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 1097 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 1098 try { 1099 pi.send(mUserContext, 0, intent); 1100 } catch (PendingIntent.CanceledException e) { 1101 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 1102 } 1103 return; 1104 } 1105 1106 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 1107 requestPermissionDialog(intent, packageName, pi); 1108 } 1109 setDevicePackage(UsbDevice device, String packageName)1110 public void setDevicePackage(UsbDevice device, String packageName) { 1111 DeviceFilter filter = new DeviceFilter(device); 1112 boolean changed = false; 1113 synchronized (mLock) { 1114 if (packageName == null) { 1115 changed = (mDevicePreferenceMap.remove(filter) != null); 1116 } else { 1117 changed = !packageName.equals(mDevicePreferenceMap.get(filter)); 1118 if (changed) { 1119 mDevicePreferenceMap.put(filter, packageName); 1120 } 1121 } 1122 if (changed) { 1123 writeSettingsLocked(); 1124 } 1125 } 1126 } 1127 setAccessoryPackage(UsbAccessory accessory, String packageName)1128 public void setAccessoryPackage(UsbAccessory accessory, String packageName) { 1129 AccessoryFilter filter = new AccessoryFilter(accessory); 1130 boolean changed = false; 1131 synchronized (mLock) { 1132 if (packageName == null) { 1133 changed = (mAccessoryPreferenceMap.remove(filter) != null); 1134 } else { 1135 changed = !packageName.equals(mAccessoryPreferenceMap.get(filter)); 1136 if (changed) { 1137 mAccessoryPreferenceMap.put(filter, packageName); 1138 } 1139 } 1140 if (changed) { 1141 writeSettingsLocked(); 1142 } 1143 } 1144 } 1145 grantDevicePermission(UsbDevice device, int uid)1146 public void grantDevicePermission(UsbDevice device, int uid) { 1147 synchronized (mLock) { 1148 String deviceName = device.getDeviceName(); 1149 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 1150 if (uidList == null) { 1151 uidList = new SparseBooleanArray(1); 1152 mDevicePermissionMap.put(deviceName, uidList); 1153 } 1154 uidList.put(uid, true); 1155 } 1156 } 1157 grantAccessoryPermission(UsbAccessory accessory, int uid)1158 public void grantAccessoryPermission(UsbAccessory accessory, int uid) { 1159 synchronized (mLock) { 1160 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 1161 if (uidList == null) { 1162 uidList = new SparseBooleanArray(1); 1163 mAccessoryPermissionMap.put(accessory, uidList); 1164 } 1165 uidList.put(uid, true); 1166 } 1167 } 1168 hasDefaults(String packageName)1169 public boolean hasDefaults(String packageName) { 1170 synchronized (mLock) { 1171 if (mDevicePreferenceMap.values().contains(packageName)) return true; 1172 if (mAccessoryPreferenceMap.values().contains(packageName)) return true; 1173 return false; 1174 } 1175 } 1176 clearDefaults(String packageName)1177 public void clearDefaults(String packageName) { 1178 synchronized (mLock) { 1179 if (clearPackageDefaultsLocked(packageName)) { 1180 writeSettingsLocked(); 1181 } 1182 } 1183 } 1184 clearPackageDefaultsLocked(String packageName)1185 private boolean clearPackageDefaultsLocked(String packageName) { 1186 boolean cleared = false; 1187 synchronized (mLock) { 1188 if (mDevicePreferenceMap.containsValue(packageName)) { 1189 // make a copy of the key set to avoid ConcurrentModificationException 1190 Object[] keys = mDevicePreferenceMap.keySet().toArray(); 1191 for (int i = 0; i < keys.length; i++) { 1192 Object key = keys[i]; 1193 if (packageName.equals(mDevicePreferenceMap.get(key))) { 1194 mDevicePreferenceMap.remove(key); 1195 cleared = true; 1196 } 1197 } 1198 } 1199 if (mAccessoryPreferenceMap.containsValue(packageName)) { 1200 // make a copy of the key set to avoid ConcurrentModificationException 1201 Object[] keys = mAccessoryPreferenceMap.keySet().toArray(); 1202 for (int i = 0; i < keys.length; i++) { 1203 Object key = keys[i]; 1204 if (packageName.equals(mAccessoryPreferenceMap.get(key))) { 1205 mAccessoryPreferenceMap.remove(key); 1206 cleared = true; 1207 } 1208 } 1209 } 1210 return cleared; 1211 } 1212 } 1213 dump(IndentingPrintWriter pw)1214 public void dump(IndentingPrintWriter pw) { 1215 synchronized (mLock) { 1216 pw.println("Device permissions:"); 1217 for (String deviceName : mDevicePermissionMap.keySet()) { 1218 pw.print(" " + deviceName + ": "); 1219 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 1220 int count = uidList.size(); 1221 for (int i = 0; i < count; i++) { 1222 pw.print(Integer.toString(uidList.keyAt(i)) + " "); 1223 } 1224 pw.println(); 1225 } 1226 pw.println("Accessory permissions:"); 1227 for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { 1228 pw.print(" " + accessory + ": "); 1229 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 1230 int count = uidList.size(); 1231 for (int i = 0; i < count; i++) { 1232 pw.print(Integer.toString(uidList.keyAt(i)) + " "); 1233 } 1234 pw.println(); 1235 } 1236 pw.println("Device preferences:"); 1237 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { 1238 pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter)); 1239 } 1240 pw.println("Accessory preferences:"); 1241 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { 1242 pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter)); 1243 } 1244 } 1245 } 1246 createDeviceAttachedIntent(UsbDevice device)1247 private static Intent createDeviceAttachedIntent(UsbDevice device) { 1248 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); 1249 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 1250 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1251 return intent; 1252 } 1253 } 1254