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