diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java new file mode 100644 index 0000000000000..d9b7c5be7dddd --- /dev/null +++ b/core/java/android/hardware/usb/AccessoryFilter.java @@ -0,0 +1,145 @@ +/* + * Copyright 2017 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 android.hardware.usb; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.Objects; + +/** + * This class is used to describe a USB accessory. + * When used in HashMaps all values must be specified, + * but wildcards can be used for any of the fields in + * the package meta-data. + * + * @hide + */ +public class AccessoryFilter { + // USB accessory manufacturer (or null for unspecified) + public final String mManufacturer; + // USB accessory model (or null for unspecified) + public final String mModel; + // USB accessory version (or null for unspecified) + public final String mVersion; + + public AccessoryFilter(String manufacturer, String model, String version) { + mManufacturer = manufacturer; + mModel = model; + mVersion = version; + } + + public AccessoryFilter(UsbAccessory accessory) { + mManufacturer = accessory.getManufacturer(); + mModel = accessory.getModel(); + mVersion = accessory.getVersion(); + } + + public static AccessoryFilter read(XmlPullParser parser) + throws XmlPullParserException, IOException { + String manufacturer = null; + String model = null; + String version = null; + + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + String name = parser.getAttributeName(i); + String value = parser.getAttributeValue(i); + + if ("manufacturer".equals(name)) { + manufacturer = value; + } else if ("model".equals(name)) { + model = value; + } else if ("version".equals(name)) { + version = value; + } + } + return new AccessoryFilter(manufacturer, model, version); + } + + public void write(XmlSerializer serializer)throws IOException { + serializer.startTag(null, "usb-accessory"); + if (mManufacturer != null) { + serializer.attribute(null, "manufacturer", mManufacturer); + } + if (mModel != null) { + serializer.attribute(null, "model", mModel); + } + if (mVersion != null) { + serializer.attribute(null, "version", mVersion); + } + serializer.endTag(null, "usb-accessory"); + } + + public boolean matches(UsbAccessory acc) { + if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false; + if (mModel != null && !acc.getModel().equals(mModel)) return false; + return !(mVersion != null && !acc.getVersion().equals(mVersion)); + } + + /** + * Is the accessories described {@code accessory} covered by this filter? + * + * @param accessory A filter describing the accessory + * + * @return {@code true} iff this the filter covers the accessory + */ + public boolean contains(AccessoryFilter accessory) { + if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) { + return false; + } + if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false; + return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion)); + } + + @Override + public boolean equals(Object obj) { + // can't compare if we have wildcard strings + if (mManufacturer == null || mModel == null || mVersion == null) { + return false; + } + if (obj instanceof AccessoryFilter) { + AccessoryFilter filter = (AccessoryFilter)obj; + return (mManufacturer.equals(filter.mManufacturer) && + mModel.equals(filter.mModel) && + mVersion.equals(filter.mVersion)); + } + if (obj instanceof UsbAccessory) { + UsbAccessory accessory = (UsbAccessory)obj; + return (mManufacturer.equals(accessory.getManufacturer()) && + mModel.equals(accessory.getModel()) && + mVersion.equals(accessory.getVersion())); + } + return false; + } + + @Override + public int hashCode() { + return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ + (mModel == null ? 0 : mModel.hashCode()) ^ + (mVersion == null ? 0 : mVersion.hashCode())); + } + + @Override + public String toString() { + return "AccessoryFilter[mManufacturer=\"" + mManufacturer + + "\", mModel=\"" + mModel + + "\", mVersion=\"" + mVersion + "\"]"; + } +} diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java new file mode 100644 index 0000000000000..439c629758b04 --- /dev/null +++ b/core/java/android/hardware/usb/DeviceFilter.java @@ -0,0 +1,313 @@ +/* + * Copyright 2017 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 android.hardware.usb; + +import android.util.Slog; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.Objects; + +/** + * This class is used to describe a USB device. + * When used in HashMaps all values must be specified, + * but wildcards can be used for any of the fields in + * the package meta-data. + * + * @hide + */ +public class DeviceFilter { + private static final String TAG = DeviceFilter.class.getSimpleName(); + + // USB Vendor ID (or -1 for unspecified) + public final int mVendorId; + // USB Product ID (or -1 for unspecified) + public final int mProductId; + // USB device or interface class (or -1 for unspecified) + public final int mClass; + // USB device subclass (or -1 for unspecified) + public final int mSubclass; + // USB device protocol (or -1 for unspecified) + public final int mProtocol; + // USB device manufacturer name string (or null for unspecified) + public final String mManufacturerName; + // USB device product name string (or null for unspecified) + public final String mProductName; + // USB device serial number string (or null for unspecified) + public final String mSerialNumber; + + public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, + String manufacturer, String product, String serialnum) { + mVendorId = vid; + mProductId = pid; + mClass = clasz; + mSubclass = subclass; + mProtocol = protocol; + mManufacturerName = manufacturer; + mProductName = product; + mSerialNumber = serialnum; + } + + public DeviceFilter(UsbDevice device) { + mVendorId = device.getVendorId(); + mProductId = device.getProductId(); + mClass = device.getDeviceClass(); + mSubclass = device.getDeviceSubclass(); + mProtocol = device.getDeviceProtocol(); + mManufacturerName = device.getManufacturerName(); + mProductName = device.getProductName(); + mSerialNumber = device.getSerialNumber(); + } + + public static DeviceFilter read(XmlPullParser parser) + throws XmlPullParserException, IOException { + int vendorId = -1; + int productId = -1; + int deviceClass = -1; + int deviceSubclass = -1; + int deviceProtocol = -1; + String manufacturerName = null; + String productName = null; + String serialNumber = null; + + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + String name = parser.getAttributeName(i); + String value = parser.getAttributeValue(i); + // Attribute values are ints or strings + if ("manufacturer-name".equals(name)) { + manufacturerName = value; + } else if ("product-name".equals(name)) { + productName = value; + } else if ("serial-number".equals(name)) { + serialNumber = value; + } else { + int intValue; + int radix = 10; + if (value != null && value.length() > 2 && value.charAt(0) == '0' && + (value.charAt(1) == 'x' || value.charAt(1) == 'X')) { + // allow hex values starting with 0x or 0X + radix = 16; + value = value.substring(2); + } + try { + intValue = Integer.parseInt(value, radix); + } catch (NumberFormatException e) { + Slog.e(TAG, "invalid number for field " + name, e); + continue; + } + if ("vendor-id".equals(name)) { + vendorId = intValue; + } else if ("product-id".equals(name)) { + productId = intValue; + } else if ("class".equals(name)) { + deviceClass = intValue; + } else if ("subclass".equals(name)) { + deviceSubclass = intValue; + } else if ("protocol".equals(name)) { + deviceProtocol = intValue; + } + } + } + return new DeviceFilter(vendorId, productId, + deviceClass, deviceSubclass, deviceProtocol, + manufacturerName, productName, serialNumber); + } + + public void write(XmlSerializer serializer) throws IOException { + serializer.startTag(null, "usb-device"); + if (mVendorId != -1) { + serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); + } + if (mProductId != -1) { + serializer.attribute(null, "product-id", Integer.toString(mProductId)); + } + if (mClass != -1) { + serializer.attribute(null, "class", Integer.toString(mClass)); + } + if (mSubclass != -1) { + serializer.attribute(null, "subclass", Integer.toString(mSubclass)); + } + if (mProtocol != -1) { + serializer.attribute(null, "protocol", Integer.toString(mProtocol)); + } + if (mManufacturerName != null) { + serializer.attribute(null, "manufacturer-name", mManufacturerName); + } + if (mProductName != null) { + serializer.attribute(null, "product-name", mProductName); + } + if (mSerialNumber != null) { + serializer.attribute(null, "serial-number", mSerialNumber); + } + serializer.endTag(null, "usb-device"); + } + + private boolean matches(int clasz, int subclass, int protocol) { + return ((mClass == -1 || clasz == mClass) && + (mSubclass == -1 || subclass == mSubclass) && + (mProtocol == -1 || protocol == mProtocol)); + } + + public boolean matches(UsbDevice device) { + if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; + if (mProductId != -1 && device.getProductId() != mProductId) return false; + if (mManufacturerName != null && device.getManufacturerName() == null) return false; + if (mProductName != null && device.getProductName() == null) return false; + if (mSerialNumber != null && device.getSerialNumber() == null) return false; + if (mManufacturerName != null && device.getManufacturerName() != null && + !mManufacturerName.equals(device.getManufacturerName())) return false; + if (mProductName != null && device.getProductName() != null && + !mProductName.equals(device.getProductName())) return false; + if (mSerialNumber != null && device.getSerialNumber() != null && + !mSerialNumber.equals(device.getSerialNumber())) return false; + + // check device class/subclass/protocol + if (matches(device.getDeviceClass(), device.getDeviceSubclass(), + device.getDeviceProtocol())) return true; + + // if device doesn't match, check the interfaces + int count = device.getInterfaceCount(); + for (int i = 0; i < count; i++) { + UsbInterface intf = device.getInterface(i); + if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), + intf.getInterfaceProtocol())) return true; + } + + return false; + } + + /** + * If the device described by {@code device} covered by this filter? + * + * @param device The device + * + * @return {@code true} iff this filter covers the {@code device} + */ + public boolean contains(DeviceFilter device) { + // -1 and null means "match anything" + + if (mVendorId != -1 && device.mVendorId != mVendorId) return false; + if (mProductId != -1 && device.mProductId != mProductId) return false; + if (mManufacturerName != null && !Objects.equals(mManufacturerName, + device.mManufacturerName)) { + return false; + } + if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) { + return false; + } + if (mSerialNumber != null + && !Objects.equals(mSerialNumber, device.mSerialNumber)) { + return false; + } + + // check device class/subclass/protocol + return matches(device.mClass, device.mSubclass, device.mProtocol); + } + + @Override + public boolean equals(Object obj) { + // can't compare if we have wildcard strings + if (mVendorId == -1 || mProductId == -1 || + mClass == -1 || mSubclass == -1 || mProtocol == -1) { + return false; + } + if (obj instanceof DeviceFilter) { + DeviceFilter filter = (DeviceFilter)obj; + + if (filter.mVendorId != mVendorId || + filter.mProductId != mProductId || + filter.mClass != mClass || + filter.mSubclass != mSubclass || + filter.mProtocol != mProtocol) { + return(false); + } + if ((filter.mManufacturerName != null && + mManufacturerName == null) || + (filter.mManufacturerName == null && + mManufacturerName != null) || + (filter.mProductName != null && + mProductName == null) || + (filter.mProductName == null && + mProductName != null) || + (filter.mSerialNumber != null && + mSerialNumber == null) || + (filter.mSerialNumber == null && + mSerialNumber != null)) { + return(false); + } + if ((filter.mManufacturerName != null && + mManufacturerName != null && + !mManufacturerName.equals(filter.mManufacturerName)) || + (filter.mProductName != null && + mProductName != null && + !mProductName.equals(filter.mProductName)) || + (filter.mSerialNumber != null && + mSerialNumber != null && + !mSerialNumber.equals(filter.mSerialNumber))) { + return false; + } + return true; + } + if (obj instanceof UsbDevice) { + UsbDevice device = (UsbDevice)obj; + if (device.getVendorId() != mVendorId || + device.getProductId() != mProductId || + device.getDeviceClass() != mClass || + device.getDeviceSubclass() != mSubclass || + device.getDeviceProtocol() != mProtocol) { + return(false); + } + if ((mManufacturerName != null && device.getManufacturerName() == null) || + (mManufacturerName == null && device.getManufacturerName() != null) || + (mProductName != null && device.getProductName() == null) || + (mProductName == null && device.getProductName() != null) || + (mSerialNumber != null && device.getSerialNumber() == null) || + (mSerialNumber == null && device.getSerialNumber() != null)) { + return(false); + } + if ((device.getManufacturerName() != null && + !mManufacturerName.equals(device.getManufacturerName())) || + (device.getProductName() != null && + !mProductName.equals(device.getProductName())) || + (device.getSerialNumber() != null && + !mSerialNumber.equals(device.getSerialNumber()))) { + return false; + } + return true; + } + return false; + } + + @Override + public int hashCode() { + return (((mVendorId << 16) | mProductId) ^ + ((mClass << 16) | (mSubclass << 8) | mProtocol)); + } + + @Override + public String toString() { + return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + + ",mClass=" + mClass + ",mSubclass=" + mSubclass + + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + + "]"; + } +} diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 1536b64dc41f1..b132160d45043 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -124,16 +124,16 @@ Physical keyboard - Allow the app %1$s to access the USB device? + Allow %1$s to access %2$s? - Allow the app %1$s to access the USB accessory? + Allow %1$s to access %2$s? - Open %1$s when this USB device is connected? + Open %1$s to handle %2$s? - Open %1$s when this USB accessory is connected? + Open %1$s to handle %2$s? No installed apps work with this USB accessory. Learn more about this accessory at %1$s @@ -145,10 +145,10 @@ View - Use by default for this USB device + Always open %1$s when %2$s is connected - - Use by default for this USB accessory + + Always open %1$s when %2$s is connected Allow USB debugging? diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java index 3eccccdd75bf6..e117969c5993f 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java @@ -71,10 +71,12 @@ public class UsbConfirmActivity extends AlertActivity ap.mIcon = mResolveInfo.loadIcon(packageManager); ap.mTitle = appName; if (mDevice == null) { - ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName); + ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName, + mAccessory.getDescription()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); } else { - ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName); + ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName, + mDevice.getProductName()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } ap.mPositiveButtonText = getString(android.R.string.ok); @@ -88,9 +90,11 @@ public class UsbConfirmActivity extends AlertActivity 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(R.string.always_use_accessory); + mAlwaysUse.setText(getString(R.string.always_use_accessory, appName, + mAccessory.getDescription())); } else { - mAlwaysUse.setText(R.string.always_use_device); + mAlwaysUse.setText(getString(R.string.always_use_device, appName, + mDevice.getProductName())); } mAlwaysUse.setOnCheckedChangeListener(this); mClearDefaultHint = (TextView)ap.mView.findViewById( diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index 1e69fc5ce1fcb..87d11b2408b12 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -16,13 +16,17 @@ 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; @@ -41,8 +45,13 @@ import android.widget.TextView; 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 { @@ -84,10 +93,12 @@ public class UsbPermissionActivity extends AlertActivity ap.mIcon = aInfo.loadIcon(packageManager); ap.mTitle = appName; if (mDevice == null) { - ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName); + 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); + ap.mMessage = getString(R.string.usb_device_permission_prompt, appName, + mDevice.getProductName()); mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); } ap.mPositiveButtonText = getString(android.R.string.ok); @@ -95,25 +106,123 @@ public class UsbPermissionActivity extends AlertActivity ap.mPositiveButtonListener = this; ap.mNegativeButtonListener = this; - // add "always use" 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(R.string.always_use_accessory); - } else { - mAlwaysUse.setText(R.string.always_use_device); + 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); + } + } 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 public void onDestroy() { IBinder b = ServiceManager.getService(USB_SERVICE); diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index ebb5a62ce7ece..917e651bb4255 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -32,9 +32,10 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.XmlResourceParser; +import android.hardware.usb.AccessoryFilter; +import android.hardware.usb.DeviceFilter; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.AsyncTask; import android.os.Environment; @@ -58,7 +59,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -71,7 +71,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; class UsbProfileGroupSettingsManager { private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName(); @@ -157,404 +156,6 @@ class UsbProfileGroupSettingsManager { } } - // This class is used to describe a USB device. - // When used in HashMaps all values must be specified, - // but wildcards can be used for any of the fields in - // the package meta-data. - private static class DeviceFilter { - // USB Vendor ID (or -1 for unspecified) - public final int mVendorId; - // USB Product ID (or -1 for unspecified) - public final int mProductId; - // USB device or interface class (or -1 for unspecified) - public final int mClass; - // USB device subclass (or -1 for unspecified) - public final int mSubclass; - // USB device protocol (or -1 for unspecified) - public final int mProtocol; - // USB device manufacturer name string (or null for unspecified) - public final String mManufacturerName; - // USB device product name string (or null for unspecified) - public final String mProductName; - // USB device serial number string (or null for unspecified) - public final String mSerialNumber; - - public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, - String manufacturer, String product, String serialnum) { - mVendorId = vid; - mProductId = pid; - mClass = clasz; - mSubclass = subclass; - mProtocol = protocol; - mManufacturerName = manufacturer; - mProductName = product; - mSerialNumber = serialnum; - } - - public DeviceFilter(UsbDevice device) { - mVendorId = device.getVendorId(); - mProductId = device.getProductId(); - mClass = device.getDeviceClass(); - mSubclass = device.getDeviceSubclass(); - mProtocol = device.getDeviceProtocol(); - mManufacturerName = device.getManufacturerName(); - mProductName = device.getProductName(); - mSerialNumber = device.getSerialNumber(); - } - - public static DeviceFilter read(XmlPullParser parser) - throws XmlPullParserException, IOException { - int vendorId = -1; - int productId = -1; - int deviceClass = -1; - int deviceSubclass = -1; - int deviceProtocol = -1; - String manufacturerName = null; - String productName = null; - String serialNumber = null; - - int count = parser.getAttributeCount(); - for (int i = 0; i < count; i++) { - String name = parser.getAttributeName(i); - String value = parser.getAttributeValue(i); - // Attribute values are ints or strings - if ("manufacturer-name".equals(name)) { - manufacturerName = value; - } else if ("product-name".equals(name)) { - productName = value; - } else if ("serial-number".equals(name)) { - serialNumber = value; - } else { - int intValue; - int radix = 10; - if (value != null && value.length() > 2 && value.charAt(0) == '0' && - (value.charAt(1) == 'x' || value.charAt(1) == 'X')) { - // allow hex values starting with 0x or 0X - radix = 16; - value = value.substring(2); - } - try { - intValue = Integer.parseInt(value, radix); - } catch (NumberFormatException e) { - Slog.e(TAG, "invalid number for field " + name, e); - continue; - } - if ("vendor-id".equals(name)) { - vendorId = intValue; - } else if ("product-id".equals(name)) { - productId = intValue; - } else if ("class".equals(name)) { - deviceClass = intValue; - } else if ("subclass".equals(name)) { - deviceSubclass = intValue; - } else if ("protocol".equals(name)) { - deviceProtocol = intValue; - } - } - } - return new DeviceFilter(vendorId, productId, - deviceClass, deviceSubclass, deviceProtocol, - manufacturerName, productName, serialNumber); - } - - public void write(XmlSerializer serializer) throws IOException { - serializer.startTag(null, "usb-device"); - if (mVendorId != -1) { - serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); - } - if (mProductId != -1) { - serializer.attribute(null, "product-id", Integer.toString(mProductId)); - } - if (mClass != -1) { - serializer.attribute(null, "class", Integer.toString(mClass)); - } - if (mSubclass != -1) { - serializer.attribute(null, "subclass", Integer.toString(mSubclass)); - } - if (mProtocol != -1) { - serializer.attribute(null, "protocol", Integer.toString(mProtocol)); - } - if (mManufacturerName != null) { - serializer.attribute(null, "manufacturer-name", mManufacturerName); - } - if (mProductName != null) { - serializer.attribute(null, "product-name", mProductName); - } - if (mSerialNumber != null) { - serializer.attribute(null, "serial-number", mSerialNumber); - } - serializer.endTag(null, "usb-device"); - } - - private boolean matches(int clasz, int subclass, int protocol) { - return ((mClass == -1 || clasz == mClass) && - (mSubclass == -1 || subclass == mSubclass) && - (mProtocol == -1 || protocol == mProtocol)); - } - - public boolean matches(UsbDevice device) { - if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; - if (mProductId != -1 && device.getProductId() != mProductId) return false; - if (mManufacturerName != null && device.getManufacturerName() == null) return false; - if (mProductName != null && device.getProductName() == null) return false; - if (mSerialNumber != null && device.getSerialNumber() == null) return false; - if (mManufacturerName != null && device.getManufacturerName() != null && - !mManufacturerName.equals(device.getManufacturerName())) return false; - if (mProductName != null && device.getProductName() != null && - !mProductName.equals(device.getProductName())) return false; - if (mSerialNumber != null && device.getSerialNumber() != null && - !mSerialNumber.equals(device.getSerialNumber())) return false; - - // check device class/subclass/protocol - if (matches(device.getDeviceClass(), device.getDeviceSubclass(), - device.getDeviceProtocol())) return true; - - // if device doesn't match, check the interfaces - int count = device.getInterfaceCount(); - for (int i = 0; i < count; i++) { - UsbInterface intf = device.getInterface(i); - if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), - intf.getInterfaceProtocol())) return true; - } - - return false; - } - - /** - * If the device described by {@code device} covered by this filter? - * - * @param device The device - * - * @return {@code true} iff this filter covers the {@code device} - */ - public boolean contains(DeviceFilter device) { - // -1 and null means "match anything" - - if (mVendorId != -1 && device.mVendorId != mVendorId) return false; - if (mProductId != -1 && device.mProductId != mProductId) return false; - if (mManufacturerName != null && !Objects.equals(mManufacturerName, - device.mManufacturerName)) { - return false; - } - if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) { - return false; - } - if (mSerialNumber != null - && !Objects.equals(mSerialNumber, device.mSerialNumber)) { - return false; - } - - // check device class/subclass/protocol - return matches(device.mClass, device.mSubclass, device.mProtocol); - } - - @Override - public boolean equals(Object obj) { - // can't compare if we have wildcard strings - if (mVendorId == -1 || mProductId == -1 || - mClass == -1 || mSubclass == -1 || mProtocol == -1) { - return false; - } - if (obj instanceof DeviceFilter) { - DeviceFilter filter = (DeviceFilter)obj; - - if (filter.mVendorId != mVendorId || - filter.mProductId != mProductId || - filter.mClass != mClass || - filter.mSubclass != mSubclass || - filter.mProtocol != mProtocol) { - return(false); - } - if ((filter.mManufacturerName != null && - mManufacturerName == null) || - (filter.mManufacturerName == null && - mManufacturerName != null) || - (filter.mProductName != null && - mProductName == null) || - (filter.mProductName == null && - mProductName != null) || - (filter.mSerialNumber != null && - mSerialNumber == null) || - (filter.mSerialNumber == null && - mSerialNumber != null)) { - return(false); - } - if ((filter.mManufacturerName != null && - mManufacturerName != null && - !mManufacturerName.equals(filter.mManufacturerName)) || - (filter.mProductName != null && - mProductName != null && - !mProductName.equals(filter.mProductName)) || - (filter.mSerialNumber != null && - mSerialNumber != null && - !mSerialNumber.equals(filter.mSerialNumber))) { - return false; - } - return true; - } - if (obj instanceof UsbDevice) { - UsbDevice device = (UsbDevice)obj; - if (device.getVendorId() != mVendorId || - device.getProductId() != mProductId || - device.getDeviceClass() != mClass || - device.getDeviceSubclass() != mSubclass || - device.getDeviceProtocol() != mProtocol) { - return(false); - } - if ((mManufacturerName != null && device.getManufacturerName() == null) || - (mManufacturerName == null && device.getManufacturerName() != null) || - (mProductName != null && device.getProductName() == null) || - (mProductName == null && device.getProductName() != null) || - (mSerialNumber != null && device.getSerialNumber() == null) || - (mSerialNumber == null && device.getSerialNumber() != null)) { - return(false); - } - if ((device.getManufacturerName() != null && - !mManufacturerName.equals(device.getManufacturerName())) || - (device.getProductName() != null && - !mProductName.equals(device.getProductName())) || - (device.getSerialNumber() != null && - !mSerialNumber.equals(device.getSerialNumber()))) { - return false; - } - return true; - } - return false; - } - - @Override - public int hashCode() { - return (((mVendorId << 16) | mProductId) ^ - ((mClass << 16) | (mSubclass << 8) | mProtocol)); - } - - @Override - public String toString() { - return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + - ",mClass=" + mClass + ",mSubclass=" + mSubclass + - ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName + - ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + - "]"; - } - } - - // This class is used to describe a USB accessory. - // When used in HashMaps all values must be specified, - // but wildcards can be used for any of the fields in - // the package meta-data. - private static class AccessoryFilter { - // USB accessory manufacturer (or null for unspecified) - public final String mManufacturer; - // USB accessory model (or null for unspecified) - public final String mModel; - // USB accessory version (or null for unspecified) - public final String mVersion; - - public AccessoryFilter(String manufacturer, String model, String version) { - mManufacturer = manufacturer; - mModel = model; - mVersion = version; - } - - public AccessoryFilter(UsbAccessory accessory) { - mManufacturer = accessory.getManufacturer(); - mModel = accessory.getModel(); - mVersion = accessory.getVersion(); - } - - public static AccessoryFilter read(XmlPullParser parser) - throws XmlPullParserException, IOException { - String manufacturer = null; - String model = null; - String version = null; - - int count = parser.getAttributeCount(); - for (int i = 0; i < count; i++) { - String name = parser.getAttributeName(i); - String value = parser.getAttributeValue(i); - - if ("manufacturer".equals(name)) { - manufacturer = value; - } else if ("model".equals(name)) { - model = value; - } else if ("version".equals(name)) { - version = value; - } - } - return new AccessoryFilter(manufacturer, model, version); - } - - public void write(XmlSerializer serializer)throws IOException { - serializer.startTag(null, "usb-accessory"); - if (mManufacturer != null) { - serializer.attribute(null, "manufacturer", mManufacturer); - } - if (mModel != null) { - serializer.attribute(null, "model", mModel); - } - if (mVersion != null) { - serializer.attribute(null, "version", mVersion); - } - serializer.endTag(null, "usb-accessory"); - } - - public boolean matches(UsbAccessory acc) { - if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false; - if (mModel != null && !acc.getModel().equals(mModel)) return false; - return !(mVersion != null && !acc.getVersion().equals(mVersion)); - } - - /** - * Is the accessories described {@code accessory} covered by this filter? - * - * @param accessory A filter describing the accessory - * - * @return {@code true} iff this the filter covers the accessory - */ - public boolean contains(AccessoryFilter accessory) { - if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) { - return false; - } - if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false; - return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion)); - } - - @Override - public boolean equals(Object obj) { - // can't compare if we have wildcard strings - if (mManufacturer == null || mModel == null || mVersion == null) { - return false; - } - if (obj instanceof AccessoryFilter) { - AccessoryFilter filter = (AccessoryFilter)obj; - return (mManufacturer.equals(filter.mManufacturer) && - mModel.equals(filter.mModel) && - mVersion.equals(filter.mVersion)); - } - if (obj instanceof UsbAccessory) { - UsbAccessory accessory = (UsbAccessory)obj; - return (mManufacturer.equals(accessory.getManufacturer()) && - mModel.equals(accessory.getModel()) && - mVersion.equals(accessory.getVersion())); - } - return false; - } - - @Override - public int hashCode() { - return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ - (mModel == null ? 0 : mModel.hashCode()) ^ - (mVersion == null ? 0 : mVersion.hashCode())); - } - - @Override - public String toString() { - return "AccessoryFilter[mManufacturer=\"" + mManufacturer + - "\", mModel=\"" + mModel + - "\", mVersion=\"" + mVersion + "\"]"; - } - } - private class MyPackageMonitor extends PackageMonitor { @Override public void onPackageAdded(String packageName, int uid) {