Adding additional prompt to UsbPermissionsDialg for audio devices.

To support, adding members to UsbDevice to mark devices as having audio
playback and audio capture capabilities.

Bug: 136080195
Test: Run "UsbAccess" test bed. Connect audio and non-audio USB devices
and see the additional prompt shown/not-shown.

Change-Id: Ie7c614d9ed30a163c350b18a54b8a9115698779d
This commit is contained in:
Paul McLean
2019-09-23 08:28:41 -06:00
parent 4252183449
commit d336453217
6 changed files with 101 additions and 24 deletions

View File

@@ -60,6 +60,9 @@ public class UsbDevice implements Parcelable {
private final int mClass;
private final int mSubclass;
private final int mProtocol;
private final boolean mHasAudioPlayback;
private final boolean mHasAudioCapture;
/** All interfaces on the device. Initialized on first call to getInterfaceList */
@UnsupportedAppUsage
@@ -73,7 +76,8 @@ public class UsbDevice implements Parcelable {
private UsbDevice(@NonNull String name, int vendorId, int productId, int Class, int subClass,
int protocol, @Nullable String manufacturerName, @Nullable String productName,
@NonNull String version, @NonNull UsbConfiguration[] configurations,
@NonNull IUsbSerialReader serialNumberReader) {
@NonNull IUsbSerialReader serialNumberReader,
boolean hasAudioPlayback, boolean hasAudioCapture) {
mName = Preconditions.checkNotNull(name);
mVendorId = vendorId;
mProductId = productId;
@@ -85,6 +89,8 @@ public class UsbDevice implements Parcelable {
mVersion = Preconditions.checkStringNotEmpty(version);
mConfigurations = Preconditions.checkArrayElementsNotNull(configurations, "configurations");
mSerialNumberReader = Preconditions.checkNotNull(serialNumberReader);
mHasAudioPlayback = hasAudioPlayback;
mHasAudioCapture = hasAudioCapture;
// Make sure the binder belongs to the system
if (ActivityThread.isSystem()) {
@@ -214,6 +220,16 @@ public class UsbDevice implements Parcelable {
return mConfigurations.length;
}
/** @hide */
public boolean getHasAudioPlayback() {
return mHasAudioPlayback;
}
/** @hide */
public boolean getHasAudioCapture() {
return mHasAudioCapture;
}
/**
* Returns the {@link UsbConfiguration} at the given index.
*
@@ -286,12 +302,14 @@ public class UsbDevice implements Parcelable {
@Override
public String toString() {
StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName +
",mVendorId=" + mVendorId + ",mProductId=" + mProductId +
",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName +
",mVersion=" + mVersion + ",mSerialNumberReader=" + mSerialNumberReader
+ ",mConfigurations=[");
StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName
+ ",mVendorId=" + mVendorId + ",mProductId=" + mProductId
+ ",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol
+ ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName
+ ",mVersion=" + mVersion + ",mSerialNumberReader=" + mSerialNumberReader
+ ", mHasAudioPlayback=" + mHasAudioPlayback
+ ", mHasAudioCapture=" + mHasAudioCapture
+ ", mConfigurations=[");
for (int i = 0; i < mConfigurations.length; i++) {
builder.append("\n");
builder.append(mConfigurations[i].toString());
@@ -316,9 +334,13 @@ public class UsbDevice implements Parcelable {
IUsbSerialReader.Stub.asInterface(in.readStrongBinder());
UsbConfiguration[] configurations = in.readParcelableArray(
UsbConfiguration.class.getClassLoader(), UsbConfiguration.class);
// Capabilities
boolean hasAudioPlayback = in.readInt() == 1;
boolean hasAudioCapture = in.readInt() == 1;
UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
manufacturerName, productName, version, configurations,
serialNumberReader);
manufacturerName, productName, version, configurations, serialNumberReader,
hasAudioPlayback, hasAudioCapture);
return device;
}
@@ -343,6 +365,8 @@ public class UsbDevice implements Parcelable {
parcel.writeString(mVersion);
parcel.writeStrongBinder(mSerialNumberReader.asBinder());
parcel.writeParcelableArray(mConfigurations, 0);
parcel.writeInt(mHasAudioPlayback ? 1 : 0);
parcel.writeInt(mHasAudioCapture ? 1 : 0);
}
public static int getDeviceId(String name) {
@@ -370,6 +394,8 @@ public class UsbDevice implements Parcelable {
private final @Nullable String mProductName;
private final @NonNull String mVersion;
private final @NonNull UsbConfiguration[] mConfigurations;
private final boolean mHasAudioPlayback;
private final boolean mHasAudioCapture;
// Temporary storage for serial number. Serial number reader need to be wrapped in a
// IUsbSerialReader as they might be used as PII.
@@ -378,7 +404,8 @@ public class UsbDevice implements Parcelable {
public Builder(@NonNull String name, int vendorId, int productId, int Class, int subClass,
int protocol, @Nullable String manufacturerName, @Nullable String productName,
@NonNull String version, @NonNull UsbConfiguration[] configurations,
@Nullable String serialNumber) {
@Nullable String serialNumber,
boolean hasAudioPlayback, boolean hasAudioCapture) {
mName = Preconditions.checkNotNull(name);
mVendorId = vendorId;
mProductId = productId;
@@ -390,6 +417,8 @@ public class UsbDevice implements Parcelable {
mVersion = Preconditions.checkStringNotEmpty(version);
mConfigurations = configurations;
this.serialNumber = serialNumber;
mHasAudioPlayback = hasAudioPlayback;
mHasAudioCapture = hasAudioCapture;
}
/**
@@ -401,7 +430,8 @@ public class UsbDevice implements Parcelable {
*/
public UsbDevice build(@NonNull IUsbSerialReader serialReader) {
return new UsbDevice(mName, mVendorId, mProductId, mClass, mSubclass, mProtocol,
mManufacturerName, mProductName, mVersion, mConfigurations, serialReader);
mManufacturerName, mProductName, mVersion, mConfigurations, serialReader,
mHasAudioPlayback, mHasAudioCapture);
}
}
}

View File

@@ -118,10 +118,13 @@
<string name="status_bar_use_physical_keyboard">Physical keyboard</string>
<!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
<string name="usb_device_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_device">%2$s</xliff:g>?</string>
<string name="usb_device_permission_prompt">Allow <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to access <xliff:g id="usb_device" example="USB Headphones">%2$s</xliff:g>?</string>
<!-- Checkbox label for USB device dialogs with warning text for USB device dialogs. [CHAR LIMIT=200]-->
<string name="usb_device_permission_prompt_warn">Allow <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to access <xliff:g id="usb_device" example="USB Headphones">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device.</string>
<!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
<string name="usb_accessory_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
<string name="usb_accessory_permission_prompt">Allow <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to access <xliff:g id="usb_accessory" example="USB Dock">%2$s</xliff:g>?</string>
<!-- Prompt for the USB device confirm dialog [CHAR LIMIT=80] -->
<string name="usb_device_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_device">%2$s</xliff:g>?</string>

View File

@@ -21,6 +21,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
@@ -84,14 +85,27 @@ public class UsbPermissionActivity extends AlertActivity
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = appName;
if (mDevice == null) {
// Accessory Case
ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName,
mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
ap.mMessage = getString(R.string.usb_device_permission_prompt, appName,
mDevice.getProductName());
boolean hasRecordPermission =
PermissionChecker.checkPermission(
this, android.Manifest.permission.RECORD_AUDIO, -1, aInfo.uid,
mPackageName)
== android.content.pm.PackageManager.PERMISSION_GRANTED;
boolean isAudioCaptureDevice = mDevice.getHasAudioCapture();
boolean useRecordWarning = isAudioCaptureDevice && !hasRecordPermission;
int strID = useRecordWarning
? R.string.usb_device_permission_prompt_warn
: R.string.usb_device_permission_prompt;
ap.mMessage = getString(strID, appName, mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
ap.mPositiveButtonText = getString(android.R.string.ok);
ap.mNegativeButtonText = getString(android.R.string.cancel);
ap.mPositiveButtonListener = this;

View File

@@ -386,7 +386,7 @@ public class UsbHostManager {
return false;
}
UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDevice();
UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
if (newDeviceBuilder == null) {
Slog.e(TAG, "Couldn't create UsbDevice object.");
// Tracking

View File

@@ -309,17 +309,17 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
public UsbDevice.Builder toAndroidUsbDevice() {
public UsbDevice.Builder toAndroidUsbDeviceBuilder() {
if (mDeviceDescriptor == null) {
Log.e(TAG, "toAndroidUsbDevice() ERROR - No Device Descriptor");
return null;
}
UsbDevice.Builder device = mDeviceDescriptor.toAndroid(this);
if (device == null) {
UsbDevice.Builder builder = mDeviceDescriptor.toAndroid(this);
if (builder == null) {
Log.e(TAG, "toAndroidUsbDevice() ERROR Creating Device");
}
return device;
return builder;
}
/**
@@ -521,6 +521,37 @@ public final class UsbDescriptorParser {
return !descriptors.isEmpty();
}
/**
* @hide
*/
public boolean hasAudioTerminal(int subType) {
for (UsbDescriptor descriptor : mDescriptors) {
if (descriptor instanceof UsbACInterface) {
if (((UsbACInterface) descriptor).getSubclass()
== UsbDescriptor.AUDIO_AUDIOCONTROL
&& ((UsbACInterface) descriptor).getSubtype()
== subType) {
return true;
}
}
}
return false;
}
/**
* @hide
*/
public boolean hasAudioPlayback() {
return hasAudioTerminal(UsbACInterface.ACI_OUTPUT_TERMINAL);
}
/**
* @hide
*/
public boolean hasAudioCapture() {
return hasAudioTerminal(UsbACInterface.ACI_INPUT_TERMINAL);
}
/**
* @hide
*/

View File

@@ -156,11 +156,10 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
for (int index = 0; index < mConfigDescriptors.size(); index++) {
configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
}
UsbDevice.Builder device = new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
configs, serialStr);
return device;
return new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
configs, serialStr, parser.hasAudioPlayback(), parser.hasAudioCapture());
}
@Override