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