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 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 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 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 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 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 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 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 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 352 public int hashCode() { 353 return (((mVendorId << 16) | mProductId) ^ 354 ((mClass << 16) | (mSubclass << 8) | mProtocol)); 355 } 356 357 @Override 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 379 public AccessoryFilter(String manufacturer, String model, String version) { 380 mManufacturer = manufacturer; 381 mModel = model; 382 mVersion = version; 383 } 384 385 public AccessoryFilter(UsbAccessory accessory) { 386 mManufacturer = accessory.getManufacturer(); 387 mModel = accessory.getModel(); 388 mVersion = accessory.getVersion(); 389 } 390 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 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 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 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 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 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 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 479 public void onPackageAdded(String packageName, int uid) { 480 handlePackageUpdate(packageName); 481 } 482 483 @Override 484 public boolean onPackageChanged(String packageName, int uid, String[] components) { 485 handlePackageUpdate(packageName); 486 return false; 487 } 488 489 @Override 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 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 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 */ 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 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 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 1177 public void clearDefaults(String packageName) { 1178 synchronized (mLock) { 1179 if (clearPackageDefaultsLocked(packageName)) { 1180 writeSettingsLocked(); 1181 } 1182 } 1183 } 1184 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 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 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