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