diff options
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java')
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java new file mode 100644 index 000000000000..ab29a9e970c7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2022 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.systemui.usb; + +import static android.Manifest.permission.RECORD_AUDIO; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.PermissionChecker; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; + +/** + * Helper class to separate model and view for USB permission and confirm dialogs. + */ +public class UsbDialogHelper { + private static final String TAG = UsbDialogHelper.class.getSimpleName(); + private static final String EXTRA_RESOLVE_INFO = "rinfo"; + + private final UsbDevice mDevice; + private final UsbAccessory mAccessory; + private final ResolveInfo mResolveInfo; + private final String mPackageName; + private final CharSequence mAppName; + private final Context mContext; + private final PendingIntent mPendingIntent; + private final IUsbManager mUsbService; + private final int mUid; + private final boolean mCanBeDefault; + + private UsbDisconnectedReceiver mDisconnectedReceiver; + private boolean mIsUsbDevice; + private boolean mResponseSent; + + /** + * @param context The Context of the caller. + * @param intent The intent of the caller. + * @throws IllegalStateException Thrown if both UsbDevice and UsbAccessory are null or if the + * query for the matching ApplicationInfo is unsuccessful. + */ + public UsbDialogHelper(Context context, Intent intent) throws IllegalStateException { + mContext = context; + mDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + mAccessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + mCanBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false); + if (mDevice == null && mAccessory == null) { + throw new IllegalStateException("Device and accessory are both null."); + } + if (mDevice != null) { + mIsUsbDevice = true; + } + mResolveInfo = intent.getParcelableExtra(EXTRA_RESOLVE_INFO); + PackageManager packageManager = mContext.getPackageManager(); + if (mResolveInfo != null) { + // If a ResolveInfo is provided it will be used to determine the activity to start + mUid = mResolveInfo.activityInfo.applicationInfo.uid; + mPackageName = mResolveInfo.activityInfo.packageName; + mPendingIntent = null; + } else { + mUid = intent.getIntExtra(Intent.EXTRA_UID, -1); + mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE); + mPendingIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); + } + try { + ApplicationInfo aInfo = packageManager.getApplicationInfo(mPackageName, 0); + mAppName = aInfo.loadLabel(packageManager); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalStateException("unable to look up package name", e); + } + IBinder b = ServiceManager.getService(Context.USB_SERVICE); + mUsbService = IUsbManager.Stub.asInterface(b); + } + + /** + * Registers UsbDisconnectedReceiver to dismiss dialog automatically when device or accessory + * gets disconnected + * @param activity The activity to finish when device / accessory gets disconnected. + */ + public void registerUsbDisconnectedReceiver(Activity activity) { + if (mIsUsbDevice) { + mDisconnectedReceiver = new UsbDisconnectedReceiver(activity, mDevice); + } else { + mDisconnectedReceiver = new UsbDisconnectedReceiver(activity, mAccessory); + } + } + + /** + * Unregisters the UsbDisconnectedReceiver. To be called when the activity is destroyed. + * @param activity The activity registered to finish when device / accessory gets disconnected. + */ + public void unregisterUsbDisconnectedReceiver(Activity activity) { + if (mDisconnectedReceiver != null) { + try { + activity.unregisterReceiver(mDisconnectedReceiver); + } catch (Exception e) { + // pass + } + mDisconnectedReceiver = null; + } + } + + /** + * @return True if the intent contains a UsbDevice which can capture audio. + */ + public boolean deviceHasAudioCapture() { + return mDevice != null && mDevice.getHasAudioCapture(); + } + + /** + * @return True if the intent contains a UsbDevice which can play audio. + */ + public boolean deviceHasAudioPlayback() { + return mDevice != null && mDevice.getHasAudioPlayback(); + } + + /** + * @return True if the package has RECORD_AUDIO permission specified in its manifest. + */ + public boolean packageHasAudioRecordingPermission() { + return PermissionChecker.checkPermissionForPreflight(mContext, RECORD_AUDIO, + PermissionChecker.PID_UNKNOWN, mUid, mPackageName) + == android.content.pm.PackageManager.PERMISSION_GRANTED; + } + + /** + * @return True if the intent contains a UsbDevice. + */ + public boolean isUsbDevice() { + return mIsUsbDevice; + } + + /** + * @return True if the intent contains a UsbAccessory. + */ + public boolean isUsbAccessory() { + return !mIsUsbDevice; + } + + /** + * Grants USB permission to the device / accessory to the calling uid. + */ + public void grantUidAccessPermission() { + try { + if (mIsUsbDevice) { + mUsbService.grantDevicePermission(mDevice, mUid); + } else { + mUsbService.grantAccessoryPermission(mAccessory, mUid); + } + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + } + + /** + * Sets the package as default for the device / accessory. + */ + public void setDefaultPackage() { + final int userId = UserHandle.myUserId(); + try { + if (mIsUsbDevice) { + mUsbService.setDevicePackage(mDevice, mPackageName, userId); + } else { + mUsbService.setAccessoryPackage(mAccessory, mPackageName, userId); + } + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + } + + /** + * Clears the default package of the device / accessory. + */ + public void clearDefaultPackage() { + final int userId = UserHandle.myUserId(); + try { + if (mIsUsbDevice) { + mUsbService.setDevicePackage(mDevice, null, userId); + } else { + mUsbService.setAccessoryPackage(mAccessory, null, userId); + } + } catch (RemoteException e) { + Log.e(TAG, "IUsbService connection failed", e); + } + } + + /** + * Starts the activity which was selected to handle the device / accessory. + */ + public void confirmDialogStartActivity() { + final int userId = UserHandle.myUserId(); + Intent intent; + + if (mIsUsbDevice) { + intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); + } else { + intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setComponent( + new ComponentName(mResolveInfo.activityInfo.packageName, + mResolveInfo.activityInfo.name)); + try { + mContext.startActivityAsUser(intent, new UserHandle(userId)); + } catch (Exception e) { + Log.e(TAG, "Unable to start activity", e); + } + } + + /** + * Sends the result of the permission dialog via the provided PendingIntent. + * + * @param permissionGranted True if the user pressed ok in the permission dialog. + */ + public void sendPermissionDialogResponse(boolean permissionGranted) { + if (!mResponseSent) { + // send response via pending intent + Intent intent = new Intent(); + if (mIsUsbDevice) { + intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); + } else { + intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); + } + intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, permissionGranted); + try { + mPendingIntent.send(mContext, 0, intent); + mResponseSent = true; + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "PendingIntent was cancelled"); + } + } + } + + /** + * @return A description of the device / accessory + */ + public String getDeviceDescription() { + String desc; + if (mIsUsbDevice) { + desc = mDevice.getProductName(); + if (desc == null) { + desc = mDevice.getDeviceName(); + } + } else { + // UsbAccessory + desc = mAccessory.getDescription(); + if (desc == null) { + desc = String.format("%s %s", mAccessory.getManufacturer(), mAccessory.getModel()); + } + } + return desc; + } + + /** + * Whether the calling package can set as default handler of the USB device or accessory. + * In case of a UsbAccessory this is the case if the calling package has an intent filter for + * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} with a usb-accessory filter matching the + * attached accessory. In case of a UsbDevice this is the case if the calling package has an + * intent filter for {@link UsbManager#ACTION_USB_DEVICE_ATTACHED} with a usb-device filter + * matching the attached device. + * + * @return True if the package can be default for the USB device. + */ + public boolean canBeDefault() { + return mCanBeDefault; + } + + /** + * @return The name of the app which requested permission or the name of the app which will be + * opened if the user allows it to handle the USB device. + */ + public CharSequence getAppName() { + return mAppName; + } +} |
