UsbSettingsManager.java revision fd7adedebf88427162a3ce27fcc9cfd3893c869d
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 boolean onPackageChanged(String packageName, int uid, String[] components) { 368 handlePackageUpdate(packageName); 369 return false; 370 } 371 372 @Override 373 public void onPackageRemoved(String packageName, int uid) { 374 clearDefaults(packageName); 375 } 376 } 377 378 MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); 379 380 public UsbSettingsManager(Context context, UserHandle user) { 381 if (DEBUG) Slog.v(TAG, "Creating settings for " + user); 382 383 try { 384 mUserContext = context.createPackageContextAsUser("android", 0, user); 385 } catch (NameNotFoundException e) { 386 throw new RuntimeException("Missing android package"); 387 } 388 389 mContext = context; 390 mPackageManager = mUserContext.getPackageManager(); 391 392 mUser = user; 393 mSettingsFile = new AtomicFile(new File( 394 Environment.getUserSystemDirectory(user.getIdentifier()), 395 "usb_device_manager.xml")); 396 397 synchronized (mLock) { 398 if (UserHandle.OWNER.equals(user)) { 399 upgradeSingleUserLocked(); 400 } 401 readSettingsLocked(); 402 } 403 404 mPackageMonitor.register(mUserContext, null, true); 405 } 406 407 private void readPreference(XmlPullParser parser) 408 throws XmlPullParserException, IOException { 409 String packageName = null; 410 int count = parser.getAttributeCount(); 411 for (int i = 0; i < count; i++) { 412 if ("package".equals(parser.getAttributeName(i))) { 413 packageName = parser.getAttributeValue(i); 414 break; 415 } 416 } 417 XmlUtils.nextElement(parser); 418 if ("usb-device".equals(parser.getName())) { 419 DeviceFilter filter = DeviceFilter.read(parser); 420 mDevicePreferenceMap.put(filter, packageName); 421 } else if ("usb-accessory".equals(parser.getName())) { 422 AccessoryFilter filter = AccessoryFilter.read(parser); 423 mAccessoryPreferenceMap.put(filter, packageName); 424 } 425 XmlUtils.nextElement(parser); 426 } 427 428 /** 429 * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}. 430 * Should only by called by owner. 431 */ 432 private void upgradeSingleUserLocked() { 433 if (sSingleUserSettingsFile.exists()) { 434 mDevicePreferenceMap.clear(); 435 mAccessoryPreferenceMap.clear(); 436 437 FileInputStream fis = null; 438 try { 439 fis = new FileInputStream(sSingleUserSettingsFile); 440 XmlPullParser parser = Xml.newPullParser(); 441 parser.setInput(fis, null); 442 443 XmlUtils.nextElement(parser); 444 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 445 final String tagName = parser.getName(); 446 if ("preference".equals(tagName)) { 447 readPreference(parser); 448 } else { 449 XmlUtils.nextElement(parser); 450 } 451 } 452 } catch (IOException e) { 453 Log.wtf(TAG, "Failed to read single-user settings", e); 454 } catch (XmlPullParserException e) { 455 Log.wtf(TAG, "Failed to read single-user settings", e); 456 } finally { 457 IoUtils.closeQuietly(fis); 458 } 459 460 writeSettingsLocked(); 461 462 // Success or failure, we delete single-user file 463 sSingleUserSettingsFile.delete(); 464 } 465 } 466 467 private void readSettingsLocked() { 468 if (DEBUG) Slog.v(TAG, "readSettingsLocked()"); 469 470 mDevicePreferenceMap.clear(); 471 mAccessoryPreferenceMap.clear(); 472 473 FileInputStream stream = null; 474 try { 475 stream = mSettingsFile.openRead(); 476 XmlPullParser parser = Xml.newPullParser(); 477 parser.setInput(stream, null); 478 479 XmlUtils.nextElement(parser); 480 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 481 String tagName = parser.getName(); 482 if ("preference".equals(tagName)) { 483 readPreference(parser); 484 } else { 485 XmlUtils.nextElement(parser); 486 } 487 } 488 } catch (FileNotFoundException e) { 489 if (DEBUG) Slog.d(TAG, "settings file not found"); 490 } catch (Exception e) { 491 Slog.e(TAG, "error reading settings file, deleting to start fresh", e); 492 mSettingsFile.delete(); 493 } finally { 494 IoUtils.closeQuietly(stream); 495 } 496 } 497 498 private void writeSettingsLocked() { 499 if (DEBUG) Slog.v(TAG, "writeSettingsLocked()"); 500 501 FileOutputStream fos = null; 502 try { 503 fos = mSettingsFile.startWrite(); 504 505 FastXmlSerializer serializer = new FastXmlSerializer(); 506 serializer.setOutput(fos, "utf-8"); 507 serializer.startDocument(null, true); 508 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 509 serializer.startTag(null, "settings"); 510 511 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { 512 serializer.startTag(null, "preference"); 513 serializer.attribute(null, "package", mDevicePreferenceMap.get(filter)); 514 filter.write(serializer); 515 serializer.endTag(null, "preference"); 516 } 517 518 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { 519 serializer.startTag(null, "preference"); 520 serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter)); 521 filter.write(serializer); 522 serializer.endTag(null, "preference"); 523 } 524 525 serializer.endTag(null, "settings"); 526 serializer.endDocument(); 527 528 mSettingsFile.finishWrite(fos); 529 } catch (IOException e) { 530 Slog.e(TAG, "Failed to write settings", e); 531 if (fos != null) { 532 mSettingsFile.failWrite(fos); 533 } 534 } 535 } 536 537 // Checks to see if a package matches a device or accessory. 538 // Only one of device and accessory should be non-null. 539 private boolean packageMatchesLocked(ResolveInfo info, String metaDataName, 540 UsbDevice device, UsbAccessory accessory) { 541 ActivityInfo ai = info.activityInfo; 542 543 XmlResourceParser parser = null; 544 try { 545 parser = ai.loadXmlMetaData(mPackageManager, metaDataName); 546 if (parser == null) { 547 Slog.w(TAG, "no meta-data for " + info); 548 return false; 549 } 550 551 XmlUtils.nextElement(parser); 552 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 553 String tagName = parser.getName(); 554 if (device != null && "usb-device".equals(tagName)) { 555 DeviceFilter filter = DeviceFilter.read(parser); 556 if (filter.matches(device)) { 557 return true; 558 } 559 } 560 else if (accessory != null && "usb-accessory".equals(tagName)) { 561 AccessoryFilter filter = AccessoryFilter.read(parser); 562 if (filter.matches(accessory)) { 563 return true; 564 } 565 } 566 XmlUtils.nextElement(parser); 567 } 568 } catch (Exception e) { 569 Slog.w(TAG, "Unable to load component info " + info.toString(), e); 570 } finally { 571 if (parser != null) parser.close(); 572 } 573 return false; 574 } 575 576 private final ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) { 577 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); 578 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent, 579 PackageManager.GET_META_DATA); 580 int count = resolveInfos.size(); 581 for (int i = 0; i < count; i++) { 582 ResolveInfo resolveInfo = resolveInfos.get(i); 583 if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) { 584 matches.add(resolveInfo); 585 } 586 } 587 return matches; 588 } 589 590 private final ArrayList<ResolveInfo> getAccessoryMatchesLocked( 591 UsbAccessory accessory, Intent intent) { 592 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); 593 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(intent, 594 PackageManager.GET_META_DATA); 595 int count = resolveInfos.size(); 596 for (int i = 0; i < count; i++) { 597 ResolveInfo resolveInfo = resolveInfos.get(i); 598 if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) { 599 matches.add(resolveInfo); 600 } 601 } 602 return matches; 603 } 604 605 public void deviceAttached(UsbDevice device) { 606 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); 607 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 608 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 609 610 ArrayList<ResolveInfo> matches; 611 String defaultPackage; 612 synchronized (mLock) { 613 matches = getDeviceMatchesLocked(device, intent); 614 // Launch our default activity directly, if we have one. 615 // Otherwise we will start the UsbResolverActivity to allow the user to choose. 616 defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device)); 617 } 618 619 // Send broadcast to running activity with registered intent 620 mUserContext.sendBroadcast(intent); 621 622 // Start activity with registered intent 623 resolveActivity(intent, matches, defaultPackage, device, null); 624 } 625 626 public void deviceDetached(UsbDevice device) { 627 // clear temporary permissions for the device 628 mDevicePermissionMap.remove(device.getDeviceName()); 629 630 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); 631 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 632 if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent); 633 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 634 } 635 636 public void accessoryAttached(UsbAccessory accessory) { 637 Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); 638 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 639 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 640 641 ArrayList<ResolveInfo> matches; 642 String defaultPackage; 643 synchronized (mLock) { 644 matches = getAccessoryMatchesLocked(accessory, intent); 645 // Launch our default activity directly, if we have one. 646 // Otherwise we will start the UsbResolverActivity to allow the user to choose. 647 defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)); 648 } 649 650 resolveActivity(intent, matches, defaultPackage, null, accessory); 651 } 652 653 public void accessoryDetached(UsbAccessory accessory) { 654 // clear temporary permissions for the accessory 655 mAccessoryPermissionMap.remove(accessory); 656 657 Intent intent = new Intent( 658 UsbManager.ACTION_USB_ACCESSORY_DETACHED); 659 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 660 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 661 } 662 663 private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches, 664 String defaultPackage, UsbDevice device, UsbAccessory accessory) { 665 int count = matches.size(); 666 667 // don't show the resolver activity if there are no choices available 668 if (count == 0) { 669 if (accessory != null) { 670 String uri = accessory.getUri(); 671 if (uri != null && uri.length() > 0) { 672 // display URI to user 673 // start UsbResolverActivity so user can choose an activity 674 Intent dialogIntent = new Intent(); 675 dialogIntent.setClassName("com.android.systemui", 676 "com.android.systemui.usb.UsbAccessoryUriActivity"); 677 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 678 dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 679 dialogIntent.putExtra("uri", uri); 680 try { 681 mUserContext.startActivityAsUser(dialogIntent, mUser); 682 } catch (ActivityNotFoundException e) { 683 Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); 684 } 685 } 686 } 687 688 // do nothing 689 return; 690 } 691 692 ResolveInfo defaultRI = null; 693 if (count == 1 && defaultPackage == null) { 694 // Check to see if our single choice is on the system partition. 695 // If so, treat it as our default without calling UsbResolverActivity 696 ResolveInfo rInfo = matches.get(0); 697 if (rInfo.activityInfo != null && 698 rInfo.activityInfo.applicationInfo != null && 699 (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 700 defaultRI = rInfo; 701 } 702 } 703 704 if (defaultRI == null && defaultPackage != null) { 705 // look for default activity 706 for (int i = 0; i < count; i++) { 707 ResolveInfo rInfo = matches.get(i); 708 if (rInfo.activityInfo != null && 709 defaultPackage.equals(rInfo.activityInfo.packageName)) { 710 defaultRI = rInfo; 711 break; 712 } 713 } 714 } 715 716 if (defaultRI != null) { 717 // grant permission for default activity 718 if (device != null) { 719 grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid); 720 } else if (accessory != null) { 721 grantAccessoryPermission(accessory, defaultRI.activityInfo.applicationInfo.uid); 722 } 723 724 // start default activity directly 725 try { 726 intent.setComponent( 727 new ComponentName(defaultRI.activityInfo.packageName, 728 defaultRI.activityInfo.name)); 729 mUserContext.startActivityAsUser(intent, mUser); 730 } catch (ActivityNotFoundException e) { 731 Slog.e(TAG, "startActivity failed", e); 732 } 733 } else { 734 Intent resolverIntent = new Intent(); 735 resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 736 737 if (count == 1) { 738 // start UsbConfirmActivity if there is only one choice 739 resolverIntent.setClassName("com.android.systemui", 740 "com.android.systemui.usb.UsbConfirmActivity"); 741 resolverIntent.putExtra("rinfo", matches.get(0)); 742 743 if (device != null) { 744 resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device); 745 } else { 746 resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 747 } 748 } else { 749 // start UsbResolverActivity so user can choose an activity 750 resolverIntent.setClassName("com.android.systemui", 751 "com.android.systemui.usb.UsbResolverActivity"); 752 resolverIntent.putParcelableArrayListExtra("rlist", matches); 753 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); 754 } 755 try { 756 mUserContext.startActivityAsUser(resolverIntent, mUser); 757 } catch (ActivityNotFoundException e) { 758 Slog.e(TAG, "unable to start activity " + resolverIntent); 759 } 760 } 761 } 762 763 private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) { 764 boolean changed = false; 765 for (DeviceFilter test : mDevicePreferenceMap.keySet()) { 766 if (filter.matches(test)) { 767 mDevicePreferenceMap.remove(test); 768 changed = true; 769 } 770 } 771 return changed; 772 } 773 774 private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) { 775 boolean changed = false; 776 for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) { 777 if (filter.matches(test)) { 778 mAccessoryPreferenceMap.remove(test); 779 changed = true; 780 } 781 } 782 return changed; 783 } 784 785 private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo, 786 String metaDataName) { 787 XmlResourceParser parser = null; 788 boolean changed = false; 789 790 try { 791 parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName); 792 if (parser == null) return false; 793 794 XmlUtils.nextElement(parser); 795 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 796 String tagName = parser.getName(); 797 if ("usb-device".equals(tagName)) { 798 DeviceFilter filter = DeviceFilter.read(parser); 799 if (clearCompatibleMatchesLocked(packageName, filter)) { 800 changed = true; 801 } 802 } 803 else if ("usb-accessory".equals(tagName)) { 804 AccessoryFilter filter = AccessoryFilter.read(parser); 805 if (clearCompatibleMatchesLocked(packageName, filter)) { 806 changed = true; 807 } 808 } 809 XmlUtils.nextElement(parser); 810 } 811 } catch (Exception e) { 812 Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); 813 } finally { 814 if (parser != null) parser.close(); 815 } 816 return changed; 817 } 818 819 // Check to see if the package supports any USB devices or accessories. 820 // If so, clear any non-matching preferences for matching devices/accessories. 821 private void handlePackageUpdate(String packageName) { 822 synchronized (mLock) { 823 PackageInfo info; 824 boolean changed = false; 825 826 try { 827 info = mPackageManager.getPackageInfo(packageName, 828 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); 829 } catch (NameNotFoundException e) { 830 Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); 831 return; 832 } 833 834 ActivityInfo[] activities = info.activities; 835 if (activities == null) return; 836 for (int i = 0; i < activities.length; i++) { 837 // check for meta-data, both for devices and accessories 838 if (handlePackageUpdateLocked(packageName, activities[i], 839 UsbManager.ACTION_USB_DEVICE_ATTACHED)) { 840 changed = true; 841 } 842 if (handlePackageUpdateLocked(packageName, activities[i], 843 UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { 844 changed = true; 845 } 846 } 847 848 if (changed) { 849 writeSettingsLocked(); 850 } 851 } 852 } 853 854 public boolean hasPermission(UsbDevice device) { 855 synchronized (mLock) { 856 SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); 857 if (uidList == null) { 858 return false; 859 } 860 return uidList.get(Binder.getCallingUid()); 861 } 862 } 863 864 public boolean hasPermission(UsbAccessory accessory) { 865 synchronized (mLock) { 866 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 867 if (uidList == null) { 868 return false; 869 } 870 return uidList.get(Binder.getCallingUid()); 871 } 872 } 873 874 public void checkPermission(UsbDevice device) { 875 if (!hasPermission(device)) { 876 throw new SecurityException("User has not given permission to device " + device); 877 } 878 } 879 880 public void checkPermission(UsbAccessory accessory) { 881 if (!hasPermission(accessory)) { 882 throw new SecurityException("User has not given permission to accessory " + accessory); 883 } 884 } 885 886 private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { 887 final int uid = Binder.getCallingUid(); 888 889 // compare uid with packageName to foil apps pretending to be someone else 890 try { 891 ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0); 892 if (aInfo.uid != uid) { 893 throw new IllegalArgumentException("package " + packageName + 894 " does not match caller's uid " + uid); 895 } 896 } catch (PackageManager.NameNotFoundException e) { 897 throw new IllegalArgumentException("package " + packageName + " not found"); 898 } 899 900 long identity = Binder.clearCallingIdentity(); 901 intent.setClassName("com.android.systemui", 902 "com.android.systemui.usb.UsbPermissionActivity"); 903 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 904 intent.putExtra(Intent.EXTRA_INTENT, pi); 905 intent.putExtra("package", packageName); 906 intent.putExtra(Intent.EXTRA_UID, uid); 907 try { 908 mUserContext.startActivityAsUser(intent, mUser); 909 } catch (ActivityNotFoundException e) { 910 Slog.e(TAG, "unable to start UsbPermissionActivity"); 911 } finally { 912 Binder.restoreCallingIdentity(identity); 913 } 914 } 915 916 public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { 917 Intent intent = new Intent(); 918 919 // respond immediately if permission has already been granted 920 if (hasPermission(device)) { 921 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 922 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 923 try { 924 pi.send(mUserContext, 0, intent); 925 } catch (PendingIntent.CanceledException e) { 926 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 927 } 928 return; 929 } 930 931 // start UsbPermissionActivity so user can choose an activity 932 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 933 requestPermissionDialog(intent, packageName, pi); 934 } 935 936 public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) { 937 Intent intent = new Intent(); 938 939 // respond immediately if permission has already been granted 940 if (hasPermission(accessory)) { 941 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 942 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 943 try { 944 pi.send(mUserContext, 0, intent); 945 } catch (PendingIntent.CanceledException e) { 946 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 947 } 948 return; 949 } 950 951 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 952 requestPermissionDialog(intent, packageName, pi); 953 } 954 955 public void setDevicePackage(UsbDevice device, String packageName) { 956 DeviceFilter filter = new DeviceFilter(device); 957 boolean changed = false; 958 synchronized (mLock) { 959 if (packageName == null) { 960 changed = (mDevicePreferenceMap.remove(filter) != null); 961 } else { 962 changed = !packageName.equals(mDevicePreferenceMap.get(filter)); 963 if (changed) { 964 mDevicePreferenceMap.put(filter, packageName); 965 } 966 } 967 if (changed) { 968 writeSettingsLocked(); 969 } 970 } 971 } 972 973 public void setAccessoryPackage(UsbAccessory accessory, String packageName) { 974 AccessoryFilter filter = new AccessoryFilter(accessory); 975 boolean changed = false; 976 synchronized (mLock) { 977 if (packageName == null) { 978 changed = (mAccessoryPreferenceMap.remove(filter) != null); 979 } else { 980 changed = !packageName.equals(mAccessoryPreferenceMap.get(filter)); 981 if (changed) { 982 mAccessoryPreferenceMap.put(filter, packageName); 983 } 984 } 985 if (changed) { 986 writeSettingsLocked(); 987 } 988 } 989 } 990 991 public void grantDevicePermission(UsbDevice device, int uid) { 992 synchronized (mLock) { 993 String deviceName = device.getDeviceName(); 994 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 995 if (uidList == null) { 996 uidList = new SparseBooleanArray(1); 997 mDevicePermissionMap.put(deviceName, uidList); 998 } 999 uidList.put(uid, true); 1000 } 1001 } 1002 1003 public void grantAccessoryPermission(UsbAccessory accessory, int uid) { 1004 synchronized (mLock) { 1005 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 1006 if (uidList == null) { 1007 uidList = new SparseBooleanArray(1); 1008 mAccessoryPermissionMap.put(accessory, uidList); 1009 } 1010 uidList.put(uid, true); 1011 } 1012 } 1013 1014 public boolean hasDefaults(String packageName) { 1015 synchronized (mLock) { 1016 if (mDevicePreferenceMap.values().contains(packageName)) return true; 1017 if (mAccessoryPreferenceMap.values().contains(packageName)) return true; 1018 return false; 1019 } 1020 } 1021 1022 public void clearDefaults(String packageName) { 1023 synchronized (mLock) { 1024 if (clearPackageDefaultsLocked(packageName)) { 1025 writeSettingsLocked(); 1026 } 1027 } 1028 } 1029 1030 private boolean clearPackageDefaultsLocked(String packageName) { 1031 boolean cleared = false; 1032 synchronized (mLock) { 1033 if (mDevicePreferenceMap.containsValue(packageName)) { 1034 // make a copy of the key set to avoid ConcurrentModificationException 1035 Object[] keys = mDevicePreferenceMap.keySet().toArray(); 1036 for (int i = 0; i < keys.length; i++) { 1037 Object key = keys[i]; 1038 if (packageName.equals(mDevicePreferenceMap.get(key))) { 1039 mDevicePreferenceMap.remove(key); 1040 cleared = true; 1041 } 1042 } 1043 } 1044 if (mAccessoryPreferenceMap.containsValue(packageName)) { 1045 // make a copy of the key set to avoid ConcurrentModificationException 1046 Object[] keys = mAccessoryPreferenceMap.keySet().toArray(); 1047 for (int i = 0; i < keys.length; i++) { 1048 Object key = keys[i]; 1049 if (packageName.equals(mAccessoryPreferenceMap.get(key))) { 1050 mAccessoryPreferenceMap.remove(key); 1051 cleared = true; 1052 } 1053 } 1054 } 1055 return cleared; 1056 } 1057 } 1058 1059 public void dump(FileDescriptor fd, PrintWriter pw) { 1060 synchronized (mLock) { 1061 pw.println(" Device permissions:"); 1062 for (String deviceName : mDevicePermissionMap.keySet()) { 1063 pw.print(" " + deviceName + ": "); 1064 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 1065 int count = uidList.size(); 1066 for (int i = 0; i < count; i++) { 1067 pw.print(Integer.toString(uidList.keyAt(i)) + " "); 1068 } 1069 pw.println(""); 1070 } 1071 pw.println(" Accessory permissions:"); 1072 for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { 1073 pw.print(" " + accessory + ": "); 1074 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 1075 int count = uidList.size(); 1076 for (int i = 0; i < count; i++) { 1077 pw.print(Integer.toString(uidList.keyAt(i)) + " "); 1078 } 1079 pw.println(""); 1080 } 1081 pw.println(" Device preferences:"); 1082 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { 1083 pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter)); 1084 } 1085 pw.println(" Accessory preferences:"); 1086 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { 1087 pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter)); 1088 } 1089 } 1090 } 1091} 1092