Merge "USB permission and resolve activity refactor."
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 3141be4..ae0855a 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -300,6 +300,23 @@
     public static final String EXTRA_PERMISSION_GRANTED = "permission";
 
     /**
+     * Name of extra added to start systemui.usb.UsbPermissionActivity
+     * containing package name of the app which requests USB permission.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PACKAGE = "android.hardware.usb.extra.PACKAGE";
+
+    /**
+     * Name of extra added to start systemui.usb.UsbPermissionActivity
+     * containing the whether the app which requests USB permission can be set as default handler
+     * for USB device attach event or USB accessory attach event or not.
+     *
+     * @hide
+     */
+    public static final String EXTRA_CAN_BE_DEFAULT = "android.hardware.usb.extra.CAN_BE_DEFAULT";
+
+    /**
      * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 238407a..a46f018 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,17 +16,13 @@
 
 package com.android.systemui.usb;
 
-import android.annotation.NonNull;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.XmlResourceParser;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
@@ -45,13 +41,8 @@
 
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
-import com.android.internal.util.XmlUtils;
-import android.hardware.usb.AccessoryFilter;
-import android.hardware.usb.DeviceFilter;
 import com.android.systemui.R;
 
-import org.xmlpull.v1.XmlPullParser;
-
 public class UsbPermissionActivity extends AlertActivity
         implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
 
@@ -71,12 +62,13 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-       Intent intent = getIntent();
+        Intent intent = getIntent();
         mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
         mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
         mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
         mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-        mPackageName = intent.getStringExtra("package");
+        mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
+        boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false);
 
         PackageManager packageManager = getPackageManager();
         ApplicationInfo aInfo;
@@ -105,121 +97,27 @@
         ap.mPositiveButtonListener = this;
         ap.mNegativeButtonListener = this;
 
-        try {
-            PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
-                    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
-
-            if ((mDevice != null && canBeDefault(mDevice, packageInfo))
-                    || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
-                // add "open when" checkbox
-                LayoutInflater inflater = (LayoutInflater) getSystemService(
-                        Context.LAYOUT_INFLATER_SERVICE);
-                ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
-                mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
-                if (mDevice == null) {
-                    mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
-                            mAccessory.getDescription()));
-                } else {
-                    mAlwaysUse.setText(getString(R.string.always_use_device, appName,
-                            mDevice.getProductName()));
-                }
-                mAlwaysUse.setOnCheckedChangeListener(this);
-
-                mClearDefaultHint = (TextView)ap.mView.findViewById(
-                        com.android.internal.R.id.clearDefaultHint);
-                mClearDefaultHint.setVisibility(View.GONE);
+        if (canBeDefault && (mDevice != null || mAccessory != null)) {
+            // add "open when" checkbox
+            LayoutInflater inflater = (LayoutInflater) getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+            mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+            if (mDevice == null) {
+                mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+                        mAccessory.getDescription()));
+            } else {
+                mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+                        mDevice.getProductName()));
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            // ignore
+            mAlwaysUse.setOnCheckedChangeListener(this);
+
+            mClearDefaultHint = (TextView)ap.mView.findViewById(
+                    com.android.internal.R.id.clearDefaultHint);
+            mClearDefaultHint.setVisibility(View.GONE);
         }
 
         setupAlert();
-
-    }
-
-    /**
-     * Can the app be the default for the USB device. I.e. can the app be launched by default if
-     * the device is plugged in.
-     *
-     * @param device The device the app would be default for
-     * @param packageInfo The package info of the app
-     *
-     * @return {@code true} iff the app can be default
-     */
-    private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
-        ActivityInfo[] activities = packageInfo.activities;
-        if (activities != null) {
-            int numActivities = activities.length;
-            for (int i = 0; i < numActivities; i++) {
-                ActivityInfo activityInfo = activities[i];
-
-                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
-                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
-                    if (parser == null) {
-                        continue;
-                    }
-
-                    XmlUtils.nextElement(parser);
-                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        if ("usb-device".equals(parser.getName())) {
-                            DeviceFilter filter = DeviceFilter.read(parser);
-                            if (filter.matches(device)) {
-                                return true;
-                            }
-                        }
-
-                        XmlUtils.nextElement(parser);
-                    }
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
-     * the accessory is plugged in.
-     *
-     * @param accessory The accessory the app would be default for
-     * @param packageInfo The package info of the app
-     *
-     * @return {@code true} iff the app can be default
-     */
-    private boolean canBeDefault(@NonNull UsbAccessory accessory,
-            @NonNull PackageInfo packageInfo) {
-        ActivityInfo[] activities = packageInfo.activities;
-        if (activities != null) {
-            int numActivities = activities.length;
-            for (int i = 0; i < numActivities; i++) {
-                ActivityInfo activityInfo = activities[i];
-
-                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
-                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
-                    if (parser == null) {
-                        continue;
-                    }
-
-                    XmlUtils.nextElement(parser);
-                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        if ("usb-accessory".equals(parser.getName())) {
-                            AccessoryFilter filter = AccessoryFilter.read(parser);
-                            if (filter.matches(accessory)) {
-                                return true;
-                            }
-                        }
-
-                        XmlUtils.nextElement(parser);
-                    }
-                } catch (Exception e) {
-                    Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
-                }
-            }
-        }
-
-        return false;
     }
 
     @Override
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
new file mode 100644
index 0000000..1730d8f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * UsbResolveActivityManager creates UI dialogs for user to pick or confirm handler for
+ * usb attach event.
+ *
+ * @hide
+ */
+class UsbHandlerManager {
+    private static final String LOG_TAG = UsbHandlerManager.class.getSimpleName();
+
+    private final Context mContext;
+
+    UsbHandlerManager(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Shows dialog to user to allow them to optionally visit that URL for more
+     * information or software downloads if the attached USB accessory has a valid
+     * URL associated with it.
+     *
+     * @param accessory The accessory to confirm in the UI dialog
+     * @param user The user to start the UI dialog
+     */
+    void showUsbAccessoryUriActivity(@NonNull UsbAccessory accessory,
+            @NonNull UserHandle user) {
+        String uri = accessory.getUri();
+        if (uri != null && uri.length() > 0) {
+            // display URI to user
+            Intent dialogIntent = createDialogIntent();
+            dialogIntent.setClassName("com.android.systemui",
+                    "com.android.systemui.usb.UsbAccessoryUriActivity");
+            dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+            dialogIntent.putExtra("uri", uri);
+            try {
+                mContext.startActivityAsUser(dialogIntent, user);
+            } catch (ActivityNotFoundException e) {
+                Slog.e(LOG_TAG, "unable to start UsbAccessoryUriActivity");
+            }
+        }
+    }
+
+    /**
+     * Shows dialog to user to confirm the package to start when the USB device
+     * or accessory is attached and there is only one package claims to handle this
+     * USB device or accessory.
+     *
+     * @param rInfo The ResolveInfo of the package to confirm in the UI dialog
+     * @param device The USB device to confirm
+     * @param accessory The USB accessory to confirm
+     */
+    void confirmUsbHandler(@NonNull ResolveInfo rInfo, @Nullable UsbDevice device,
+            @Nullable UsbAccessory accessory) {
+        Intent resolverIntent = createDialogIntent();
+        // start UsbConfirmActivity if there is only one choice
+        resolverIntent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbConfirmActivity");
+        resolverIntent.putExtra("rinfo", rInfo);
+        UserHandle user =
+                UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
+
+        if (device != null) {
+            resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        } else {
+            resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        }
+
+        try {
+            mContext.startActivityAsUser(resolverIntent, user);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+        }
+    }
+
+    /**
+     * Shows dialog to user to select the package to start when the USB device
+     * or accessory is attached and there are more than one package claim to handle this
+     * USB device or accessory.
+     *
+     * @param matches The available resolutions of the intent
+     * @param user The user to start UI dialog
+     * @param intent The intent to start the UI dialog
+     */
+    void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
+            @NonNull UserHandle user, @NonNull Intent intent) {
+        Intent resolverIntent = createDialogIntent();
+        resolverIntent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbResolverActivity");
+        resolverIntent.putParcelableArrayListExtra("rlist", matches);
+        resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+
+        try {
+            mContext.startActivityAsUser(resolverIntent, user);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+        }
+    }
+
+    private Intent createDialogIntent() {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
new file mode 100644
index 0000000..2c9ee36
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.usb.UsbSettingsAccessoryPermissionProto;
+import android.service.usb.UsbSettingsDevicePermissionProto;
+import android.service.usb.UsbUserSettingsManagerProto;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.HashMap;
+
+/**
+ * UsbPermissionManager manages usb device or accessory access permissions.
+ *
+ * @hide
+ */
+class UsbPermissionManager {
+    private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
+
+    @GuardedBy("mLock")
+    /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
+    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+            new HashMap<>();
+    @GuardedBy("mLock")
+    /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
+    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+            new HashMap<>();
+
+    private final UserHandle mUser;
+    private final boolean mDisablePermissionDialogs;
+
+    private final Object mLock = new Object();
+
+    UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
+        mUser = user;
+        mDisablePermissionDialogs = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+    }
+
+    /**
+     * Removes access permissions of all packages for the USB accessory.
+     *
+     * @param accessory to remove permissions for
+     */
+    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+        synchronized (mLock) {
+            mAccessoryPermissionMap.remove(accessory);
+        }
+    }
+
+    /**
+     * Removes access permissions of all packages for the USB device.
+     *
+     * @param device to remove permissions for
+     */
+    void removeDevicePermissions(@NonNull UsbDevice device) {
+        synchronized (mLock) {
+            mDevicePermissionMap.remove(device.getDeviceName());
+        }
+    }
+
+    /**
+     * Grants permission for USB device without showing system dialog for package with uid.
+     *
+     * @param device to grant permission for
+     * @param uid to grant permission for
+     */
+    void grantDevicePermission(@NonNull UsbDevice device, int uid) {
+        synchronized (mLock) {
+            String deviceName = device.getDeviceName();
+            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mDevicePermissionMap.put(deviceName, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    /**
+     * Grants permission for USB accessory without showing system dialog for package with uid.
+     *
+     * @param accessory to grant permission for
+     * @param uid to grant permission for
+     */
+    void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
+        synchronized (mLock) {
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mAccessoryPermissionMap.put(accessory, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    /**
+     * Returns true if package with uid has permission to access the device.
+     *
+     * @param device to check permission for
+     * @param uid to check permission for
+     * @return {@code true} if package with uid has permission
+     */
+    boolean hasPermission(@NonNull UsbDevice device, int uid) {
+        synchronized (mLock) {
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    /**
+     * Returns true if caller has permission to access the accessory.
+     *
+     * @param accessory to check permission for
+     * @return {@code true} if caller has permssion
+     */
+    boolean hasPermission(@NonNull UsbAccessory accessory) {
+        synchronized (mLock) {
+            int uid = Binder.getCallingUid();
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    /**
+     * Creates UI dialog to request permission for the given package to access the device
+     * or accessory.
+     *
+     * @param device The USB device attached
+     * @param accessory The USB accessory attached
+     * @param canBeDefault Whether the calling pacakge can set as default handler
+     * of the USB device or accessory
+     * @param packageName The package name of the calling package
+     * @param uid The uid of the calling package
+     * @param userContext The context to start the UI dialog
+     * @param pi PendingIntent for returning result
+     */
+    void requestPermissionDialog(@Nullable UsbDevice device,
+                                 @Nullable UsbAccessory accessory,
+                                 boolean canBeDefault,
+                                 @NonNull String packageName,
+                                 int uid,
+                                 @NonNull Context userContext,
+                                 @NonNull PendingIntent pi) {
+        long identity = Binder.clearCallingIdentity();
+        Intent intent = new Intent();
+        if (device != null) {
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        } else {
+            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        }
+        intent.putExtra(Intent.EXTRA_INTENT, pi);
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+        intent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbPermissionActivity");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        try {
+            userContext.startActivityAsUser(intent, mUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    void dump(@NonNull DualDumpOutputStream dump) {
+        synchronized (mLock) {
+            for (String deviceName : mDevicePermissionMap.keySet()) {
+                long devicePermissionToken = dump.start("device_permissions",
+                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+
+                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+
+                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+                }
+
+                dump.end(devicePermissionToken);
+            }
+
+            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+                long accessoryPermissionToken = dump.start("accessory_permissions",
+                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+
+                dump.write("accessory_description",
+                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+                        accessory.getDescription());
+
+                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+                }
+
+                dump.end(accessoryPermissionToken);
+            }
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 7a906d0..1ab1f7e 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -193,6 +193,8 @@
 
     MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
+    private final UsbHandlerManager mUsbHandlerManager;
+
     private final MtpNotificationManager mMtpNotificationManager;
 
     /**
@@ -201,9 +203,11 @@
      * @param context The context of the service
      * @param user The parent profile
      * @param settingsManager The settings manager of the service
+     * @param usbResolveActivityManager The resovle activity manager of the service
      */
     UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
-            @NonNull UsbSettingsManager settingsManager) {
+            @NonNull UsbSettingsManager settingsManager,
+            @NonNull UsbHandlerManager usbResolveActivityManager) {
         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
 
         Context parentUserContext;
@@ -238,6 +242,8 @@
                 parentUserContext,
                 device -> resolveActivity(createDeviceAttachedIntent(device),
                         device, false /* showMtpNotification */));
+
+        mUsbHandlerManager = usbResolveActivityManager;
     }
 
     /**
@@ -830,23 +836,8 @@
         // don't show the resolver activity if there are no choices available
         if (matches.size() == 0) {
             if (accessory != null) {
-                String uri = accessory.getUri();
-                if (uri != null && uri.length() > 0) {
-                    // display URI to user
-                    Intent dialogIntent = new Intent();
-                    dialogIntent.setClassName("com.android.systemui",
-                            "com.android.systemui.usb.UsbAccessoryUriActivity");
-                    dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                    dialogIntent.putExtra("uri", uri);
-                    try {
-                        mContext.startActivityAsUser(dialogIntent, mParentUser);
-                    } catch (ActivityNotFoundException e) {
-                        Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
-                    }
-                }
+                mUsbHandlerManager.showUsbAccessoryUriActivity(accessory, mParentUser);
             }
-
             // do nothing
             return;
         }
@@ -875,37 +866,10 @@
                 Slog.e(TAG, "startActivity failed", e);
             }
         } else {
-            Intent resolverIntent = new Intent();
-            resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            UserHandle user;
-
             if (matches.size() == 1) {
-                ResolveInfo rInfo = matches.get(0);
-
-                // start UsbConfirmActivity if there is only one choice
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbConfirmActivity");
-                resolverIntent.putExtra("rinfo", rInfo);
-                user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
-
-                if (device != null) {
-                    resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
-                } else {
-                    resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-                }
+                mUsbHandlerManager.confirmUsbHandler(matches.get(0), device, accessory);
             } else {
-                user = mParentUser;
-
-                // start UsbResolverActivity so user can choose an activity
-                resolverIntent.setClassName("com.android.systemui",
-                        "com.android.systemui.usb.UsbResolverActivity");
-                resolverIntent.putParcelableArrayListExtra("rlist", matches);
-                resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
-            }
-            try {
-                mContext.startActivityAsUser(resolverIntent, user);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "unable to start activity " + resolverIntent, e);
+                mUsbHandlerManager.selectUsbHandler(matches, mParentUser, intent);
             }
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 9221825..27566f0 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -57,10 +57,12 @@
     private final SparseArray<UsbProfileGroupSettingsManager> mSettingsByProfileGroup
             = new SparseArray<>();
     private UserManager mUserManager;
+    private UsbHandlerManager mUsbHandlerManager;
 
     public UsbSettingsManager(@NonNull Context context) {
         mContext = context;
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mUsbHandlerManager = new UsbHandlerManager(context);
     }
 
     /**
@@ -74,7 +76,8 @@
         synchronized (mSettingsByUser) {
             UsbUserSettingsManager settings = mSettingsByUser.get(userId);
             if (settings == null) {
-                settings = new UsbUserSettingsManager(mContext, new UserHandle(userId));
+                settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
+                        new UsbPermissionManager(mContext, UserHandle.of(userId)));
                 mSettingsByUser.put(userId, settings);
             }
             return settings;
@@ -102,7 +105,8 @@
             UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.get(
                     parentUser.getIdentifier());
             if (settings == null) {
-                settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this);
+                settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this,
+                      mUsbHandlerManager);
                 mSettingsByProfileGroup.put(parentUser.getIdentifier(), settings);
             }
             return settings;
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 24a2d72..fe93399 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -21,15 +21,18 @@
 import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.res.XmlResourceParser;
 import android.hardware.usb.AccessoryFilter;
 import android.hardware.usb.DeviceFilter;
 import android.hardware.usb.UsbAccessory;
@@ -38,42 +41,34 @@
 import android.hardware.usb.UsbInterface;
 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;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
 
+import com.android.internal.util.XmlUtils;
 import com.android.internal.util.dump.DualDumpOutputStream;
 
+import org.xmlpull.v1.XmlPullParser;
+
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 class UsbUserSettingsManager {
-    private static final String TAG = "UsbUserSettingsManager";
+    private static final String TAG = UsbUserSettingsManager.class.getSimpleName();
     private static final boolean DEBUG = false;
 
     private final UserHandle mUser;
-    private final boolean mDisablePermissionDialogs;
 
     private final Context mUserContext;
     private final PackageManager mPackageManager;
-
-    // Temporary mapping USB device name to list of UIDs with permissions for the device
-    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
-            new HashMap<>();
-    // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
-    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
-            new HashMap<>();
+    private final UsbPermissionManager mUsbPermissionManager;
 
     private final Object mLock = new Object();
 
-    public UsbUserSettingsManager(Context context, UserHandle user) {
+    UsbUserSettingsManager(Context context, UserHandle user,
+            @NonNull UsbPermissionManager usbPermissionManager) {
         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
 
         try {
@@ -85,9 +80,7 @@
         mPackageManager = mUserContext.getPackageManager();
 
         mUser = user;
-
-        mDisablePermissionDialogs = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+        mUsbPermissionManager = usbPermissionManager;
     }
 
     /**
@@ -96,9 +89,7 @@
      * @param device The device the permissions are for
      */
     void removeDevicePermissions(@NonNull UsbDevice device) {
-        synchronized (mLock) {
-            mDevicePermissionMap.remove(device.getDeviceName());
-        }
+        mUsbPermissionManager.removeDevicePermissions(device);
     }
 
     /**
@@ -107,9 +98,7 @@
      * @param accessory The accessory the permissions are for
      */
     void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
-        synchronized (mLock) {
-            mAccessoryPermissionMap.remove(accessory);
-        }
+        mUsbPermissionManager.removeAccessoryPermissions(accessory);
     }
 
     /**
@@ -170,35 +159,17 @@
     }
 
     public boolean hasPermission(UsbDevice device, String packageName, int uid) {
-        synchronized (mLock) {
-            if (isCameraDevicePresent(device)) {
-                if (!isCameraPermissionGranted(packageName, uid)) {
-                    return false;
-                }
-            }
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
-            if (uidList == null) {
+        if (isCameraDevicePresent(device)) {
+            if (!isCameraPermissionGranted(packageName, uid)) {
                 return false;
             }
-            return uidList.get(uid);
         }
+
+        return mUsbPermissionManager.hasPermission(device, uid);
     }
 
     public boolean hasPermission(UsbAccessory accessory) {
-        synchronized (mLock) {
-            int uid = Binder.getCallingUid();
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                return false;
-            }
-            return uidList.get(uid);
-        }
+        return mUsbPermissionManager.hasPermission(accessory);
     }
 
     public void checkPermission(UsbDevice device, String packageName, int uid) {
@@ -213,7 +184,11 @@
         }
     }
 
-    private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+    private void requestPermissionDialog(@Nullable UsbDevice device,
+                                         @Nullable UsbAccessory accessory,
+                                         boolean canBeDefault,
+                                         String packageName,
+                                         PendingIntent pi) {
         final int uid = Binder.getCallingUid();
 
         // compare uid with packageName to foil apps pretending to be someone else
@@ -227,27 +202,15 @@
             throw new IllegalArgumentException("package " + packageName + " not found");
         }
 
-        long identity = Binder.clearCallingIdentity();
-        intent.setClassName("com.android.systemui",
-                "com.android.systemui.usb.UsbPermissionActivity");
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(Intent.EXTRA_INTENT, pi);
-        intent.putExtra("package", packageName);
-        intent.putExtra(Intent.EXTRA_UID, uid);
-        try {
-            mUserContext.startActivityAsUser(intent, mUser);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(TAG, "unable to start UsbPermissionActivity");
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        mUsbPermissionManager.requestPermissionDialog(device,
+                accessory, canBeDefault, packageName, uid, mUserContext, pi);
     }
 
     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
-      Intent intent = new Intent();
+        Intent intent = new Intent();
 
         // respond immediately if permission has already been granted
-      if (hasPermission(device, packageName, uid)) {
+        if (hasPermission(device, packageName, uid)) {
             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
             try {
@@ -270,16 +233,13 @@
             }
         }
 
-        // start UsbPermissionActivity so user can choose an activity
-        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        requestPermissionDialog(intent, packageName, pi);
+        requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi);
     }
 
     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
-        Intent intent = new Intent();
-
         // respond immediately if permission has already been granted
         if (hasPermission(accessory)) {
+            Intent intent = new Intent();
             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
             try {
@@ -290,31 +250,16 @@
             return;
         }
 
-        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        requestPermissionDialog(intent, packageName, pi);
+        requestPermissionDialog(null, accessory,
+                canBeDefault(accessory, packageName), packageName, pi);
     }
 
     public void grantDevicePermission(UsbDevice device, int uid) {
-        synchronized (mLock) {
-            String deviceName = device.getDeviceName();
-            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mDevicePermissionMap.put(deviceName, uidList);
-            }
-            uidList.put(uid, true);
-        }
+        mUsbPermissionManager.grantDevicePermission(device, uid);
     }
 
     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
-        synchronized (mLock) {
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mAccessoryPermissionMap.put(accessory, uidList);
-            }
-            uidList.put(uid, true);
-        }
+        mUsbPermissionManager.grantAccessoryPermission(accessory, uid);
     }
 
     /**
@@ -329,42 +274,108 @@
                 mUser.getIdentifier());
     }
 
+    /**
+     * Can the app be the default for the USB device. I.e. can the app be launched by default if
+     * the device is plugged in.
+     *
+     * @param device The device the app would be default for
+     * @param packageName The package name of the app
+     *
+     * @return {@code true} if the app can be default
+     */
+    private boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
+        ActivityInfo[] activities = getPackageActivities(packageName);
+        if (activities != null) {
+            int numActivities = activities.length;
+            for (int i = 0; i < numActivities; i++) {
+                ActivityInfo activityInfo = activities[i];
+
+                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+                        UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                    if (parser == null) {
+                        continue;
+                    }
+
+                    XmlUtils.nextElement(parser);
+                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if ("usb-device".equals(parser.getName())) {
+                            DeviceFilter filter = DeviceFilter.read(parser);
+                            if (filter.matches(device)) {
+                                return true;
+                            }
+                        }
+
+                        XmlUtils.nextElement(parser);
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+     * the accessory is plugged in.
+     *
+     * @param accessory The accessory the app would be default for
+     * @param packageName The package name of the app
+     *
+     * @return {@code true} if the app can be default
+     */
+    private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
+        ActivityInfo[] activities = getPackageActivities(packageName);
+        if (activities != null) {
+            int numActivities = activities.length;
+            for (int i = 0; i < numActivities; i++) {
+                ActivityInfo activityInfo = activities[i];
+
+                try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+                        UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+                    if (parser == null) {
+                        continue;
+                    }
+
+                    XmlUtils.nextElement(parser);
+                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                        if ("usb-accessory".equals(parser.getName())) {
+                            AccessoryFilter filter = AccessoryFilter.read(parser);
+                            if (filter.matches(accessory)) {
+                                return true;
+                            }
+                        }
+
+                        XmlUtils.nextElement(parser);
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private ActivityInfo[] getPackageActivities(String packageName) {
+        try {
+            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+                    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+            return packageInfo.activities;
+        } catch (PackageManager.NameNotFoundException e) {
+            // ignore
+        }
+        return null;
+    }
+
     public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
         long token = dump.start(idName, id);
 
         synchronized (mLock) {
             dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
 
-            for (String deviceName : mDevicePermissionMap.keySet()) {
-                long devicePermissionToken = dump.start("device_permissions",
-                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
-
-                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
-
-                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
-                }
-
-                dump.end(devicePermissionToken);
-            }
-            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
-                long accessoryPermissionToken = dump.start("accessory_permissions",
-                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
-
-                dump.write("accessory_description",
-                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
-                        accessory.getDescription());
-
-                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
-                }
-
-                dump.end(accessoryPermissionToken);
-            }
+            mUsbPermissionManager.dump(dump);
 
             List<ResolveInfo> deviceAttachedActivities = queryIntentActivities(
                     new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED));