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