From 66c5a8d2073d2076fc0e06237659143e8de52539 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 31 May 2018 11:02:32 -0700 Subject: [PATCH] Dump activities that handle when USB devices get plugged in When a USB device or accessory gets plugged in the system might automatically start activities that have a device/accessory filter set to include the plugged in USB device. Unfortunately we seen too many apps setting too broad of a filter and it annoys the user that this apps wants to start every time the device gets plugged in. This change dumps the acitivity names and their filters so debugging such issues is much easier. To do this, there is some mild refactoring: 1. Split packageMatchesLocked into - getDeviceFilters to resolve all device filters - getAccessoryFilterer to resolve all accessories - Check if a filter matches after resolving all filters. This is no performance issue as usually there is only one or two filters per activity. 2. Split queryIntentActivitiesForAllProfiles into - A per user part, so that we can use the exact same intent-resolution code for the dump-code - And a loop over the per user part Test: adb shell dumpsys usb + incident_report usb + verified that the output is as expected Change-Id: I552c963b9a1b1c7df0adc6746af8cd42a4691cd6 --- core/proto/android/service/usb.proto | 16 +++ .../usb/UsbProfileGroupSettingsManager.java | 126 ++++++++++++++---- .../server/usb/UsbSettingsManager.java | 7 +- .../server/usb/UsbUserSettingsManager.java | 77 +++++++++++ 4 files changed, 200 insertions(+), 26 deletions(-) diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto index c36371004c35c..9f58611397f90 100644 --- a/core/proto/android/service/usb.proto +++ b/core/proto/android/service/usb.proto @@ -278,6 +278,8 @@ message UsbUserSettingsManagerProto { optional int32 user_id = 1; repeated UsbSettingsDevicePermissionProto device_permissions = 2; repeated UsbSettingsAccessoryPermissionProto accessory_permissions = 3; + repeated UsbDeviceAttachedActivities device_attached_activities = 4; + repeated UsbAccessoryAttachedActivities accessory_attached_activities = 5; } message UsbSettingsDevicePermissionProto { @@ -343,3 +345,17 @@ message UsbAccessoryFilterProto { optional string model = 2; optional string version = 3; } + +message UsbDeviceAttachedActivities { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional android.content.ComponentNameProto activity = 1; + repeated UsbDeviceFilterProto filters = 2; +} + +message UsbAccessoryAttachedActivities { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional android.content.ComponentNameProto activity = 1; + repeated UsbAccessoryFilterProto filters = 2; +} diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index 43f189b26dfa7..7a906d0b7aaf8 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -445,38 +445,38 @@ class UsbProfileGroupSettingsManager { }); } - // Checks to see if a package matches a device or accessory. - // Only one of device and accessory should be non-null. - private boolean packageMatchesLocked(ResolveInfo info, String metaDataName, - UsbDevice device, UsbAccessory accessory) { - if (isForwardMatch(info)) { - return true; - } - + /** + * Get {@link DeviceFilter} for all devices an activity should be launched for. + * + * @param pm The package manager used to get the device filter files + * @param info The {@link ResolveInfo} for the activity that can handle usb device attached + * events + * + * @return The list of {@link DeviceFilter} the activity should be called for or {@code null} if + * none + */ + @Nullable + static ArrayList getDeviceFilters(@NonNull PackageManager pm, + @NonNull ResolveInfo info) { + ArrayList filters = null; ActivityInfo ai = info.activityInfo; XmlResourceParser parser = null; try { - parser = ai.loadXmlMetaData(mPackageManager, metaDataName); + parser = ai.loadXmlMetaData(pm, UsbManager.ACTION_USB_DEVICE_ATTACHED); if (parser == null) { Slog.w(TAG, "no meta-data for " + info); - return false; + return null; } XmlUtils.nextElement(parser); while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { String tagName = parser.getName(); - if (device != null && "usb-device".equals(tagName)) { - DeviceFilter filter = DeviceFilter.read(parser); - if (filter.matches(device)) { - return true; - } - } - else if (accessory != null && "usb-accessory".equals(tagName)) { - AccessoryFilter filter = AccessoryFilter.read(parser); - if (filter.matches(accessory)) { - return true; + if ("usb-device".equals(tagName)) { + if (filters == null) { + filters = new ArrayList<>(1); } + filters.add(DeviceFilter.read(parser)); } XmlUtils.nextElement(parser); } @@ -485,6 +485,84 @@ class UsbProfileGroupSettingsManager { } finally { if (parser != null) parser.close(); } + return filters; + } + + /** + * Get {@link AccessoryFilter} for all accessories an activity should be launched for. + * + * @param pm The package manager used to get the accessory filter files + * @param info The {@link ResolveInfo} for the activity that can handle usb accessory attached + * events + * + * @return The list of {@link AccessoryFilter} the activity should be called for or {@code null} + * if none + */ + static @Nullable ArrayList getAccessoryFilters(@NonNull PackageManager pm, + @NonNull ResolveInfo info) { + ArrayList filters = null; + ActivityInfo ai = info.activityInfo; + + XmlResourceParser parser = null; + try { + parser = ai.loadXmlMetaData(pm, UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + if (parser == null) { + Slog.w(TAG, "no meta-data for " + info); + return null; + } + + XmlUtils.nextElement(parser); + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + String tagName = parser.getName(); + if ("usb-accessory".equals(tagName)) { + if (filters == null) { + filters = new ArrayList<>(1); + } + filters.add(AccessoryFilter.read(parser)); + } + XmlUtils.nextElement(parser); + } + } catch (Exception e) { + Slog.w(TAG, "Unable to load component info " + info.toString(), e); + } finally { + if (parser != null) parser.close(); + } + return filters; + } + + // Checks to see if a package matches a device or accessory. + // Only one of device and accessory should be non-null. + private boolean packageMatchesLocked(ResolveInfo info, UsbDevice device, + UsbAccessory accessory) { + if (isForwardMatch(info)) { + return true; + } + + if (device != null) { + ArrayList deviceFilters = getDeviceFilters(mPackageManager, info); + if (deviceFilters != null) { + int numDeviceFilters = deviceFilters.size(); + for (int i = 0; i < numDeviceFilters; i++) { + if (deviceFilters.get(i).matches(device)) { + return true; + } + } + } + } + + if (accessory != null) { + ArrayList accessoryFilters = getAccessoryFilters(mPackageManager, + info); + if (accessoryFilters != null) { + int numAccessoryFilters = accessoryFilters.size(); + for (int i = 0; i < numAccessoryFilters; i++) { + if (accessoryFilters.get(i).matches(accessory)) { + return true; + } + } + } + } + return false; } @@ -502,8 +580,8 @@ class UsbProfileGroupSettingsManager { ArrayList resolveInfos = new ArrayList<>(); int numProfiles = profiles.size(); for (int i = 0; i < numProfiles; i++) { - resolveInfos.addAll(mPackageManager.queryIntentActivitiesAsUser(intent, - PackageManager.GET_META_DATA, profiles.get(i).id)); + resolveInfos.addAll(mSettingsManager.getSettingsForUser(profiles.get(i).id) + .queryIntentActivities(intent)); } return resolveInfos; @@ -629,7 +707,7 @@ class UsbProfileGroupSettingsManager { int count = resolveInfos.size(); for (int i = 0; i < count; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); - if (packageMatchesLocked(resolveInfo, intent.getAction(), device, null)) { + if (packageMatchesLocked(resolveInfo, device, null)) { matches.add(resolveInfo); } } @@ -644,7 +722,7 @@ class UsbProfileGroupSettingsManager { int count = resolveInfos.size(); for (int i = 0; i < count; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); - if (packageMatchesLocked(resolveInfo, intent.getAction(), null, accessory)) { + if (packageMatchesLocked(resolveInfo, null, accessory)) { matches.add(resolveInfo); } } diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java index caf05a3538ec3..9221825b2d873 100644 --- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java @@ -33,6 +33,8 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.dump.DualDumpOutputStream; +import java.util.List; + /** * Maintains all {@link UsbUserSettingsManager} for all users. */ @@ -140,9 +142,10 @@ class UsbSettingsManager { long token = dump.start(idName, id); synchronized (mSettingsByUser) { - int numUsers = mSettingsByUser.size(); + List users = mUserManager.getUsers(); + int numUsers = users.size(); for (int i = 0; i < numUsers; i++) { - mSettingsByUser.valueAt(i).dump(dump, "user_settings", + getSettingsForUser(users.get(i).id).dump(dump, "user_settings", UsbSettingsManagerProto.USER_SETTINGS); } } diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java index 840950679903e..24a2d72415eda 100644 --- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java @@ -16,14 +16,22 @@ package com.android.server.usb; +import static com.android.internal.util.dump.DumpUtils.writeComponentName; +import static com.android.server.usb.UsbProfileGroupSettingsManager.getAccessoryFilters; +import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters; + import android.annotation.NonNull; import android.app.PendingIntent; import android.content.ActivityNotFoundException; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.hardware.usb.AccessoryFilter; +import android.hardware.usb.DeviceFilter; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; @@ -32,6 +40,8 @@ import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.Process; import android.os.UserHandle; +import android.service.usb.UsbAccessoryAttachedActivities; +import android.service.usb.UsbDeviceAttachedActivities; import android.service.usb.UsbSettingsAccessoryPermissionProto; import android.service.usb.UsbSettingsDevicePermissionProto; import android.service.usb.UsbUserSettingsManagerProto; @@ -40,7 +50,9 @@ import android.util.SparseBooleanArray; import com.android.internal.util.dump.DualDumpOutputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; class UsbUserSettingsManager { private static final String TAG = "UsbUserSettingsManager"; @@ -305,6 +317,18 @@ class UsbUserSettingsManager { } } + /** + * Get all activities that can handle the device/accessory attached intent. + * + * @param intent The intent to handle + * + * @return The resolve infos of the activities that can handle the intent + */ + List queryIntentActivities(@NonNull Intent intent) { + return mPackageManager.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA, + mUser.getIdentifier()); + } + public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) { long token = dump.start(idName, id); @@ -341,6 +365,59 @@ class UsbUserSettingsManager { dump.end(accessoryPermissionToken); } + + List deviceAttachedActivities = queryIntentActivities( + new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED)); + int numDeviceAttachedActivities = deviceAttachedActivities.size(); + for (int activityNum = 0; activityNum < numDeviceAttachedActivities; activityNum++) { + ResolveInfo deviceAttachedActivity = deviceAttachedActivities.get(activityNum); + + long deviceAttachedActivityToken = dump.start("device_attached_activities", + UsbUserSettingsManagerProto.DEVICE_ATTACHED_ACTIVITIES); + + writeComponentName(dump, "activity", UsbDeviceAttachedActivities.ACTIVITY, + new ComponentName(deviceAttachedActivity.activityInfo.packageName, + deviceAttachedActivity.activityInfo.name)); + + ArrayList deviceFilters = getDeviceFilters(mPackageManager, + deviceAttachedActivity); + if (deviceFilters != null) { + int numDeviceFilters = deviceFilters.size(); + for (int filterNum = 0; filterNum < numDeviceFilters; filterNum++) { + deviceFilters.get(filterNum).dump(dump, "filters", + UsbDeviceAttachedActivities.FILTERS); + } + } + + dump.end(deviceAttachedActivityToken); + } + + List accessoryAttachedActivities = + queryIntentActivities(new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)); + int numAccessoryAttachedActivities = accessoryAttachedActivities.size(); + for (int activityNum = 0; activityNum < numAccessoryAttachedActivities; activityNum++) { + ResolveInfo accessoryAttachedActivity = + accessoryAttachedActivities.get(activityNum); + + long accessoryAttachedActivityToken = dump.start("accessory_attached_activities", + UsbUserSettingsManagerProto.ACCESSORY_ATTACHED_ACTIVITIES); + + writeComponentName(dump, "activity", UsbAccessoryAttachedActivities.ACTIVITY, + new ComponentName(accessoryAttachedActivity.activityInfo.packageName, + accessoryAttachedActivity.activityInfo.name)); + + ArrayList accessoryFilters = getAccessoryFilters(mPackageManager, + accessoryAttachedActivity); + if (accessoryFilters != null) { + int numAccessoryFilters = accessoryFilters.size(); + for (int filterNum = 0; filterNum < numAccessoryFilters; filterNum++) { + accessoryFilters.get(filterNum).dump(dump, "filters", + UsbAccessoryAttachedActivities.FILTERS); + } + } + + dump.end(accessoryAttachedActivityToken); + } } dump.end(token);