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