Merge "Migrate USB audio device permission warning dialog for both Direct USB Access and AoC Offload feature" into tm-dev am: 0c5df6883b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17472850

Change-Id: I6186d145bdf3b760758dd1a68ed425dbe04daa02
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jason Chang
2022-04-15 09:46:51 +00:00
committed by Automerger Merge Worker
8 changed files with 267 additions and 44 deletions

View File

@@ -0,0 +1,30 @@
# USB audio Permission and Confirmation warning dialog resource string id matrix table
### go/support-usb-access-aoc-offload-feature
|---|------------|----------------|------------------|-----------------|--------------------|
| # | Permission |isUsbAudioDevice| hasAudioPlayback | hasAudioCapture | string resource ID |
|---|------------|----------------|------------------|-----------------|--------------------|
| 1 | TRUE | TRUE | TRUE | FALSE | usb_audio_device_
permission_prompt |
|---|------------|----------------|------------------|-----------------|--------------------|
| 2 | TRUE | TRUE | FALSE | TRUE | usb_audio_device_
permission_prompt |
|---|------------|----------------|------------------|-----------------|--------------------|
| 3 | TRUE | TRUE | TRUE | TRUE | usb_audio_device_
permission_prompt |
|---|------------|----------------|------------------|-----------------|--------------------|
| 4 | TRUE | FALSE | N/A | N/A | usb_device_
permission_prompt |
|---|------------|----------------|------------------|-----------------|--------------------|
| 5 | FALSE | TRUE | TRUE | FALSE | usb_audio_device_
permission_prompt |
|---|------------|----------------|------------------|-----------------|--------------------|
| 6 | FALSE | TRUE | FALSE | TRUE | usb_audio_device_
permission_prompt_warn
|---|------------|----------------|------------------|-----------------|--------------------|
| 7 | FALSE | TRUE | TRUE | TRUE | usb_audio_device_
permission_prompt_warn
|---|------------|----------------|------------------|-----------------|--------------------|
| 8 | FALSE | FALSE | N/A | N/A | usb_device_
permission_prompt |
|---|------------|----------------|------------------|-----------------|--------------------|

View File

@@ -29,8 +29,10 @@ import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.tuner.TunerActivity; import com.android.systemui.tuner.TunerActivity;
import com.android.systemui.usb.UsbConfirmActivity;
import com.android.systemui.usb.UsbDebuggingActivity; import com.android.systemui.usb.UsbDebuggingActivity;
import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity; import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
import com.android.systemui.usb.UsbPermissionActivity;
import com.android.systemui.user.CreateUserActivity; import com.android.systemui.user.CreateUserActivity;
import dagger.Binds; import dagger.Binds;
@@ -80,6 +82,18 @@ public abstract class DefaultActivityBinder {
public abstract Activity bindUsbDebuggingSecondaryUserActivity( public abstract Activity bindUsbDebuggingSecondaryUserActivity(
UsbDebuggingSecondaryUserActivity activity); UsbDebuggingSecondaryUserActivity activity);
/** Inject into UsbPermissionActivity. */
@Binds
@IntoMap
@ClassKey(UsbPermissionActivity.class)
public abstract Activity bindUsbPermissionActivity(UsbPermissionActivity activity);
/** Inject into UsbConfirmActivity. */
@Binds
@IntoMap
@ClassKey(UsbConfirmActivity.class)
public abstract Activity bindUsbConfirmActivity(UsbConfirmActivity activity);
/** Inject into CreateUserActivity. */ /** Inject into CreateUserActivity. */
@Binds @Binds
@IntoMap @IntoMap

View File

@@ -0,0 +1,124 @@
/*
* 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 java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.content.res.Resources;
import android.util.Log;
import com.android.systemui.R;
import java.lang.annotation.Retention;
import javax.inject.Inject;
/**
* USB Audio devices warning dialog messages help class.
*/
public class UsbAudioWarningDialogMessage {
private static final String TAG = "UsbAudioWarningDialogMessage";
@Retention(SOURCE)
@IntDef({TYPE_PERMISSION, TYPE_CONFIRM})
public @interface DialogType {}
public static final int TYPE_PERMISSION = 0;
public static final int TYPE_CONFIRM = 1;
private int mDialogType;
private UsbDialogHelper mDialogHelper;
@Inject
public UsbAudioWarningDialogMessage() {
}
/**
* Initialize USB audio warning dialog message type and helper class.
* @param type Dialog type for Activity.
* @param usbDialogHelper Helper class for getting USB permission and confirm dialogs
*/
public void init(@DialogType int type, UsbDialogHelper usbDialogHelper) {
mDialogType = type;
mDialogHelper = usbDialogHelper;
}
boolean hasRecordPermission() {
return mDialogHelper.packageHasAudioRecordingPermission();
}
boolean isUsbAudioDevice() {
return mDialogHelper.isUsbDevice() && (mDialogHelper.deviceHasAudioCapture()
|| (mDialogHelper.deviceHasAudioPlayback()));
}
boolean hasAudioPlayback() {
return mDialogHelper.deviceHasAudioPlayback();
}
boolean hasAudioCapture() {
return mDialogHelper.deviceHasAudioCapture();
}
/**
* According to USB audio warning dialog matrix table to return warning message id.
* @return string resId for USB audio warning dialog message, otherwise {ID_NULL}.
* See usb_audio.md for USB audio Permission and Confirmation warning dialog resource
* string id matrix table.
*/
public int getMessageId() {
if (!mDialogHelper.isUsbDevice()) {
return getUsbAccessoryPromptId();
}
if (hasRecordPermission() && isUsbAudioDevice()) {
// case# 1, 2, 3
return R.string.usb_audio_device_prompt;
} else if (!hasRecordPermission() && isUsbAudioDevice() && hasAudioPlayback()
&& !hasAudioCapture()) {
// case# 5
return R.string.usb_audio_device_prompt;
}
if (!hasRecordPermission() && isUsbAudioDevice() && hasAudioCapture()) {
// case# 6,7
return R.string.usb_audio_device_prompt_warn;
}
Log.w(TAG, "Only shows title with empty content description!");
return Resources.ID_NULL;
}
/**
* Gets prompt dialog title.
* @return string id for USB prompt dialog title.
*/
public int getPromptTitleId() {
return (mDialogType == TYPE_PERMISSION)
? R.string.usb_audio_device_permission_prompt_title
: R.string.usb_audio_device_confirm_prompt_title;
}
/**
* Gets USB Accessory prompt message id.
* @return string id for USB Accessory prompt message.
*/
public int getUsbAccessoryPromptId() {
return (mDialogType == TYPE_PERMISSION)
? R.string.usb_accessory_permission_prompt : R.string.usb_accessory_confirm_prompt;
}
}

View File

@@ -16,7 +16,10 @@
package com.android.systemui.usb; package com.android.systemui.usb;
import com.android.systemui.R; import android.content.res.Resources;
import android.os.Bundle;
import javax.inject.Inject;
/** /**
* Dialog shown to confirm the package to start when a USB device or accessory is attached and there * Dialog shown to confirm the package to start when a USB device or accessory is attached and there
@@ -24,23 +27,35 @@ import com.android.systemui.R;
*/ */
public class UsbConfirmActivity extends UsbDialogActivity { public class UsbConfirmActivity extends UsbDialogActivity {
private UsbAudioWarningDialogMessage mUsbConfirmMessageHandler;
@Inject
public UsbConfirmActivity(UsbAudioWarningDialogMessage usbAudioWarningDialogMessage) {
mUsbConfirmMessageHandler = usbAudioWarningDialogMessage;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUsbConfirmMessageHandler.init(UsbAudioWarningDialogMessage.TYPE_CONFIRM, mDialogHelper);
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
final int strId;
boolean useRecordWarning = false;
if (mDialogHelper.isUsbDevice()) {
useRecordWarning = mDialogHelper.deviceHasAudioCapture()
&& !mDialogHelper.packageHasAudioRecordingPermission();
strId = useRecordWarning
? R.string.usb_device_confirm_prompt_warn
: R.string.usb_device_confirm_prompt;
} else {
// UsbAccessory case
strId = R.string.usb_accessory_confirm_prompt;
}
setAlertParams(strId);
// Only show the "always use" checkbox if there is no USB/Record warning // Only show the "always use" checkbox if there is no USB/Record warning
final boolean useRecordWarning = mDialogHelper.isUsbDevice()
&& (mDialogHelper.deviceHasAudioCapture()
&& !mDialogHelper.packageHasAudioRecordingPermission());
final int titleId = mUsbConfirmMessageHandler.getPromptTitleId();
final String title = getString(titleId, mDialogHelper.getAppName(),
mDialogHelper.getDeviceDescription());
final int messageId = mUsbConfirmMessageHandler.getMessageId();
String message = (messageId != Resources.ID_NULL)
? getString(messageId, mDialogHelper.getAppName(),
mDialogHelper.getDeviceDescription()) : null;
setAlertParams(title, message);
if (!useRecordWarning) { if (!useRecordWarning) {
addAlwaysUseCheckbox(); addAlwaysUseCheckbox();
} }

View File

@@ -41,7 +41,7 @@ abstract class UsbDialogActivity extends AlertActivity
private TextView mClearDefaultHint; private TextView mClearDefaultHint;
@Override @Override
protected final void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addSystemFlags( getWindow().addSystemFlags(
WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
@@ -87,11 +87,10 @@ abstract class UsbDialogActivity extends AlertActivity
} }
} }
void setAlertParams(int strId) { void setAlertParams(String title, String message) {
final AlertController.AlertParams ap = mAlertParams; final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = mDialogHelper.getAppName(); ap.mTitle = title;
ap.mMessage = getString(strId, mDialogHelper.getAppName(), ap.mMessage = message;
mDialogHelper.getDeviceDescription());
ap.mPositiveButtonText = getString(android.R.string.ok); ap.mPositiveButtonText = getString(android.R.string.ok);
ap.mNegativeButtonText = getString(android.R.string.cancel); ap.mNegativeButtonText = getString(android.R.string.cancel);
ap.mPositiveButtonListener = this; ap.mPositiveButtonListener = this;

View File

@@ -133,6 +133,13 @@ public class UsbDialogHelper {
return mDevice != null && mDevice.getHasAudioCapture(); 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. * @return True if the package has RECORD_AUDIO permission specified in its manifest.
*/ */

View File

@@ -16,7 +16,10 @@
package com.android.systemui.usb; package com.android.systemui.usb;
import com.android.systemui.R; import android.content.res.Resources;
import android.os.Bundle;
import javax.inject.Inject;
/** /**
* Dialog shown when a package requests access to a USB device or accessory. * Dialog shown when a package requests access to a USB device or accessory.
@@ -24,23 +27,36 @@ import com.android.systemui.R;
public class UsbPermissionActivity extends UsbDialogActivity { public class UsbPermissionActivity extends UsbDialogActivity {
private boolean mPermissionGranted = false; private boolean mPermissionGranted = false;
private UsbAudioWarningDialogMessage mUsbPermissionMessageHandler;
@Inject
public UsbPermissionActivity(UsbAudioWarningDialogMessage usbAudioWarningDialogMessage) {
mUsbPermissionMessageHandler = usbAudioWarningDialogMessage;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUsbPermissionMessageHandler.init(UsbAudioWarningDialogMessage.TYPE_PERMISSION,
mDialogHelper);
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
final int strId; final boolean useRecordWarning = mDialogHelper.isUsbDevice()
boolean useRecordWarning = false; && (mDialogHelper.deviceHasAudioCapture()
if (mDialogHelper.isUsbDevice()) { && !mDialogHelper.packageHasAudioRecordingPermission());
useRecordWarning = mDialogHelper.deviceHasAudioCapture()
&& !mDialogHelper.packageHasAudioRecordingPermission(); final int titleId = mUsbPermissionMessageHandler.getPromptTitleId();
strId = useRecordWarning final String title = getString(titleId, mDialogHelper.getAppName(),
? R.string.usb_device_permission_prompt_warn mDialogHelper.getDeviceDescription());
: R.string.usb_device_permission_prompt; final int messageId = mUsbPermissionMessageHandler.getMessageId();
} else { String message = (messageId != Resources.ID_NULL)
// UsbAccessory case ? getString(messageId, mDialogHelper.getAppName(),
strId = R.string.usb_accessory_permission_prompt; mDialogHelper.getDeviceDescription()) : null;
} setAlertParams(title, message);
setAlertParams(strId);
// Only show the "always use" checkbox if there is no USB/Record warning // Only show the "always use" checkbox if there is no USB/Record warning
if (!useRecordWarning && mDialogHelper.canBeDefault()) { if (!useRecordWarning && mDialogHelper.canBeDefault()) {
addAlwaysUseCheckbox(); addAlwaysUseCheckbox();

View File

@@ -21,11 +21,16 @@ import android.hardware.usb.IUsbSerialReader
import android.hardware.usb.UsbAccessory import android.hardware.usb.UsbAccessory
import android.hardware.usb.UsbManager import android.hardware.usb.UsbManager
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import androidx.test.runner.intercepting.SingleActivityFactory
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import javax.inject.Inject
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@@ -37,14 +42,26 @@ import org.junit.runner.RunWith
*/ */
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@SmallTest @SmallTest
@TestableLooper.RunWithLooper
class UsbPermissionActivityTest : SysuiTestCase() { class UsbPermissionActivityTest : SysuiTestCase() {
class UsbPermissionActivityTestable : UsbPermissionActivity() private var mMessage: UsbAudioWarningDialogMessage = UsbAudioWarningDialogMessage()
open class UsbPermissionActivityTestable @Inject constructor (
val message: UsbAudioWarningDialogMessage
)
: UsbPermissionActivity(UsbAudioWarningDialogMessage())
@Rule @Rule
@JvmField @JvmField
var activityRule = ActivityTestRule<UsbPermissionActivityTestable>( var activityRule = ActivityTestRule<UsbPermissionActivityTestable>(
UsbPermissionActivityTestable::class.java, false, false) object : SingleActivityFactory<UsbPermissionActivityTestable>(
UsbPermissionActivityTestable::class.java
) {
override fun create(intent: Intent?): UsbPermissionActivityTestable {
return UsbPermissionActivityTestable(mMessage)
}
}, false, false)
private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java) private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java)
.apply { .apply {
@@ -72,6 +89,7 @@ class UsbPermissionActivityTest : SysuiTestCase() {
@Before @Before
fun setUp() { fun setUp() {
UsbPermissionActivityTestable(mMessage)
activityRule.launchActivity(activityIntent) activityRule.launchActivity(activityIntent)
} }