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