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