Merge "Implement special app access settings for MEDIA_ROUTING_CONTROL app-op." into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
86b5a0afc1
@@ -4185,6 +4185,25 @@
|
||||
android:value="@string/menu_key_apps"/>
|
||||
</activity>
|
||||
|
||||
<!-- @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") -->
|
||||
<activity-alias
|
||||
android:name="MediaRoutingControlActivity"
|
||||
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
|
||||
android:exported="true"
|
||||
android:targetActivity=".spa.SpaBridgeActivity"
|
||||
android:label="@string/media_routing_control_title">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.settings.REQUEST_MEDIA_ROUTING_CONTROL" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.spa.DESTINATION"
|
||||
android:value="TogglePermissionAppList/MediaRoutingControl"/>
|
||||
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
|
||||
android:value="@string/menu_key_apps"/>
|
||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||
android:value="true" />
|
||||
</activity-alias>
|
||||
|
||||
<!-- Keep compatibility with old WebView-picker implementation -->
|
||||
<activity-alias android:name=".WebViewImplementation"
|
||||
android:targetActivity="Settings$WebViewAppPickerActivity"
|
||||
|
@@ -9479,6 +9479,14 @@
|
||||
<!-- Description of allowing overlay setting [CHAR LIMIT=NONE] -->
|
||||
<string name="allow_overlay_description">Allow this app to display on top of other apps you\u2019re using. This app will be able to see where you tap or change what\u2019s displayed on the screen.</string>
|
||||
|
||||
<!-- Change Media Output settings -->
|
||||
<!-- Title for Change Media Output screen [CHAR LIMIT=30] -->
|
||||
<string name="media_routing_control_title">Change media output</string>
|
||||
<!-- Label for setting which controls whether app can change media outputs for other apps [CHAR LIMIT=45] -->
|
||||
<string name="allow_media_routing_control">Allow app to switch media output</string>
|
||||
<!-- Description for allowing change media output setting [CHAR LIMIT=NONE] -->
|
||||
<string name="allow_media_routing_description">Allow this app to choose which connected device plays audio or video from other apps. If allowed, this app can access a list of available devices such as headphones and speakers and choose which output device is used to stream or cast audio or video.</string>
|
||||
|
||||
<!-- Manager External Storage settings title [CHAR LIMIT=40] -->
|
||||
<string name="manage_external_storage_title">All files access</string>
|
||||
<!-- Label for a setting which controls whether an app can manage external storage [CHAR LIMIT=45] -->
|
||||
|
@@ -94,6 +94,13 @@
|
||||
android:fragment="com.android.settings.notification.NotificationAccessSettings"
|
||||
settings:controller="com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessController" />
|
||||
|
||||
<Preference
|
||||
android:key="media_routing_control"
|
||||
android:title="@string/media_routing_control_title"
|
||||
android:order="-1100"
|
||||
settings:controller="com.android.settings.applications.specialaccess.MediaRoutingControlPreferenceController" >
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="use_full_screen_intent"
|
||||
android:title="@string/full_screen_intent_title"
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settings.applications.specialaccess;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.media.flags.Flags;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.spa.SpaActivity;
|
||||
import com.android.settings.spa.app.specialaccess.MediaRoutingControlAppListProvider;
|
||||
|
||||
/**
|
||||
* This controller manages features availability for special app access for
|
||||
* {@link Manifest.permission#MEDIA_ROUTING_CONTROL} permission.
|
||||
*/
|
||||
public class MediaRoutingControlPreferenceController extends BasePreferenceController {
|
||||
public MediaRoutingControlPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return Flags.enablePrivilegedRoutingForMediaRoutingControl()
|
||||
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (TextUtils.equals(preference.getKey(), mPreferenceKey)) {
|
||||
SpaActivity.startSpaActivity(
|
||||
mContext, MediaRoutingControlAppListProvider.INSTANCE.getAppListRoute());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -32,6 +32,7 @@ import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListPro
|
||||
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
|
||||
import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
|
||||
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
||||
import com.android.settings.spa.app.specialaccess.MediaRoutingControlAppListProvider
|
||||
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
|
||||
import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
|
||||
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
|
||||
@@ -64,6 +65,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
|
||||
AllFilesAccessAppListProvider,
|
||||
DisplayOverOtherAppsAppListProvider,
|
||||
MediaManagementAppsAppListProvider,
|
||||
MediaRoutingControlAppListProvider,
|
||||
ModifySystemSettingsAppListProvider,
|
||||
UseFullScreenIntentAppListProvider,
|
||||
PictureInPictureListProvider,
|
||||
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settings.spa.app.specialaccess
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AppOpsManager
|
||||
import android.app.role.RoleManager
|
||||
import android.app.settings.SettingsEnums
|
||||
import android.companion.AssociationRequest
|
||||
import android.content.Context
|
||||
import com.android.settings.R
|
||||
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
|
||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
|
||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||
|
||||
object MediaRoutingControlAppListProvider : TogglePermissionAppListProvider {
|
||||
override val permissionType = "MediaRoutingControl"
|
||||
override fun createModel(context: Context) = MediaRoutingControlAppsListModel(context)
|
||||
}
|
||||
|
||||
class MediaRoutingControlAppsListModel(context: Context) : AppOpPermissionListModel(context) {
|
||||
override val pageTitleResId = R.string.media_routing_control_title
|
||||
override val switchTitleResId = R.string.allow_media_routing_control
|
||||
override val footerResId = R.string.allow_media_routing_description
|
||||
override val appOp = AppOpsManager.OP_MEDIA_ROUTING_CONTROL
|
||||
override val permission = Manifest.permission.MEDIA_ROUTING_CONTROL
|
||||
override val setModeByUid = true
|
||||
private val roleManager = context.getSystemService(RoleManager::class.java)
|
||||
|
||||
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
|
||||
super.setAllowed(record, newAllowed)
|
||||
logPermissionToggleAction(newAllowed)
|
||||
}
|
||||
|
||||
override fun isChangeable(record: AppOpPermissionRecord): Boolean {
|
||||
return super.isChangeable(record) && (this.roleManager
|
||||
?.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH)
|
||||
?.contains(record.app.packageName) == true)
|
||||
}
|
||||
|
||||
private fun logPermissionToggleAction(newAllowed: Boolean) {
|
||||
featureFactory.metricsFeatureProvider.action(
|
||||
context,
|
||||
SettingsEnums.MEDIA_ROUTING_CONTROL,
|
||||
if (newAllowed)
|
||||
VALUE_LOGGING_ALLOWED
|
||||
else
|
||||
VALUE_LOGGING_DENIED
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VALUE_LOGGING_ALLOWED = 1
|
||||
const val VALUE_LOGGING_DENIED = 0
|
||||
}
|
||||
}
|
@@ -61,6 +61,7 @@ object SpecialAppAccessPageProvider : SettingsPageProvider {
|
||||
AllFilesAccessAppListProvider,
|
||||
DisplayOverOtherAppsAppListProvider,
|
||||
MediaManagementAppsAppListProvider,
|
||||
MediaRoutingControlAppListProvider,
|
||||
ModifySystemSettingsAppListProvider,
|
||||
UseFullScreenIntentAppListProvider,
|
||||
PictureInPictureListProvider,
|
||||
|
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settings.applications.specialaccess;
|
||||
|
||||
import static com.android.settingslib.spa.framework.util.SpaIntentKt.KEY_DESTINATION;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.media.flags.Flags;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.spa.SpaActivity;
|
||||
import com.android.settings.spa.app.specialaccess.MediaRoutingControlAppListProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class MediaRoutingControlPreferenceControllerTest {
|
||||
|
||||
private static final String PREFERENCE_KEY = "test_preference_key";
|
||||
private static final String DIFFERENT_PREFERENCE_KEY = "other_preference_key";
|
||||
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private MediaRoutingControlPreferenceController mTestController;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
mTestController = new MediaRoutingControlPreferenceController(
|
||||
mContext, PREFERENCE_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
||||
public void getAvailabilityStatus_withFlagEnabled_shouldReturnTrue() {
|
||||
assertThat(mTestController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
|
||||
public void getAvailabilityStatus_withFlagDisabled_shouldReturnFalse() {
|
||||
assertThat(mTestController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_withDifferentPreference_shouldReturnFalse() {
|
||||
Preference preference = mock(Preference.class);
|
||||
when(preference.getKey()).thenReturn(DIFFERENT_PREFERENCE_KEY);
|
||||
|
||||
assertThat(mTestController.handlePreferenceTreeClick(preference)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_withMediaRoutingPreference_shouldReturnTrue() {
|
||||
Preference preference = mock(Preference.class);
|
||||
when(preference.getKey()).thenReturn(PREFERENCE_KEY);
|
||||
|
||||
assertThat(mTestController.handlePreferenceTreeClick(preference)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_withDifferentPreference_shouldNotStartSpaActivity() {
|
||||
Preference preference = mock(Preference.class);
|
||||
when(preference.getKey()).thenReturn(DIFFERENT_PREFERENCE_KEY);
|
||||
|
||||
mTestController.handlePreferenceTreeClick(preference);
|
||||
|
||||
verify(mContext, never()).startActivity(any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_withMediaRoutingPreference_shouldStartSpaActivity() {
|
||||
Preference preference = mock(Preference.class);
|
||||
when(preference.getKey()).thenReturn(PREFERENCE_KEY);
|
||||
|
||||
mTestController.handlePreferenceTreeClick(preference);
|
||||
|
||||
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
verify(mContext).startActivity(intentCaptor.capture());
|
||||
final Intent intent = intentCaptor.getValue();
|
||||
assertThat(intent.getComponent().getClassName()).isEqualTo(SpaActivity.class.getName());
|
||||
assertThat(intent.getStringExtra(KEY_DESTINATION)).isEqualTo(
|
||||
MediaRoutingControlAppListProvider.INSTANCE.getAppListRoute());
|
||||
}
|
||||
}
|
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settings.spa.app.specialaccess
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AppOpsManager
|
||||
import android.app.role.RoleManager
|
||||
import android.app.settings.SettingsEnums
|
||||
import android.companion.AssociationRequest
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.testutils.FakeFeatureFactory
|
||||
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
|
||||
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MediaRoutingControlTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Spy
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private lateinit var listModel: MediaRoutingControlAppsListModel
|
||||
|
||||
@Mock
|
||||
private lateinit var mockRoleManager: RoleManager
|
||||
|
||||
private val fakeFeatureFactory = FakeFeatureFactory()
|
||||
private val metricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(context.getSystemService(RoleManager::class.java))
|
||||
.thenReturn(mockRoleManager)
|
||||
listModel = MediaRoutingControlAppsListModel(context)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun modelResourceIdAndProperties() {
|
||||
assertThat(listModel.pageTitleResId).isEqualTo(R.string.media_routing_control_title)
|
||||
assertThat(listModel.switchTitleResId).isEqualTo(R.string.allow_media_routing_control)
|
||||
assertThat(listModel.footerResId).isEqualTo(R.string.allow_media_routing_description)
|
||||
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_MEDIA_ROUTING_CONTROL)
|
||||
assertThat(listModel.permission).isEqualTo(Manifest.permission.MEDIA_ROUTING_CONTROL)
|
||||
assertThat(listModel.setModeByUid).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsTrue_shouldChangeAppControllerModeToAllowed() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, true)
|
||||
|
||||
assertThat(fakeAppOpController.getMode()).isEqualTo(AppOpsManager.MODE_ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsTrue_shouldLogPermissionToggleActionAsAllowed() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, true)
|
||||
|
||||
verify(metricsFeatureProvider)
|
||||
.action(context, SettingsEnums.MEDIA_ROUTING_CONTROL, VALUE_LOGGING_ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsFalse_shouldChangeAppControllerModeToErrored() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, false)
|
||||
|
||||
assertThat(fakeAppOpController.getMode()).isEqualTo(AppOpsManager.MODE_ERRORED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed_callWithNewStatusAsFalse_shouldLogPermissionToggleActionAsDenied() {
|
||||
val fakeAppOpController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController = fakeAppOpController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(permissionRequestedRecord, false)
|
||||
|
||||
verify(metricsFeatureProvider)
|
||||
.action(context, SettingsEnums.MEDIA_ROUTING_CONTROL, VALUE_LOGGING_DENIED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_permissionRequestedByAppAndWatchCompanionRoleAssigned_shouldReturnTrue() {
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf(PACKAGE_NAME))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_permissionNotRequestedByAppButWatchCompanionRoleAssigned_shouldReturnFalse() {
|
||||
val permissionNotRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = false,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf(PACKAGE_NAME))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionNotRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_permissionRequestedByAppButWatchCompanionRoleNotAssigned_shouldReturnFalse() {
|
||||
val permissionRequestedRecord =
|
||||
AppOpPermissionRecord(
|
||||
app = ApplicationInfo().apply { packageName = PACKAGE_NAME },
|
||||
hasRequestPermission = true,
|
||||
hasRequestBroaderPermission = false,
|
||||
appOpsController =
|
||||
FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
whenever(mockRoleManager.getRoleHolders(AssociationRequest.DEVICE_PROFILE_WATCH))
|
||||
.thenReturn(listOf("other.package.name"))
|
||||
|
||||
val isSpecialAccessChangeable = listModel.isChangeable(permissionRequestedRecord)
|
||||
|
||||
assertThat(isSpecialAccessChangeable).isFalse()
|
||||
}
|
||||
|
||||
private class FakeAppOpsController(fakeMode: Int) : IAppOpsController {
|
||||
|
||||
override val mode = MutableLiveData(fakeMode)
|
||||
|
||||
override fun setAllowed(allowed: Boolean) {
|
||||
if (allowed)
|
||||
mode.postValue(AppOpsManager.MODE_ALLOWED)
|
||||
else
|
||||
mode.postValue(AppOpsManager.MODE_ERRORED)
|
||||
}
|
||||
|
||||
override fun getMode(): Int = mode.value!!
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PACKAGE_NAME = "test.package.name"
|
||||
const val VALUE_LOGGING_ALLOWED = 1
|
||||
const val VALUE_LOGGING_DENIED = 0
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user