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