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));