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