1 /* 2 * Copyright (C) 2016 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 static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.ActivityNotFoundException; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ActivityInfo; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.content.pm.PackageManager.NameNotFoundException; 32 import android.content.pm.ResolveInfo; 33 import android.content.pm.UserInfo; 34 import android.content.res.XmlResourceParser; 35 import android.hardware.usb.UsbAccessory; 36 import android.hardware.usb.UsbDevice; 37 import android.hardware.usb.UsbInterface; 38 import android.hardware.usb.UsbManager; 39 import android.os.AsyncTask; 40 import android.os.Environment; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.util.AtomicFile; 44 import android.util.Log; 45 import android.util.Slog; 46 import android.util.SparseArray; 47 import android.util.SparseIntArray; 48 import android.util.Xml; 49 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.annotations.Immutable; 52 import com.android.internal.content.PackageMonitor; 53 import com.android.internal.util.FastXmlSerializer; 54 import com.android.internal.util.IndentingPrintWriter; 55 import com.android.internal.util.XmlUtils; 56 57 import libcore.io.IoUtils; 58 59 import org.xmlpull.v1.XmlPullParser; 60 import org.xmlpull.v1.XmlPullParserException; 61 import org.xmlpull.v1.XmlSerializer; 62 63 import java.io.File; 64 import java.io.FileInputStream; 65 import java.io.FileNotFoundException; 66 import java.io.FileOutputStream; 67 import java.io.IOException; 68 import java.nio.charset.StandardCharsets; 69 import java.util.ArrayList; 70 import java.util.HashMap; 71 import java.util.Iterator; 72 import java.util.List; 73 import java.util.Map; 74 import java.util.Objects; 75 76 class UsbProfileGroupSettingsManager { 77 private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName(); 78 private static final boolean DEBUG = false; 79 80 /** Legacy settings file, before multi-user */ 81 private static final File sSingleUserSettingsFile = new File( 82 "/data/system/usb_device_manager.xml"); 83 84 /** The parent user (main user of the profile group) */ 85 private final UserHandle mParentUser; 86 87 private final AtomicFile mSettingsFile; 88 private final boolean mDisablePermissionDialogs; 89 90 private final Context mContext; 91 92 private final PackageManager mPackageManager; 93 94 private final UserManager mUserManager; 95 private final @NonNull UsbSettingsManager mSettingsManager; 96 97 /** Maps DeviceFilter to user preferred application package */ 98 @GuardedBy("mLock") 99 private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>(); 100 101 /** Maps AccessoryFilter to user preferred application package */ 102 @GuardedBy("mLock") 103 private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>(); 104 105 private final Object mLock = new Object(); 106 107 /** 108 * If a async task to persist the mDevicePreferenceMap and mAccessoryPreferenceMap is currently 109 * scheduled. 110 */ 111 @GuardedBy("mLock") 112 private boolean mIsWriteSettingsScheduled; 113 114 /** 115 * A package of a user. 116 */ 117 @Immutable 118 private static class UserPackage { 119 /** User */ 120 final @NonNull UserHandle user; 121 122 /** Package name */ 123 final @NonNull String packageName; 124 125 /** 126 * Create a description of a per user package. 127 * 128 * @param packageName The name of the package 129 * @param user The user 130 */ 131 private UserPackage(@NonNull String packageName, @NonNull UserHandle user) { 132 this.packageName = packageName; 133 this.user = user; 134 } 135 136 @Override 137 public boolean equals(Object obj) { 138 if (!(obj instanceof UserPackage)) { 139 return false; 140 } else { 141 UserPackage other = (UserPackage)obj; 142 143 return user.equals(other.user) && packageName.equals(other.packageName); 144 } 145 } 146 147 @Override 148 public int hashCode() { 149 int result = user.hashCode(); 150 result = 31 * result + packageName.hashCode(); 151 return result; 152 } 153 154 @Override 155 public String toString() { 156 return user.getIdentifier() + "/" + packageName; 157 } 158 } 159 160 // This class is used to describe a USB device. 161 // When used in HashMaps all values must be specified, 162 // but wildcards can be used for any of the fields in 163 // the package meta-data. 164 private static class DeviceFilter { 165 // USB Vendor ID (or -1 for unspecified) 166 public final int mVendorId; 167 // USB Product ID (or -1 for unspecified) 168 public final int mProductId; 169 // USB device or interface class (or -1 for unspecified) 170 public final int mClass; 171 // USB device subclass (or -1 for unspecified) 172 public final int mSubclass; 173 // USB device protocol (or -1 for unspecified) 174 public final int mProtocol; 175 // USB device manufacturer name string (or null for unspecified) 176 public final String mManufacturerName; 177 // USB device product name string (or null for unspecified) 178 public final String mProductName; 179 // USB device serial number string (or null for unspecified) 180 public final String mSerialNumber; 181 182 public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, 183 String manufacturer, String product, String serialnum) { 184 mVendorId = vid; 185 mProductId = pid; 186 mClass = clasz; 187 mSubclass = subclass; 188 mProtocol = protocol; 189 mManufacturerName = manufacturer; 190 mProductName = product; 191 mSerialNumber = serialnum; 192 } 193 194 public DeviceFilter(UsbDevice device) { 195 mVendorId = device.getVendorId(); 196 mProductId = device.getProductId(); 197 mClass = device.getDeviceClass(); 198 mSubclass = device.getDeviceSubclass(); 199 mProtocol = device.getDeviceProtocol(); 200 mManufacturerName = device.getManufacturerName(); 201 mProductName = device.getProductName(); 202 mSerialNumber = device.getSerialNumber(); 203 } 204 205 public static DeviceFilter read(XmlPullParser parser) 206 throws XmlPullParserException, IOException { 207 int vendorId = -1; 208 int productId = -1; 209 int deviceClass = -1; 210 int deviceSubclass = -1; 211 int deviceProtocol = -1; 212 String manufacturerName = null; 213 String productName = null; 214 String serialNumber = null; 215 216 int count = parser.getAttributeCount(); 217 for (int i = 0; i < count; i++) { 218 String name = parser.getAttributeName(i); 219 String value = parser.getAttributeValue(i); 220 // Attribute values are ints or strings 221 if ("manufacturer-name".equals(name)) { 222 manufacturerName = value; 223 } else if ("product-name".equals(name)) { 224 productName = value; 225 } else if ("serial-number".equals(name)) { 226 serialNumber = value; 227 } else { 228 int intValue; 229 int radix = 10; 230 if (value != null && value.length() > 2 && value.charAt(0) == '0' && 231 (value.charAt(1) == 'x' || value.charAt(1) == 'X')) { 232 // allow hex values starting with 0x or 0X 233 radix = 16; 234 value = value.substring(2); 235 } 236 try { 237 intValue = Integer.parseInt(value, radix); 238 } catch (NumberFormatException e) { 239 Slog.e(TAG, "invalid number for field " + name, e); 240 continue; 241 } 242 if ("vendor-id".equals(name)) { 243 vendorId = intValue; 244 } else if ("product-id".equals(name)) { 245 productId = intValue; 246 } else if ("class".equals(name)) { 247 deviceClass = intValue; 248 } else if ("subclass".equals(name)) { 249 deviceSubclass = intValue; 250 } else if ("protocol".equals(name)) { 251 deviceProtocol = intValue; 252 } 253 } 254 } 255 return new DeviceFilter(vendorId, productId, 256 deviceClass, deviceSubclass, deviceProtocol, 257 manufacturerName, productName, serialNumber); 258 } 259 260 public void write(XmlSerializer serializer) throws IOException { 261 serializer.startTag(null, "usb-device"); 262 if (mVendorId != -1) { 263 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); 264 } 265 if (mProductId != -1) { 266 serializer.attribute(null, "product-id", Integer.toString(mProductId)); 267 } 268 if (mClass != -1) { 269 serializer.attribute(null, "class", Integer.toString(mClass)); 270 } 271 if (mSubclass != -1) { 272 serializer.attribute(null, "subclass", Integer.toString(mSubclass)); 273 } 274 if (mProtocol != -1) { 275 serializer.attribute(null, "protocol", Integer.toString(mProtocol)); 276 } 277 if (mManufacturerName != null) { 278 serializer.attribute(null, "manufacturer-name", mManufacturerName); 279 } 280 if (mProductName != null) { 281 serializer.attribute(null, "product-name", mProductName); 282 } 283 if (mSerialNumber != null) { 284 serializer.attribute(null, "serial-number", mSerialNumber); 285 } 286 serializer.endTag(null, "usb-device"); 287 } 288 289 private boolean matches(int clasz, int subclass, int protocol) { 290 return ((mClass == -1 || clasz == mClass) && 291 (mSubclass == -1 || subclass == mSubclass) && 292 (mProtocol == -1 || protocol == mProtocol)); 293 } 294 295 public boolean matches(UsbDevice device) { 296 if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; 297 if (mProductId != -1 && device.getProductId() != mProductId) return false; 298 if (mManufacturerName != null && device.getManufacturerName() == null) return false; 299 if (mProductName != null && device.getProductName() == null) return false; 300 if (mSerialNumber != null && device.getSerialNumber() == null) return false; 301 if (mManufacturerName != null && device.getManufacturerName() != null && 302 !mManufacturerName.equals(device.getManufacturerName())) return false; 303 if (mProductName != null && device.getProductName() != null && 304 !mProductName.equals(device.getProductName())) return false; 305 if (mSerialNumber != null && device.getSerialNumber() != null && 306 !mSerialNumber.equals(device.getSerialNumber())) return false; 307 308 // check device class/subclass/protocol 309 if (matches(device.getDeviceClass(), device.getDeviceSubclass(), 310 device.getDeviceProtocol())) return true; 311 312 // if device doesn't match, check the interfaces 313 int count = device.getInterfaceCount(); 314 for (int i = 0; i < count; i++) { 315 UsbInterface intf = device.getInterface(i); 316 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), 317 intf.getInterfaceProtocol())) return true; 318 } 319 320 return false; 321 } 322 323 /** 324 * If the device described by {@code device} covered by this filter? 325 * 326 * @param device The device 327 * 328 * @return {@code true} iff this filter covers the {@code device} 329 */ 330 public boolean contains(DeviceFilter device) { 331 // -1 and null means "match anything" 332 333 if (mVendorId != -1 && device.mVendorId != mVendorId) return false; 334 if (mProductId != -1 && device.mProductId != mProductId) return false; 335 if (mManufacturerName != null && !Objects.equals(mManufacturerName, 336 device.mManufacturerName)) { 337 return false; 338 } 339 if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) { 340 return false; 341 } 342 if (mSerialNumber != null 343 && !Objects.equals(mSerialNumber, device.mSerialNumber)) { 344 return false; 345 } 346 347 // check device class/subclass/protocol 348 return matches(device.mClass, device.mSubclass, device.mProtocol); 349 } 350 351 @Override 352 public boolean equals(Object obj) { 353 // can't compare if we have wildcard strings 354 if (mVendorId == -1 || mProductId == -1 || 355 mClass == -1 || mSubclass == -1 || mProtocol == -1) { 356 return false; 357 } 358 if (obj instanceof DeviceFilter) { 359 DeviceFilter filter = (DeviceFilter)obj; 360 361 if (filter.mVendorId != mVendorId || 362 filter.mProductId != mProductId || 363 filter.mClass != mClass || 364 filter.mSubclass != mSubclass || 365 filter.mProtocol != mProtocol) { 366 return(false); 367 } 368 if ((filter.mManufacturerName != null && 369 mManufacturerName == null) || 370 (filter.mManufacturerName == null && 371 mManufacturerName != null) || 372 (filter.mProductName != null && 373 mProductName == null) || 374 (filter.mProductName == null && 375 mProductName != null) || 376 (filter.mSerialNumber != null && 377 mSerialNumber == null) || 378 (filter.mSerialNumber == null && 379 mSerialNumber != null)) { 380 return(false); 381 } 382 if ((filter.mManufacturerName != null && 383 mManufacturerName != null && 384 !mManufacturerName.equals(filter.mManufacturerName)) || 385 (filter.mProductName != null && 386 mProductName != null && 387 !mProductName.equals(filter.mProductName)) || 388 (filter.mSerialNumber != null && 389 mSerialNumber != null && 390 !mSerialNumber.equals(filter.mSerialNumber))) { 391 return false; 392 } 393 return true; 394 } 395 if (obj instanceof UsbDevice) { 396 UsbDevice device = (UsbDevice)obj; 397 if (device.getVendorId() != mVendorId || 398 device.getProductId() != mProductId || 399 device.getDeviceClass() != mClass || 400 device.getDeviceSubclass() != mSubclass || 401 device.getDeviceProtocol() != mProtocol) { 402 return(false); 403 } 404 if ((mManufacturerName != null && device.getManufacturerName() == null) || 405 (mManufacturerName == null && device.getManufacturerName() != null) || 406 (mProductName != null && device.getProductName() == null) || 407 (mProductName == null && device.getProductName() != null) || 408 (mSerialNumber != null && device.getSerialNumber() == null) || 409 (mSerialNumber == null && device.getSerialNumber() != null)) { 410 return(false); 411 } 412 if ((device.getManufacturerName() != null && 413 !mManufacturerName.equals(device.getManufacturerName())) || 414 (device.getProductName() != null && 415 !mProductName.equals(device.getProductName())) || 416 (device.getSerialNumber() != null && 417 !mSerialNumber.equals(device.getSerialNumber()))) { 418 return false; 419 } 420 return true; 421 } 422 return false; 423 } 424 425 @Override 426 public int hashCode() { 427 return (((mVendorId << 16) | mProductId) ^ 428 ((mClass << 16) | (mSubclass << 8) | mProtocol)); 429 } 430 431 @Override 432 public String toString() { 433 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + 434 ",mClass=" + mClass + ",mSubclass=" + mSubclass + 435 ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + 436 ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + 437 "]"; 438 } 439 } 440 441 // This class is used to describe a USB accessory. 442 // When used in HashMaps all values must be specified, 443 // but wildcards can be used for any of the fields in 444 // the package meta-data. 445 private static class AccessoryFilter { 446 // USB accessory manufacturer (or null for unspecified) 447 public final String mManufacturer; 448 // USB accessory model (or null for unspecified) 449 public final String mModel; 450 // USB accessory version (or null for unspecified) 451 public final String mVersion; 452 453 public AccessoryFilter(String manufacturer, String model, String version) { 454 mManufacturer = manufacturer; 455 mModel = model; 456 mVersion = version; 457 } 458 459 public AccessoryFilter(UsbAccessory accessory) { 460 mManufacturer = accessory.getManufacturer(); 461 mModel = accessory.getModel(); 462 mVersion = accessory.getVersion(); 463 } 464 465 public static AccessoryFilter read(XmlPullParser parser) 466 throws XmlPullParserException, IOException { 467 String manufacturer = null; 468 String model = null; 469 String version = null; 470 471 int count = parser.getAttributeCount(); 472 for (int i = 0; i < count; i++) { 473 String name = parser.getAttributeName(i); 474 String value = parser.getAttributeValue(i); 475 476 if ("manufacturer".equals(name)) { 477 manufacturer = value; 478 } else if ("model".equals(name)) { 479 model = value; 480 } else if ("version".equals(name)) { 481 version = value; 482 } 483 } 484 return new AccessoryFilter(manufacturer, model, version); 485 } 486 487 public void write(XmlSerializer serializer)throws IOException { 488 serializer.startTag(null, "usb-accessory"); 489 if (mManufacturer != null) { 490 serializer.attribute(null, "manufacturer", mManufacturer); 491 } 492 if (mModel != null) { 493 serializer.attribute(null, "model", mModel); 494 } 495 if (mVersion != null) { 496 serializer.attribute(null, "version", mVersion); 497 } 498 serializer.endTag(null, "usb-accessory"); 499 } 500 501 public boolean matches(UsbAccessory acc) { 502 if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false; 503 if (mModel != null && !acc.getModel().equals(mModel)) return false; 504 return !(mVersion != null && !acc.getVersion().equals(mVersion)); 505 } 506 507 /** 508 * Is the accessories described {@code accessory} covered by this filter? 509 * 510 * @param accessory A filter describing the accessory 511 * 512 * @return {@code true} iff this the filter covers the accessory 513 */ 514 public boolean contains(AccessoryFilter accessory) { 515 if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) { 516 return false; 517 } 518 if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false; 519 return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion)); 520 } 521 522 @Override 523 public boolean equals(Object obj) { 524 // can't compare if we have wildcard strings 525 if (mManufacturer == null || mModel == null || mVersion == null) { 526 return false; 527 } 528 if (obj instanceof AccessoryFilter) { 529 AccessoryFilter filter = (AccessoryFilter)obj; 530 return (mManufacturer.equals(filter.mManufacturer) && 531 mModel.equals(filter.mModel) && 532 mVersion.equals(filter.mVersion)); 533 } 534 if (obj instanceof UsbAccessory) { 535 UsbAccessory accessory = (UsbAccessory)obj; 536 return (mManufacturer.equals(accessory.getManufacturer()) && 537 mModel.equals(accessory.getModel()) && 538 mVersion.equals(accessory.getVersion())); 539 } 540 return false; 541 } 542 543 @Override 544 public int hashCode() { 545 return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ 546 (mModel == null ? 0 : mModel.hashCode()) ^ 547 (mVersion == null ? 0 : mVersion.hashCode())); 548 } 549 550 @Override 551 public String toString() { 552 return "AccessoryFilter[mManufacturer=\"" + mManufacturer + 553 "\", mModel=\"" + mModel + 554 "\", mVersion=\"" + mVersion + "\"]"; 555 } 556 } 557 558 private class MyPackageMonitor extends PackageMonitor { 559 @Override 560 public void onPackageAdded(String packageName, int uid) { 561 if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(), 562 UserHandle.getUserId(uid))) { 563 return; 564 } 565 566 handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid))); 567 } 568 569 @Override 570 public void onPackageRemoved(String packageName, int uid) { 571 if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(), 572 UserHandle.getUserId(uid))) { 573 return; 574 } 575 576 clearDefaults(packageName, UserHandle.getUserHandleForUid(uid)); 577 } 578 } 579 580 MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); 581 582 private final MtpNotificationManager mMtpNotificationManager; 583 584 /** 585 * Create new settings manager for a profile group. 586 * 587 * @param context The context of the service 588 * @param user The parent profile 589 * @param settingsManager The settings manager of the service 590 */ 591 UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user, 592 @NonNull UsbSettingsManager settingsManager) { 593 if (DEBUG) Slog.v(TAG, "Creating settings for " + user); 594 595 Context parentUserContext; 596 try { 597 parentUserContext = context.createPackageContextAsUser("android", 0, user); 598 } catch (NameNotFoundException e) { 599 throw new RuntimeException("Missing android package"); 600 } 601 602 mContext = context; 603 mPackageManager = context.getPackageManager(); 604 mSettingsManager = settingsManager; 605 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 606 607 mParentUser = user; 608 mSettingsFile = new AtomicFile(new File( 609 Environment.getUserSystemDirectory(user.getIdentifier()), 610 "usb_device_manager.xml")); 611 612 mDisablePermissionDialogs = context.getResources().getBoolean( 613 com.android.internal.R.bool.config_disableUsbPermissionDialogs); 614 615 synchronized (mLock) { 616 if (UserHandle.SYSTEM.equals(user)) { 617 upgradeSingleUserLocked(); 618 } 619 readSettingsLocked(); 620 } 621 622 mPackageMonitor.register(context, null, UserHandle.ALL, true); 623 mMtpNotificationManager = new MtpNotificationManager( 624 parentUserContext, 625 device -> resolveActivity(createDeviceAttachedIntent(device), 626 device, false /* showMtpNotification */)); 627 } 628 629 /** 630 * Remove all defaults for a user. 631 * 632 * @param userToRemove The user the defaults belong to. 633 */ 634 void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) { 635 synchronized (mLock) { 636 boolean needToPersist = false; 637 Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap 638 .entrySet().iterator(); 639 while (devicePreferenceIt.hasNext()) { 640 Map.Entry<DeviceFilter, UserPackage> entry = devicePreferenceIt.next(); 641 642 if (entry.getValue().user.equals(userToRemove)) { 643 devicePreferenceIt.remove(); 644 needToPersist = true; 645 } 646 } 647 648 Iterator<Map.Entry<AccessoryFilter, UserPackage>> accessoryPreferenceIt = 649 mAccessoryPreferenceMap.entrySet().iterator(); 650 while (accessoryPreferenceIt.hasNext()) { 651 Map.Entry<AccessoryFilter, UserPackage> entry = accessoryPreferenceIt.next(); 652 653 if (entry.getValue().user.equals(userToRemove)) { 654 accessoryPreferenceIt.remove(); 655 needToPersist = true; 656 } 657 } 658 659 if (needToPersist) { 660 scheduleWriteSettingsLocked(); 661 } 662 } 663 } 664 665 private void readPreference(XmlPullParser parser) 666 throws XmlPullParserException, IOException { 667 String packageName = null; 668 669 // If not set, assume it to be the parent profile 670 UserHandle user = mParentUser; 671 672 int count = parser.getAttributeCount(); 673 for (int i = 0; i < count; i++) { 674 if ("package".equals(parser.getAttributeName(i))) { 675 packageName = parser.getAttributeValue(i); 676 } 677 if ("user".equals(parser.getAttributeName(i))) { 678 // Might return null if user is not known anymore 679 user = mUserManager 680 .getUserForSerialNumber(Integer.parseInt(parser.getAttributeValue(i))); 681 } 682 } 683 684 XmlUtils.nextElement(parser); 685 if ("usb-device".equals(parser.getName())) { 686 DeviceFilter filter = DeviceFilter.read(parser); 687 if (user != null) { 688 mDevicePreferenceMap.put(filter, new UserPackage(packageName, user)); 689 } 690 } else if ("usb-accessory".equals(parser.getName())) { 691 AccessoryFilter filter = AccessoryFilter.read(parser); 692 if (user != null) { 693 mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user)); 694 } 695 } 696 XmlUtils.nextElement(parser); 697 } 698 699 /** 700 * Upgrade any single-user settings from {@link #sSingleUserSettingsFile}. 701 * Should only by called by owner. 702 */ 703 private void upgradeSingleUserLocked() { 704 if (sSingleUserSettingsFile.exists()) { 705 mDevicePreferenceMap.clear(); 706 mAccessoryPreferenceMap.clear(); 707 708 FileInputStream fis = null; 709 try { 710 fis = new FileInputStream(sSingleUserSettingsFile); 711 XmlPullParser parser = Xml.newPullParser(); 712 parser.setInput(fis, StandardCharsets.UTF_8.name()); 713 714 XmlUtils.nextElement(parser); 715 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 716 final String tagName = parser.getName(); 717 if ("preference".equals(tagName)) { 718 readPreference(parser); 719 } else { 720 XmlUtils.nextElement(parser); 721 } 722 } 723 } catch (IOException | XmlPullParserException e) { 724 Log.wtf(TAG, "Failed to read single-user settings", e); 725 } finally { 726 IoUtils.closeQuietly(fis); 727 } 728 729 scheduleWriteSettingsLocked(); 730 731 // Success or failure, we delete single-user file 732 sSingleUserSettingsFile.delete(); 733 } 734 } 735 736 private void readSettingsLocked() { 737 if (DEBUG) Slog.v(TAG, "readSettingsLocked()"); 738 739 mDevicePreferenceMap.clear(); 740 mAccessoryPreferenceMap.clear(); 741 742 FileInputStream stream = null; 743 try { 744 stream = mSettingsFile.openRead(); 745 XmlPullParser parser = Xml.newPullParser(); 746 parser.setInput(stream, StandardCharsets.UTF_8.name()); 747 748 XmlUtils.nextElement(parser); 749 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 750 String tagName = parser.getName(); 751 if ("preference".equals(tagName)) { 752 readPreference(parser); 753 } else { 754 XmlUtils.nextElement(parser); 755 } 756 } 757 } catch (FileNotFoundException e) { 758 if (DEBUG) Slog.d(TAG, "settings file not found"); 759 } catch (Exception e) { 760 Slog.e(TAG, "error reading settings file, deleting to start fresh", e); 761 mSettingsFile.delete(); 762 } finally { 763 IoUtils.closeQuietly(stream); 764 } 765 } 766 767 /** 768 * Schedule a async task to persist {@link #mDevicePreferenceMap} and 769 * {@link #mAccessoryPreferenceMap}. If a task is already scheduled but not completed, do 770 * nothing as the currently scheduled one will do the work. 771 * <p>Called with {@link #mLock} held.</p> 772 * <p>In the uncommon case that the system crashes in between the scheduling and the write the 773 * update is lost.</p> 774 */ 775 private void scheduleWriteSettingsLocked() { 776 if (mIsWriteSettingsScheduled) { 777 return; 778 } else { 779 mIsWriteSettingsScheduled = true; 780 } 781 782 AsyncTask.execute(() -> { 783 synchronized (mLock) { 784 FileOutputStream fos = null; 785 try { 786 fos = mSettingsFile.startWrite(); 787 788 FastXmlSerializer serializer = new FastXmlSerializer(); 789 serializer.setOutput(fos, StandardCharsets.UTF_8.name()); 790 serializer.startDocument(null, true); 791 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", 792 true); 793 serializer.startTag(null, "settings"); 794 795 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { 796 serializer.startTag(null, "preference"); 797 serializer.attribute(null, "package", 798 mDevicePreferenceMap.get(filter).packageName); 799 serializer.attribute(null, "user", 800 String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user))); 801 filter.write(serializer); 802 serializer.endTag(null, "preference"); 803 } 804 805 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { 806 serializer.startTag(null, "preference"); 807 serializer.attribute(null, "package", 808 mAccessoryPreferenceMap.get(filter).packageName); 809 serializer.attribute(null, "user", String.valueOf( 810 getSerial(mAccessoryPreferenceMap.get(filter).user))); 811 filter.write(serializer); 812 serializer.endTag(null, "preference"); 813 } 814 815 serializer.endTag(null, "settings"); 816 serializer.endDocument(); 817 818 mSettingsFile.finishWrite(fos); 819 } catch (IOException e) { 820 Slog.e(TAG, "Failed to write settings", e); 821 if (fos != null) { 822 mSettingsFile.failWrite(fos); 823 } 824 } 825 826 mIsWriteSettingsScheduled = false; 827 } 828 }); 829 } 830 831 // Checks to see if a package matches a device or accessory. 832 // Only one of device and accessory should be non-null. 833 private boolean packageMatchesLocked(ResolveInfo info, String metaDataName, 834 UsbDevice device, UsbAccessory accessory) { 835 if (isForwardMatch(info)) { 836 return true; 837 } 838 839 ActivityInfo ai = info.activityInfo; 840 841 XmlResourceParser parser = null; 842 try { 843 parser = ai.loadXmlMetaData(mPackageManager, metaDataName); 844 if (parser == null) { 845 Slog.w(TAG, "no meta-data for " + info); 846 return false; 847 } 848 849 XmlUtils.nextElement(parser); 850 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 851 String tagName = parser.getName(); 852 if (device != null && "usb-device".equals(tagName)) { 853 DeviceFilter filter = DeviceFilter.read(parser); 854 if (filter.matches(device)) { 855 return true; 856 } 857 } 858 else if (accessory != null && "usb-accessory".equals(tagName)) { 859 AccessoryFilter filter = AccessoryFilter.read(parser); 860 if (filter.matches(accessory)) { 861 return true; 862 } 863 } 864 XmlUtils.nextElement(parser); 865 } 866 } catch (Exception e) { 867 Slog.w(TAG, "Unable to load component info " + info.toString(), e); 868 } finally { 869 if (parser != null) parser.close(); 870 } 871 return false; 872 } 873 874 /** 875 * Resolve all activities that match an intent for all profiles of this group. 876 * 877 * @param intent The intent to resolve 878 * 879 * @return The {@link ResolveInfo}s for all profiles of the group 880 */ 881 private @NonNull ArrayList<ResolveInfo> queryIntentActivitiesForAllProfiles( 882 @NonNull Intent intent) { 883 List<UserInfo> profiles = mUserManager.getEnabledProfiles(mParentUser.getIdentifier()); 884 885 ArrayList<ResolveInfo> resolveInfos = new ArrayList<>(); 886 int numProfiles = profiles.size(); 887 for (int i = 0; i < numProfiles; i++) { 888 resolveInfos.addAll(mPackageManager.queryIntentActivitiesAsUser(intent, 889 PackageManager.GET_META_DATA, profiles.get(i).id)); 890 } 891 892 return resolveInfos; 893 } 894 895 /** 896 * If this match used to forward the intent to another profile? 897 * 898 * @param match The match 899 * 900 * @return {@code true} iff this is such a forward match 901 */ 902 private boolean isForwardMatch(@NonNull ResolveInfo match) { 903 return match.getComponentInfo().name.equals(FORWARD_INTENT_TO_MANAGED_PROFILE); 904 } 905 906 /** 907 * Only return those matches with the highest priority. 908 * 909 * @param matches All matches, some might have lower priority 910 * 911 * @return The matches with the highest priority 912 */ 913 @NonNull 914 private ArrayList<ResolveInfo> preferHighPriority(@NonNull ArrayList<ResolveInfo> matches) { 915 SparseArray<ArrayList<ResolveInfo>> highestPriorityMatchesByUserId = new SparseArray<>(); 916 SparseIntArray highestPriorityByUserId = new SparseIntArray(); 917 ArrayList<ResolveInfo> forwardMatches = new ArrayList<>(); 918 919 // Create list of highest priority matches per user in highestPriorityMatchesByUserId 920 int numMatches = matches.size(); 921 for (int matchNum = 0; matchNum < numMatches; matchNum++) { 922 ResolveInfo match = matches.get(matchNum); 923 924 // Unnecessary forward matches are filtered out later, hence collect them all to add 925 // them below 926 if (isForwardMatch(match)) { 927 forwardMatches.add(match); 928 continue; 929 } 930 931 // If this a previously unknown user? 932 if (highestPriorityByUserId.indexOfKey(match.targetUserId) < 0) { 933 highestPriorityByUserId.put(match.targetUserId, Integer.MIN_VALUE); 934 highestPriorityMatchesByUserId.put(match.targetUserId, new ArrayList<>()); 935 } 936 937 // Find current highest priority matches for the current user 938 int highestPriority = highestPriorityByUserId.get(match.targetUserId); 939 ArrayList<ResolveInfo> highestPriorityMatches = highestPriorityMatchesByUserId.get( 940 match.targetUserId); 941 942 if (match.priority == highestPriority) { 943 highestPriorityMatches.add(match); 944 } else if (match.priority > highestPriority) { 945 highestPriorityByUserId.put(match.targetUserId, match.priority); 946 947 highestPriorityMatches.clear(); 948 highestPriorityMatches.add(match); 949 } 950 } 951 952 // Combine all users (+ forward matches) back together. This means that all non-forward 953 // matches have the same priority for a user. Matches for different users might have 954 // different priority. 955 ArrayList<ResolveInfo> combinedMatches = new ArrayList<>(forwardMatches); 956 int numMatchArrays = highestPriorityMatchesByUserId.size(); 957 for (int matchArrayNum = 0; matchArrayNum < numMatchArrays; matchArrayNum++) { 958 combinedMatches.addAll(highestPriorityMatchesByUserId.valueAt(matchArrayNum)); 959 } 960 961 return combinedMatches; 962 } 963 964 /** 965 * If there are no matches for a profile, remove the forward intent to this profile. 966 * 967 * @param rawMatches The matches that contain all forward intents 968 * 969 * @return The matches with the unnecessary forward intents removed 970 */ 971 @NonNull private ArrayList<ResolveInfo> removeForwardIntentIfNotNeeded( 972 @NonNull ArrayList<ResolveInfo> rawMatches) { 973 final int numRawMatches = rawMatches.size(); 974 975 // The raw matches contain the activities that can be started but also the intents to 976 // forward the intent to the other profile 977 int numParentActivityMatches = 0; 978 int numNonParentActivityMatches = 0; 979 for (int i = 0; i < numRawMatches; i++) { 980 final ResolveInfo rawMatch = rawMatches.get(i); 981 if (!isForwardMatch(rawMatch)) { 982 if (UserHandle.getUserHandleForUid( 983 rawMatch.activityInfo.applicationInfo.uid).equals(mParentUser)) { 984 numParentActivityMatches++; 985 } else { 986 numNonParentActivityMatches++; 987 } 988 } 989 } 990 991 // If only one profile has activity matches, we need to remove all switch intents 992 if (numParentActivityMatches == 0 || numNonParentActivityMatches == 0) { 993 ArrayList<ResolveInfo> matches = new ArrayList<>( 994 numParentActivityMatches + numNonParentActivityMatches); 995 996 for (int i = 0; i < numRawMatches; i++) { 997 ResolveInfo rawMatch = rawMatches.get(i); 998 if (!isForwardMatch(rawMatch)) { 999 matches.add(rawMatch); 1000 } 1001 } 1002 return matches; 1003 1004 } else { 1005 return rawMatches; 1006 } 1007 } 1008 1009 private ArrayList<ResolveInfo> getDeviceMatchesLocked(UsbDevice device, Intent intent) { 1010 ArrayList<ResolveInfo> matches = new ArrayList<>(); 1011 List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent); 1012 int count = resolveInfos.size(); 1013 for (int i = 0; i < count; i++) { 1014 ResolveInfo resolveInfo = resolveInfos.get(i); 1015 if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) { 1016 matches.add(resolveInfo); 1017 } 1018 } 1019 1020 return removeForwardIntentIfNotNeeded(preferHighPriority(matches)); 1021 } 1022 1023 private ArrayList<ResolveInfo> getAccessoryMatchesLocked( 1024 UsbAccessory accessory, Intent intent) { 1025 ArrayList<ResolveInfo> matches = new ArrayList<>(); 1026 List<ResolveInfo> resolveInfos = queryIntentActivitiesForAllProfiles(intent); 1027 int count = resolveInfos.size(); 1028 for (int i = 0; i < count; i++) { 1029 ResolveInfo resolveInfo = resolveInfos.get(i); 1030 if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) { 1031 matches.add(resolveInfo); 1032 } 1033 } 1034 1035 return removeForwardIntentIfNotNeeded(preferHighPriority(matches)); 1036 } 1037 1038 public void deviceAttached(UsbDevice device) { 1039 final Intent intent = createDeviceAttachedIntent(device); 1040 1041 // Send broadcast to running activities with registered intent 1042 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1043 1044 resolveActivity(intent, device, true /* showMtpNotification */); 1045 } 1046 1047 private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) { 1048 final ArrayList<ResolveInfo> matches; 1049 final ActivityInfo defaultActivity; 1050 synchronized (mLock) { 1051 matches = getDeviceMatchesLocked(device, intent); 1052 defaultActivity = getDefaultActivityLocked( 1053 matches, mDevicePreferenceMap.get(new DeviceFilter(device))); 1054 } 1055 1056 if (showMtpNotification && MtpNotificationManager.shouldShowNotification( 1057 mPackageManager, device) && defaultActivity == null) { 1058 // Show notification if the device is MTP storage. 1059 mMtpNotificationManager.showNotification(device); 1060 return; 1061 } 1062 1063 // Start activity with registered intent 1064 resolveActivity(intent, matches, defaultActivity, device, null); 1065 } 1066 1067 public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) { 1068 final Intent intent = createDeviceAttachedIntent(device); 1069 1070 // Send broadcast to running activity with registered intent 1071 mContext.sendBroadcast(intent); 1072 1073 ApplicationInfo appInfo; 1074 try { 1075 // Fixed handlers are always for parent user 1076 appInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0, 1077 mParentUser.getIdentifier()); 1078 } catch (NameNotFoundException e) { 1079 Slog.e(TAG, "Default USB handling package (" + component.getPackageName() 1080 + ") not found for user " + mParentUser); 1081 return; 1082 } 1083 1084 mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid)) 1085 .grantDevicePermission(device, appInfo.uid); 1086 1087 Intent activityIntent = new Intent(intent); 1088 activityIntent.setComponent(component); 1089 try { 1090 mContext.startActivityAsUser(activityIntent, mParentUser); 1091 } catch (ActivityNotFoundException e) { 1092 Slog.e(TAG, "unable to start activity " + activityIntent); 1093 } 1094 } 1095 1096 /** 1097 * Remove notifications for a usb device. 1098 * 1099 * @param device The device the notifications are for. 1100 */ 1101 void usbDeviceRemoved(@NonNull UsbDevice device) { 1102 mMtpNotificationManager.hideNotification(device.getDeviceId()); 1103 } 1104 1105 public void accessoryAttached(UsbAccessory accessory) { 1106 Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); 1107 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 1108 intent.addFlags( 1109 Intent.FLAG_ACTIVITY_NEW_TASK | 1110 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1111 1112 final ArrayList<ResolveInfo> matches; 1113 final ActivityInfo defaultActivity; 1114 synchronized (mLock) { 1115 matches = getAccessoryMatchesLocked(accessory, intent); 1116 defaultActivity = getDefaultActivityLocked( 1117 matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory))); 1118 } 1119 1120 resolveActivity(intent, matches, defaultActivity, null, accessory); 1121 } 1122 1123 /** 1124 * Start the appropriate package when an device/accessory got attached. 1125 * 1126 * @param intent The intent to start the package 1127 * @param matches The available resolutions of the intent 1128 * @param defaultActivity The default activity for the device (if set) 1129 * @param device The device if a device was attached 1130 * @param accessory The accessory if a device was attached 1131 */ 1132 private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, 1133 @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, 1134 @Nullable UsbAccessory accessory) { 1135 // don't show the resolver activity if there are no choices available 1136 if (matches.size() == 0) { 1137 if (accessory != null) { 1138 String uri = accessory.getUri(); 1139 if (uri != null && uri.length() > 0) { 1140 // display URI to user 1141 Intent dialogIntent = new Intent(); 1142 dialogIntent.setClassName("com.android.systemui", 1143 "com.android.systemui.usb.UsbAccessoryUriActivity"); 1144 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1145 dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 1146 dialogIntent.putExtra("uri", uri); 1147 try { 1148 mContext.startActivityAsUser(dialogIntent, mParentUser); 1149 } catch (ActivityNotFoundException e) { 1150 Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); 1151 } 1152 } 1153 } 1154 1155 // do nothing 1156 return; 1157 } 1158 1159 if (defaultActivity != null) { 1160 UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser( 1161 UserHandle.getUserId(defaultActivity.applicationInfo.uid)); 1162 // grant permission for default activity 1163 if (device != null) { 1164 defaultRIUserSettings. 1165 grantDevicePermission(device, defaultActivity.applicationInfo.uid); 1166 } else if (accessory != null) { 1167 defaultRIUserSettings.grantAccessoryPermission(accessory, 1168 defaultActivity.applicationInfo.uid); 1169 } 1170 1171 // start default activity directly 1172 try { 1173 intent.setComponent( 1174 new ComponentName(defaultActivity.packageName, defaultActivity.name)); 1175 1176 UserHandle user = UserHandle.getUserHandleForUid( 1177 defaultActivity.applicationInfo.uid); 1178 mContext.startActivityAsUser(intent, user); 1179 } catch (ActivityNotFoundException e) { 1180 Slog.e(TAG, "startActivity failed", e); 1181 } 1182 } else { 1183 Intent resolverIntent = new Intent(); 1184 resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1185 UserHandle user; 1186 1187 if (matches.size() == 1) { 1188 ResolveInfo rInfo = matches.get(0); 1189 1190 // start UsbConfirmActivity if there is only one choice 1191 resolverIntent.setClassName("com.android.systemui", 1192 "com.android.systemui.usb.UsbConfirmActivity"); 1193 resolverIntent.putExtra("rinfo", rInfo); 1194 user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid); 1195 1196 if (device != null) { 1197 resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device); 1198 } else { 1199 resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 1200 } 1201 } else { 1202 user = mParentUser; 1203 1204 // start UsbResolverActivity so user can choose an activity 1205 resolverIntent.setClassName("com.android.systemui", 1206 "com.android.systemui.usb.UsbResolverActivity"); 1207 resolverIntent.putParcelableArrayListExtra("rlist", matches); 1208 resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); 1209 } 1210 try { 1211 mContext.startActivityAsUser(resolverIntent, user); 1212 } catch (ActivityNotFoundException e) { 1213 Slog.e(TAG, "unable to start activity " + resolverIntent, e); 1214 } 1215 } 1216 } 1217 1218 /** 1219 * Returns a default activity for matched ResolveInfo. 1220 * @param matches Resolved activities matched with connected device/accesary. 1221 * @param userPackage Default activity choosed by a user before. Should be null if no activity 1222 * is choosed by a user. 1223 * @return Default activity 1224 */ 1225 private @Nullable ActivityInfo getDefaultActivityLocked( 1226 @NonNull ArrayList<ResolveInfo> matches, 1227 @Nullable UserPackage userPackage) { 1228 if (userPackage != null) { 1229 // look for default activity 1230 for (final ResolveInfo info : matches) { 1231 if (info.activityInfo != null && userPackage.equals( 1232 new UserPackage(info.activityInfo.packageName, 1233 UserHandle.getUserHandleForUid( 1234 info.activityInfo.applicationInfo.uid)))) { 1235 return info.activityInfo; 1236 } 1237 } 1238 } 1239 1240 if (matches.size() == 1) { 1241 final ActivityInfo activityInfo = matches.get(0).activityInfo; 1242 if (activityInfo != null) { 1243 if (mDisablePermissionDialogs) { 1244 return activityInfo; 1245 } 1246 // System apps are considered default unless there are other matches 1247 if (activityInfo.applicationInfo != null 1248 && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 1249 != 0) { 1250 return activityInfo; 1251 } 1252 } 1253 } 1254 1255 return null; 1256 } 1257 1258 private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage, 1259 @NonNull DeviceFilter filter) { 1260 ArrayList<DeviceFilter> keysToRemove = new ArrayList<>(); 1261 1262 // The keys in mDevicePreferenceMap are filters that match devices very narrowly 1263 for (DeviceFilter device : mDevicePreferenceMap.keySet()) { 1264 if (filter.contains(device)) { 1265 UserPackage currentMatch = mDevicePreferenceMap.get(device); 1266 if (!currentMatch.equals(userPackage)) { 1267 keysToRemove.add(device); 1268 } 1269 } 1270 } 1271 1272 if (!keysToRemove.isEmpty()) { 1273 for (DeviceFilter keyToRemove : keysToRemove) { 1274 mDevicePreferenceMap.remove(keyToRemove); 1275 } 1276 } 1277 1278 return !keysToRemove.isEmpty(); 1279 } 1280 1281 private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage, 1282 @NonNull AccessoryFilter filter) { 1283 ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>(); 1284 1285 // The keys in mAccessoryPreferenceMap are filters that match accessories very narrowly 1286 for (AccessoryFilter accessory : mAccessoryPreferenceMap.keySet()) { 1287 if (filter.contains(accessory)) { 1288 UserPackage currentMatch = mAccessoryPreferenceMap.get(accessory); 1289 if (!currentMatch.equals(userPackage)) { 1290 keysToRemove.add(accessory); 1291 } 1292 } 1293 } 1294 1295 if (!keysToRemove.isEmpty()) { 1296 for (AccessoryFilter keyToRemove : keysToRemove) { 1297 mAccessoryPreferenceMap.remove(keyToRemove); 1298 } 1299 } 1300 1301 return !keysToRemove.isEmpty(); 1302 } 1303 1304 private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo, 1305 String metaDataName) { 1306 XmlResourceParser parser = null; 1307 boolean changed = false; 1308 1309 try { 1310 parser = aInfo.loadXmlMetaData(mPackageManager, metaDataName); 1311 if (parser == null) return false; 1312 1313 XmlUtils.nextElement(parser); 1314 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 1315 String tagName = parser.getName(); 1316 if ("usb-device".equals(tagName)) { 1317 DeviceFilter filter = DeviceFilter.read(parser); 1318 if (clearCompatibleMatchesLocked(userPackage, filter)) { 1319 changed = true; 1320 } 1321 } 1322 else if ("usb-accessory".equals(tagName)) { 1323 AccessoryFilter filter = AccessoryFilter.read(parser); 1324 if (clearCompatibleMatchesLocked(userPackage, filter)) { 1325 changed = true; 1326 } 1327 } 1328 XmlUtils.nextElement(parser); 1329 } 1330 } catch (Exception e) { 1331 Slog.w(TAG, "Unable to load component info " + aInfo.toString(), e); 1332 } finally { 1333 if (parser != null) parser.close(); 1334 } 1335 return changed; 1336 } 1337 1338 // Check to see if the package supports any USB devices or accessories. 1339 // If so, clear any preferences for matching devices/accessories. 1340 private void handlePackageAdded(@NonNull UserPackage userPackage) { 1341 synchronized (mLock) { 1342 PackageInfo info; 1343 boolean changed = false; 1344 1345 try { 1346 info = mPackageManager.getPackageInfoAsUser(userPackage.packageName, 1347 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA, 1348 userPackage.user.getIdentifier()); 1349 } catch (NameNotFoundException e) { 1350 Slog.e(TAG, "handlePackageUpdate could not find package " + userPackage, e); 1351 return; 1352 } 1353 1354 ActivityInfo[] activities = info.activities; 1355 if (activities == null) return; 1356 for (int i = 0; i < activities.length; i++) { 1357 // check for meta-data, both for devices and accessories 1358 if (handlePackageAddedLocked(userPackage, activities[i], 1359 UsbManager.ACTION_USB_DEVICE_ATTACHED)) { 1360 changed = true; 1361 } 1362 1363 if (handlePackageAddedLocked(userPackage, activities[i], 1364 UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { 1365 changed = true; 1366 } 1367 } 1368 1369 if (changed) { 1370 scheduleWriteSettingsLocked(); 1371 } 1372 } 1373 } 1374 1375 /** 1376 * Get the serial number for a user handle. 1377 * 1378 * @param user The user handle 1379 * 1380 * @return The serial number 1381 */ 1382 private int getSerial(@NonNull UserHandle user) { 1383 return mUserManager.getUserSerialNumber(user.getIdentifier()); 1384 } 1385 1386 /** 1387 * Set a package as default handler for a device. 1388 * 1389 * @param device The device that should be handled by default 1390 * @param packageName The default handler package 1391 * @param user The user the package belongs to 1392 */ 1393 void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName, 1394 @NonNull UserHandle user) { 1395 DeviceFilter filter = new DeviceFilter(device); 1396 boolean changed; 1397 synchronized (mLock) { 1398 if (packageName == null) { 1399 changed = (mDevicePreferenceMap.remove(filter) != null); 1400 } else { 1401 UserPackage userPackage = new UserPackage(packageName, user); 1402 1403 changed = !userPackage.equals(mDevicePreferenceMap.get(filter)); 1404 if (changed) { 1405 mDevicePreferenceMap.put(filter, userPackage); 1406 } 1407 } 1408 if (changed) { 1409 scheduleWriteSettingsLocked(); 1410 } 1411 } 1412 } 1413 1414 /** 1415 * Set a package as default handler for a accessory. 1416 * 1417 * @param accessory The accessory that should be handled by default 1418 * @param packageName The default handler package 1419 * @param user The user the package belongs to 1420 */ 1421 void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName, 1422 @NonNull UserHandle user) { 1423 AccessoryFilter filter = new AccessoryFilter(accessory); 1424 boolean changed; 1425 synchronized (mLock) { 1426 if (packageName == null) { 1427 changed = (mAccessoryPreferenceMap.remove(filter) != null); 1428 } else { 1429 UserPackage userPackage = new UserPackage(packageName, user); 1430 1431 changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter)); 1432 if (changed) { 1433 mAccessoryPreferenceMap.put(filter, userPackage); 1434 } 1435 } 1436 if (changed) { 1437 scheduleWriteSettingsLocked(); 1438 } 1439 } 1440 } 1441 1442 /** 1443 * Check if a package has is the default handler for any usb device or accessory. 1444 * 1445 * @param packageName The package name 1446 * @param user The user the package belongs to 1447 * 1448 * @return {@code true} iff the package is default for any usb device or accessory 1449 */ 1450 boolean hasDefaults(@NonNull String packageName, @NonNull UserHandle user) { 1451 UserPackage userPackage = new UserPackage(packageName, user); 1452 synchronized (mLock) { 1453 if (mDevicePreferenceMap.values().contains(userPackage)) return true; 1454 return mAccessoryPreferenceMap.values().contains(userPackage); 1455 } 1456 } 1457 1458 /** 1459 * Clear defaults for a package from any preference. 1460 * 1461 * @param packageName The package to remove 1462 * @param user The user the package belongs to 1463 */ 1464 void clearDefaults(@NonNull String packageName, @NonNull UserHandle user) { 1465 UserPackage userPackage = new UserPackage(packageName, user); 1466 1467 synchronized (mLock) { 1468 if (clearPackageDefaultsLocked(userPackage)) { 1469 scheduleWriteSettingsLocked(); 1470 } 1471 } 1472 } 1473 1474 /** 1475 * Clear defaults for a package from any preference (does not persist). 1476 * 1477 * @param userPackage The package to remove 1478 * 1479 * @return {@code true} iff at least one preference was cleared 1480 */ 1481 private boolean clearPackageDefaultsLocked(@NonNull UserPackage userPackage) { 1482 boolean cleared = false; 1483 synchronized (mLock) { 1484 if (mDevicePreferenceMap.containsValue(userPackage)) { 1485 // make a copy of the key set to avoid ConcurrentModificationException 1486 DeviceFilter[] keys = mDevicePreferenceMap.keySet().toArray(new DeviceFilter[0]); 1487 for (int i = 0; i < keys.length; i++) { 1488 DeviceFilter key = keys[i]; 1489 if (userPackage.equals(mDevicePreferenceMap.get(key))) { 1490 mDevicePreferenceMap.remove(key); 1491 cleared = true; 1492 } 1493 } 1494 } 1495 if (mAccessoryPreferenceMap.containsValue(userPackage)) { 1496 // make a copy of the key set to avoid ConcurrentModificationException 1497 AccessoryFilter[] keys = 1498 mAccessoryPreferenceMap.keySet().toArray(new AccessoryFilter[0]); 1499 for (int i = 0; i < keys.length; i++) { 1500 AccessoryFilter key = keys[i]; 1501 if (userPackage.equals(mAccessoryPreferenceMap.get(key))) { 1502 mAccessoryPreferenceMap.remove(key); 1503 cleared = true; 1504 } 1505 } 1506 } 1507 return cleared; 1508 } 1509 } 1510 1511 public void dump(IndentingPrintWriter pw) { 1512 synchronized (mLock) { 1513 pw.println("Device preferences:"); 1514 for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { 1515 pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter)); 1516 } 1517 pw.println("Accessory preferences:"); 1518 for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { 1519 pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter)); 1520 } 1521 } 1522 } 1523 1524 private static Intent createDeviceAttachedIntent(UsbDevice device) { 1525 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); 1526 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 1527 intent.addFlags( 1528 Intent.FLAG_ACTIVITY_NEW_TASK | 1529 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1530 return intent; 1531 } 1532 } 1533