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