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 */ UserPackage(@onNull String packageName, @NonNull UserHandle user)131 private UserPackage(@NonNull String packageName, @NonNull UserHandle user) { 132 this.packageName = packageName; 133 this.user = user; 134 } 135 136 @Override equals(Object obj)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 hashCode()148 public int hashCode() { 149 int result = user.hashCode(); 150 result = 31 * result + packageName.hashCode(); 151 return result; 152 } 153 154 @Override toString()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 DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum)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 DeviceFilter(UsbDevice device)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 read(XmlPullParser parser)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 write(XmlSerializer serializer)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 matches(int clasz, int subclass, int protocol)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 matches(UsbDevice device)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 */ contains(DeviceFilter device)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 equals(Object obj)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 hashCode()426 public int hashCode() { 427 return (((mVendorId << 16) | mProductId) ^ 428 ((mClass << 16) | (mSubclass << 8) | mProtocol)); 429 } 430 431 @Override toString()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 AccessoryFilter(String manufacturer, String model, String version)453 public AccessoryFilter(String manufacturer, String model, String version) { 454 mManufacturer = manufacturer; 455 mModel = model; 456 mVersion = version; 457 } 458 AccessoryFilter(UsbAccessory accessory)459 public AccessoryFilter(UsbAccessory accessory) { 460 mManufacturer = accessory.getManufacturer(); 461 mModel = accessory.getModel(); 462 mVersion = accessory.getVersion(); 463 } 464 read(XmlPullParser parser)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 write(XmlSerializer serializer)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 matches(UsbAccessory acc)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 */ contains(AccessoryFilter accessory)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 equals(Object obj)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 hashCode()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 toString()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 onPackageAdded(String packageName, int uid)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 onPackageRemoved(String packageName, int uid)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 */ UsbProfileGroupSettingsManager(@onNull Context context, @NonNull UserHandle user, @NonNull UsbSettingsManager settingsManager)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 */ removeAllDefaultsForUser(@onNull UserHandle userToRemove)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 readPreference(XmlPullParser parser)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 */ upgradeSingleUserLocked()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 readSettingsLocked()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 */ scheduleWriteSettingsLocked()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. packageMatchesLocked(ResolveInfo info, String metaDataName, UsbDevice device, UsbAccessory accessory)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 */ queryIntentActivitiesForAllProfiles( @onNull Intent intent)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 */ isForwardMatch(@onNull ResolveInfo match)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 preferHighPriority(@onNull ArrayList<ResolveInfo> matches)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 */ removeForwardIntentIfNotNeeded( @onNull ArrayList<ResolveInfo> rawMatches)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 getDeviceMatchesLocked(UsbDevice device, Intent intent)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 getAccessoryMatchesLocked( UsbAccessory accessory, Intent intent)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 deviceAttached(UsbDevice device)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 resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification)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 deviceAttachedForFixedHandler(UsbDevice device, ComponentName component)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 */ usbDeviceRemoved(@onNull UsbDevice device)1101 void usbDeviceRemoved(@NonNull UsbDevice device) { 1102 mMtpNotificationManager.hideNotification(device.getDeviceId()); 1103 } 1104 accessoryAttached(UsbAccessory accessory)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 */ resolveActivity(@onNull Intent intent, @NonNull ArrayList<ResolveInfo> matches, @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device, @Nullable UsbAccessory accessory)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 */ getDefaultActivityLocked( @onNull ArrayList<ResolveInfo> matches, @Nullable UserPackage userPackage)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 clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull DeviceFilter filter)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 clearCompatibleMatchesLocked(@onNull UserPackage userPackage, @NonNull AccessoryFilter filter)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 handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo, String metaDataName)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. handlePackageAdded(@onNull UserPackage userPackage)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 */ getSerial(@onNull UserHandle user)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 */ setDevicePackage(@onNull UsbDevice device, @Nullable String packageName, @NonNull UserHandle user)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 */ setAccessoryPackage(@onNull UsbAccessory accessory, @Nullable String packageName, @NonNull UserHandle user)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 */ hasDefaults(@onNull String packageName, @NonNull UserHandle user)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 */ clearDefaults(@onNull String packageName, @NonNull UserHandle user)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 */ clearPackageDefaultsLocked(@onNull UserPackage userPackage)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 dump(IndentingPrintWriter pw)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 createDeviceAttachedIntent(UsbDevice device)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