diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-08-13 19:40:12 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-08-13 19:40:12 +0000 |
commit | d42e8928647aa183aa06564329dbfc08e0f1acff (patch) | |
tree | 7762520ddd0b59caab59de6e1b0b572d8ebbf97b | |
parent | 5b202eec3429d69666c897a391cd845cef4970da (diff) | |
parent | 2c3fcd7fd8ae1238068435f36eba3897e1005886 (diff) | |
download | base-d42e8928647aa183aa06564329dbfc08e0f1acff.tar.gz |
Merge cherrypicks of [15560884, 15560845, 15560903, 15560904, 15560905, 15560906, 15560769, 15560755, 15557357, 15557358] into sc-release
Change-Id: I0d3297dea2e7b755df16d443dd441cff44792f83
7 files changed, 224 insertions, 95 deletions
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 8398be1af49a..5094498dbee5 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -64,6 +64,8 @@ import android.os.SystemProperties; import android.util.Log; import android.util.Pair; +import com.android.internal.annotations.GuardedBy; + import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,6 +80,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -715,10 +718,21 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private final AttributionSource mAttributionSource; + // Yeah, keeping both mService and sService isn't pretty, but it's too late + // in the current release for a major refactoring, so we leave them both + // intact until this can be cleaned up in a future release + @UnsupportedAppUsage + @GuardedBy("mServiceLock") private IBluetooth mService; private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); + @GuardedBy("sServiceLock") + private static boolean sServiceRegistered; + @GuardedBy("sServiceLock") + private static IBluetooth sService; + private static final Object sServiceLock = new Object(); + private final Object mLock = new Object(); private final Map<LeScanCallback, ScanCallback> mLeScanClients; private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> @@ -792,19 +806,11 @@ public final class BluetoothAdapter { * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. */ BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { - if (managerService == null) { - throw new IllegalArgumentException("bluetooth manager service is null"); - } - try { - mServiceLock.writeLock().lock(); - mService = managerService.registerAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.writeLock().unlock(); - } mManagerService = Objects.requireNonNull(managerService); mAttributionSource = Objects.requireNonNull(attributionSource); + synchronized (mServiceLock.writeLock()) { + mService = getBluetoothService(mManagerCallback); + } mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); mToken = new Binder(DESCRIPTOR); } @@ -3154,21 +3160,16 @@ public final class BluetoothAdapter { } } - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothManagerCallback mManagerCallback = + private static final IBluetoothManagerCallback sManagerCallback = new IBluetoothManagerCallback.Stub() { - @SuppressLint("AndroidFrameworkRequiresPermission") public void onBluetoothServiceUp(IBluetooth bluetoothService) { if (DBG) { Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); } - mServiceLock.writeLock().lock(); - mService = bluetoothService; - mServiceLock.writeLock().unlock(); - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { + synchronized (sServiceLock) { + sService = bluetoothService; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { try { if (cb != null) { cb.onBluetoothServiceUp(bluetoothService); @@ -3180,6 +3181,56 @@ public final class BluetoothAdapter { } } } + } + + public void onBluetoothServiceDown() { + if (DBG) { + Log.d(TAG, "onBluetoothServiceDown"); + } + + synchronized (sServiceLock) { + sService = null; + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBluetoothServiceDown(); + } else { + Log.d(TAG, "onBluetoothServiceDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + + public void onBrEdrDown() { + if (VDBG) { + Log.i(TAG, "onBrEdrDown"); + } + + synchronized (sServiceLock) { + for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { + try { + if (cb != null) { + cb.onBrEdrDown(); + } else { + Log.d(TAG, "onBrEdrDown: cb is null!"); + } + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + } + } + }; + + private final IBluetoothManagerCallback mManagerCallback = + new IBluetoothManagerCallback.Stub() { + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + synchronized (mServiceLock.writeLock()) { + mService = bluetoothService; + } synchronized (mMetadataListeners) { mMetadataListeners.forEach((device, pair) -> { try { @@ -3204,12 +3255,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) { - Log.d(TAG, "onBluetoothServiceDown: " + mService); - } - - try { - mServiceLock.writeLock().lock(); + synchronized (mServiceLock.writeLock()) { mService = null; if (mLeScanClients != null) { mLeScanClients.clear(); @@ -3220,29 +3266,10 @@ public final class BluetoothAdapter { if (mBluetoothLeScanner != null) { mBluetoothLeScanner.cleanup(); } - } finally { - mServiceLock.writeLock().unlock(); - } - - synchronized (mProxyServiceStateCallbacks) { - for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) { - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } } } public void onBrEdrDown() { - if (VDBG) { - Log.i(TAG, "onBrEdrDown: " + mService); - } } }; @@ -3477,15 +3504,12 @@ public final class BluetoothAdapter { protected void finalize() throws Throwable { try { - mManagerService.unregisterAdapter(mManagerCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + removeServiceStateCallback(mManagerCallback); } finally { super.finalize(); } } - /** * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" * <p>Alphabetic characters must be uppercase to be valid. @@ -3549,24 +3573,64 @@ public final class BluetoothAdapter { return mAttributionSource; } - private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks = - new ArrayList<IBluetoothManagerCallback>(); + @GuardedBy("sServiceLock") + private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks = + new WeakHashMap<>(); + + /*package*/ IBluetooth getBluetoothService() { + synchronized (sServiceLock) { + if (sProxyServiceStateCallbacks.isEmpty()) { + throw new IllegalStateException( + "Anonymous service access requires at least one lifecycle in process"); + } + return sService; + } + } @UnsupportedAppUsage /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - if (cb == null) { - Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback"); - } else if (!mProxyServiceStateCallbacks.contains(cb)) { - mProxyServiceStateCallbacks.add(cb); - } + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.put(cb, null); + registerOrUnregisterAdapterLocked(); + return sService; } - return mService; } /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - synchronized (mProxyServiceStateCallbacks) { - mProxyServiceStateCallbacks.remove(cb); + Objects.requireNonNull(cb); + synchronized (sServiceLock) { + sProxyServiceStateCallbacks.remove(cb); + registerOrUnregisterAdapterLocked(); + } + } + + /** + * Handle registering (or unregistering) a single process-wide + * {@link IBluetoothManagerCallback} based on the presence of local + * {@link #sProxyServiceStateCallbacks} clients. + */ + @GuardedBy("sServiceLock") + private void registerOrUnregisterAdapterLocked() { + final boolean isRegistered = sServiceRegistered; + final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); + + if (isRegistered != wantRegistered) { + if (wantRegistered) { + try { + sService = mManagerService.registerAdapter(sManagerCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + try { + mManagerService.unregisterAdapter(sManagerCallback); + sService = null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + sServiceRegistered = wantRegistered; } } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index bb409d5360f9..1655b62bbfec 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -399,7 +399,7 @@ public final class BluetoothSocket implements Closeable { try { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags()); @@ -438,7 +438,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int bindListen() { int ret; if (mSocketState == SocketState.CLOSED) return EBADFD; - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { Log.e(TAG, "bindListen fail, reason: bluetooth is off"); return -1; @@ -706,7 +706,7 @@ public final class BluetoothSocket implements Closeable { throw new IOException("socket closed"); } IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + BluetoothAdapter.getDefaultAdapter().getBluetoothService(); if (bluetoothProxy == null) { throw new IOException("Bluetooth is off"); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 632919ae51e4..f4a3fb236ffa 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -97,6 +97,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private SmartspaceTransitionController mSmartspaceTransitionController; + private boolean mOnlyClock = false; + @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, @@ -129,6 +131,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** + * Mostly used for alternate displays, limit the information shown + */ + public void setOnlyClock(boolean onlyClock) { + mOnlyClock = onlyClock; + } + + /** * Attach the controller to the view it relates to. */ @Override @@ -166,6 +175,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } mColorExtractor.addOnColorsChangedListener(mColorsListener); mView.updateColors(getGradientColors()); + + if (mOnlyClock) { + View ksa = mView.findViewById(R.id.keyguard_status_area); + ksa.setVisibility(View.GONE); + + View nic = mView.findViewById( + R.id.left_aligned_notification_icon_container); + nic.setVisibility(View.GONE); + return; + } updateAodIcons(); if (mSmartspaceController.isEnabled()) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 76a7473e25e8..cac90ea60e97 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -332,6 +332,7 @@ public class KeyguardDisplayManager { .build(findViewById(R.id.clock)) .getKeyguardClockSwitchController(); + mKeyguardClockSwitchController.setOnlyClock(true); mKeyguardClockSwitchController.init(); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index f41036cde433..a2fec2753340 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -6314,10 +6314,17 @@ public final class ActiveServices { final String msg = "Background started FGS: " + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ") + r.mInfoAllowStartForeground; - Slog.wtfQuiet(TAG, msg); if (r.mAllowStartForeground != REASON_DENIED) { + if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName, + mAm.mConstants.mFgsStartAllowedLogSampleRate)) { + Slog.wtfQuiet(TAG, msg); + } Slog.i(TAG, msg); } else { + if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName, + mAm.mConstants.mFgsStartDeniedLogSampleRate)) { + Slog.wtfQuiet(TAG, msg); + } Slog.w(TAG, msg); } r.mLoggedInfoAllowStartForeground = true; diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index ac0a1985ac89..eeb41a3df969 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -108,6 +108,8 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate"; + static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate"; + static final String KEY_FGS_START_DENIED_LOG_SAMPLE_RATE = "fgs_start_denied_log_sample_rate"; static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -152,6 +154,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 % + private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25% + private static final float DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE = 1; // 100% /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ @@ -496,6 +500,20 @@ final class ActivityManagerConstants extends ContentObserver { volatile float mFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE; /** + * Sample rate for the allowed FGS start WTF logs. + * + * If the value is 0.1, 10% of the logs would be sampled. + */ + volatile float mFgsStartAllowedLogSampleRate = DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE; + + /** + * Sample rate for the denied FGS start WTF logs. + * + * If the value is 0.1, 10% of the logs would be sampled. + */ + volatile float mFgsStartDeniedLogSampleRate = DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE; + + /** * Whether to allow "opt-out" from the foreground service restrictions. * (https://developer.android.com/about/versions/12/foreground-services) */ @@ -711,6 +729,12 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_FGS_ATOM_SAMPLE_RATE: updateFgsAtomSamplePercent(); break; + case KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE: + updateFgsStartAllowedLogSamplePercent(); + break; + case KEY_FGS_START_DENIED_LOG_SAMPLE_RATE: + updateFgsStartDeniedLogSamplePercent(); + break; case KEY_FGS_ALLOW_OPT_OUT: updateFgsAllowOptOut(); break; @@ -1057,6 +1081,20 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_FGS_ATOM_SAMPLE_RATE); } + private void updateFgsStartAllowedLogSamplePercent() { + mFgsStartAllowedLogSampleRate = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE, + DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE); + } + + private void updateFgsStartDeniedLogSamplePercent() { + mFgsStartDeniedLogSampleRate = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FGS_START_DENIED_LOG_SAMPLE_RATE, + DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE); + } + private void updateFgsAllowOptOut() { mFgsAllowOptOut = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1285,6 +1323,10 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk); pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE); pw.print("="); pw.println(mFgsAtomSampleRate); + pw.print(" "); pw.print(KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE); + pw.print("="); pw.println(mFgsStartAllowedLogSampleRate); + pw.print(" "); pw.print(KEY_FGS_START_DENIED_LOG_SAMPLE_RATE); + pw.print("="); pw.println(mFgsStartDeniedLogSampleRate); pw.print(" "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR); pw.print("="); pw.println(mPushMessagingOverQuotaBehavior); pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT); diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java index 5874b4b9fd3e..7b6ccd31adcc 100644 --- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java +++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java @@ -25,12 +25,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.hardware.SensorPrivacyManager.Sensors; +import android.hardware.SensorPrivacyManagerInternal; import android.hardware.usb.AccessoryFilter; import android.hardware.usb.DeviceFilter; import android.hardware.usb.UsbAccessory; -import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.AsyncTask; import android.os.Binder; @@ -52,9 +52,9 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.internal.util.dump.DualDumpOutputStream; +import com.android.server.LocalServices; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -64,7 +64,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; /** * UsbUserPermissionManager manages usb device or accessory access permissions. @@ -110,19 +109,20 @@ class UsbUserPermissionManager { */ @GuardedBy("mLock") private boolean mIsCopyPermissionsScheduled; + private final SensorPrivacyManagerInternal mSensorPrivacyMgrInternal; UsbUserPermissionManager(@NonNull Context context, @NonNull UsbUserSettingsManager usbUserSettingsManager) { mContext = context; mUser = context.getUser(); mUsbUserSettingsManager = usbUserSettingsManager; + mSensorPrivacyMgrInternal = LocalServices.getService(SensorPrivacyManagerInternal.class); mDisablePermissionDialogs = context.getResources().getBoolean( com.android.internal.R.bool.config_disableUsbPermissionDialogs); mPermissionsFile = new AtomicFile(new File( Environment.getUserSystemDirectory(mUser.getIdentifier()), "usb_permissions.xml"), "usb-permissions"); - synchronized (mLock) { readPermissionsLocked(); } @@ -195,11 +195,27 @@ class UsbUserPermissionManager { */ boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid, int uid) { - if (isCameraDevicePresent(device)) { - if (!isCameraPermissionGranted(packageName, pid, uid)) { + if (device.getHasVideoCapture()) { + boolean isCameraPrivacyEnabled = mSensorPrivacyMgrInternal.isSensorPrivacyEnabled( + UserHandle.getUserId(uid), Sensors.CAMERA); + if (DEBUG) { + Slog.d(TAG, "isCameraPrivacyEnabled: " + isCameraPrivacyEnabled); + } + if (isCameraPrivacyEnabled || !isCameraPermissionGranted(packageName, pid, uid)) { return false; } } + // Only check for microphone privacy and not RECORD_AUDIO permission, because access to usb + // camera device with audio recording capabilities may still be granted with a warning + if (device.getHasAudioCapture() && mSensorPrivacyMgrInternal.isSensorPrivacyEnabled( + UserHandle.getUserId(uid), Sensors.MICROPHONE)) { + if (DEBUG) { + Slog.d(TAG, + "Access to device with audio recording capabilities denied because " + + "microphone privacy is enabled."); + } + return false; + } synchronized (mLock) { if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { return true; @@ -698,7 +714,10 @@ class UsbUserPermissionManager { } return; } - if (isCameraDevicePresent(device)) { + // If the app doesn't have camera permission do not request permission to the USB device. + // Note that if the USB camera also has a microphone, a warning will be shown to the user if + // the app doesn't have RECORD_AUDIO permission. + if (device.getHasVideoCapture()) { if (!isCameraPermissionGranted(packageName, pid, uid)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); @@ -733,27 +752,4 @@ class UsbUserPermissionManager { requestPermissionDialog(null, accessory, mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid); } - - /** - * Check whether a particular device or any of its interfaces - * is of class VIDEO. - * - * @param device The device that needs to get scanned - * @return True in case a VIDEO device or interface is present, - * False otherwise. - */ - private boolean isCameraDevicePresent(UsbDevice device) { - if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) { - return true; - } - - for (int i = 0; i < device.getInterfaceCount(); i++) { - UsbInterface iface = device.getInterface(i); - if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) { - return true; - } - } - - return false; - } } |