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