diff --git a/res/values/strings.xml b/res/values/strings.xml index ad117062aae..de9b8e879f1 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9359,4 +9359,18 @@ See Android 8.0 battery settings + + Toggle wifi on and off + + toggle wifi on and off + + + Toggle wifi on and off + + + Allow toggle wifi + + + Allow this app to change wifi state including connecting to wifi and turing wifi on and off. + diff --git a/res/xml/change_wifi_state_details.xml b/res/xml/change_wifi_state_details.xml new file mode 100644 index 00000000000..6121f002bf1 --- /dev/null +++ b/res/xml/change_wifi_state_details.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml index 7205eaf28ab..d1eb623aebd 100644 --- a/res/xml/special_access.xml +++ b/res/xml/special_access.xml @@ -121,4 +121,14 @@ android:value="com.android.settings.Settings$DirectoryAccessSettingsActivity" /> + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 599d6fbe762..e6dc48e9f05 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -128,6 +128,7 @@ public class Settings extends SettingsActivity { public static class AppMemoryUsageActivity extends SettingsActivity { /* empty */ } public static class OverlaySettingsActivity extends SettingsActivity { /* empty */ } public static class WriteSettingsActivity extends SettingsActivity { /* empty */ } + public static class ChangeWifiStateActivity extends SettingsActivity { /* empty */ } public static class AppDrawOverlaySettingsActivity extends SettingsActivity { /* empty */ } public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ } public static class AdvancedAppsActivity extends SettingsActivity { /* empty */ } diff --git a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java index fb5caf78867..8ebb7a41b03 100644 --- a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java +++ b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java @@ -26,6 +26,7 @@ import com.android.settings.applications.AppStatePowerBridge; import com.android.settings.applications.AppStateDirectoryAccessBridge; import com.android.settings.applications.AppStateUsageBridge; import com.android.settings.applications.AppStateWriteSettingsBridge; +import com.android.settings.wifi.AppStateChangeWifiStateBridge; import com.android.settingslib.applications.ApplicationsState; /** @@ -67,14 +68,15 @@ public class AppFilterRegistry { public static final int FILTER_APPS_WRITE_SETTINGS = 11; public static final int FILTER_APPS_INSTALL_SOURCES = 12; public static final int FILTER_APP_HAS_DIRECTORY_ACCESS = 13; - // Next id: 14 + public static final int FILTER_APP_CAN_CHANGE_WIFI_STATE = 14; + // Next id: 15 private static AppFilterRegistry sRegistry; private final AppFilterItem[] mFilters; private AppFilterRegistry() { - mFilters = new AppFilterItem[14]; + mFilters = new AppFilterItem[15]; // High power whitelist, on mFilters[FILTER_APPS_POWER_WHITELIST] = new AppFilterItem( @@ -163,6 +165,11 @@ public class AppFilterRegistry { AppStateDirectoryAccessBridge.FILTER_APP_HAS_DIRECTORY_ACCESS, FILTER_APP_HAS_DIRECTORY_ACCESS, R.string.filter_install_sources_apps); + + mFilters[FILTER_APP_CAN_CHANGE_WIFI_STATE] = new AppFilterItem( + AppStateChangeWifiStateBridge.FILTER_CHANGE_WIFI_STATE, + FILTER_APP_CAN_CHANGE_WIFI_STATE, + R.string.filter_write_settings_apps); } public static AppFilterRegistry getInstance() { @@ -187,6 +194,8 @@ public class AppFilterRegistry { return FILTER_APPS_INSTALL_SOURCES; case ManageApplications.LIST_TYPE_DIRECTORY_ACCESS: return FILTER_APP_HAS_DIRECTORY_ACCESS; + case ManageApplications.LIST_TYPE_WIFI_ACCESS: + return FILTER_APP_CAN_CHANGE_WIFI_STATE; default: return FILTER_APPS_ALL; } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index f9794d2b6bd..8f4cbe386a7 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -96,6 +96,8 @@ import com.android.settings.applications.appinfo.AppNotificationPreferenceContro import com.android.settings.applications.appinfo.DrawOverlayDetails; import com.android.settings.applications.appinfo.ExternalSourcesDetails; import com.android.settings.applications.appinfo.WriteSettingsDetails; + +import com.android.settings.core.FeatureFlags; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.SummaryLoader; @@ -105,6 +107,8 @@ import com.android.settings.notification.ConfigureNotificationSettings; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend.AppRow; import com.android.settings.widget.LoadingViewController; +import com.android.settings.wifi.AppStateChangeWifiStateBridge; +import com.android.settings.wifi.ChangeWifiStateDetails; import com.android.settingslib.HelpUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -204,6 +208,7 @@ public class ManageApplications extends InstrumentedPreferenceFragment public static final int LIST_TYPE_MOVIES = 10; public static final int LIST_TYPE_PHOTOGRAPHY = 11; public static final int LIST_TYPE_DIRECTORY_ACCESS = 12; + public static final int LIST_TYPE_WIFI_ACCESS = 13; // List types that should show instant apps. public static final Set LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList( @@ -277,6 +282,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment } else if (className.equals(Settings.DirectoryAccessSettingsActivity.class.getName())) { mListType = LIST_TYPE_DIRECTORY_ACCESS; screenTitle = R.string.directory_access; + } else if (className.equals(Settings.ChangeWifiStateActivity.class.getName())) { + mListType = LIST_TYPE_WIFI_ACCESS; + screenTitle = R.string.change_wifi_state_title; } else { mListType = LIST_TYPE_MAIN; } @@ -443,6 +451,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment return MetricsEvent.MANAGE_EXTERNAL_SOURCES; case LIST_TYPE_DIRECTORY_ACCESS: return MetricsEvent.DIRECTORY_ACCESS; + case LIST_TYPE_WIFI_ACCESS: + return MetricsEvent.CONFIGURE_WIFI; default: return MetricsEvent.VIEW_UNKNOWN; } @@ -540,7 +550,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment case LIST_TYPE_DIRECTORY_ACCESS: startAppInfoFragment(DirectoryAccessDetails.class, R.string.directory_access); break; - + case LIST_TYPE_WIFI_ACCESS: + startAppInfoFragment(ChangeWifiStateDetails.class, R.string.change_wifi_state_title); + break; // TODO: Figure out if there is a way where we can spin up the profile's settings // process ahead of time, to avoid a long load of data when user clicks on a managed // app. Maybe when they load the list of apps that contains managed profile apps. @@ -846,6 +858,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this); } else if (mManageApplications.mListType == LIST_TYPE_DIRECTORY_ACCESS) { mExtraInfoBridge = new AppStateDirectoryAccessBridge(mState, this); + } else if (mManageApplications.mListType == LIST_TYPE_WIFI_ACCESS) { + mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this); } else { mExtraInfoBridge = null; } @@ -1251,6 +1265,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment case LIST_TYPE_DIRECTORY_ACCESS: holder.setSummary(null); break; + case LIST_TYPE_WIFI_ACCESS: + holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry)); + break; default: holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); break; diff --git a/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java b/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java new file mode 100644 index 00000000000..23d6b67712a --- /dev/null +++ b/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 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.wifi; + +import android.Manifest; +import android.app.AppOpsManager; +import android.content.Context; + +import com.android.settings.applications.AppStateAppOpsBridge; +import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.applications.ApplicationsState.AppFilter; + +import java.util.List; +/* + * Connects info of apps that change wifi state to the ApplicationsState. Wraps around the generic + * AppStateAppOpsBridge class to tailor to the semantics of CHANGE_WIFI_STATE. Also provides app + * filters that can use the info. + */ +public class AppStateChangeWifiStateBridge extends AppStateAppOpsBridge { + + private static final String TAG = "AppStateChangeWifiStateBridge"; + private static final int APP_OPS_OP_CODE = AppOpsManager.OP_CHANGE_WIFI_STATE; + private static final String PM_CHANGE_WIFI_STATE = Manifest.permission.CHANGE_WIFI_STATE; + + private static final String[] PM_PERMISSIONS = { + PM_CHANGE_WIFI_STATE + }; + + public AppStateChangeWifiStateBridge(Context context, ApplicationsState appState, Callback + callback) { + super(context, appState, callback, APP_OPS_OP_CODE, PM_PERMISSIONS); + } + + @Override + protected void updateExtraInfo(AppEntry app, String pkg, int uid) { + app.extraInfo = getWifiSettingsInfo(pkg, uid); + } + + @Override + protected void loadAllExtraInfo() { + final List allApps = mAppSession.getAllApps(); + for (AppEntry entry : allApps) { + updateExtraInfo(entry, entry.info.packageName, entry.info.uid); + } + } + + public WifiSettingsState getWifiSettingsInfo(String pkg, int uid) { + PermissionState permissionState = super.getPermissionInfo(pkg, uid); + return new WifiSettingsState(permissionState); + } + + public static class WifiSettingsState extends AppStateAppOpsBridge.PermissionState { + public WifiSettingsState(PermissionState permissionState) { + super(permissionState.packageName, permissionState.userHandle); + this.packageInfo = permissionState.packageInfo; + this.appOpMode = permissionState.appOpMode; + this.permissionDeclared = permissionState.permissionDeclared; + this.staticPermissionGranted = permissionState.staticPermissionGranted; + } + } + + public static final AppFilter FILTER_CHANGE_WIFI_STATE = new AppFilter() { + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry info) { + if (info == null || info.extraInfo == null) { + return false; + } + WifiSettingsState wifiSettingsState = (WifiSettingsState) info.extraInfo; + return wifiSettingsState.permissionDeclared; + } + }; +} diff --git a/src/com/android/settings/wifi/ChangeWifiStateDetails.java b/src/com/android/settings/wifi/ChangeWifiStateDetails.java new file mode 100644 index 00000000000..1df11e05039 --- /dev/null +++ b/src/com/android/settings/wifi/ChangeWifiStateDetails.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2018 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.wifi; + +import android.Manifest.permission; +import android.app.AlertDialog; +import android.app.AppOpsManager; +import android.content.Context; +import android.os.Bundle; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.applications.AppInfoWithHeader; +import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; +import com.android.settings.R; +import com.android.settings.overlay.FeatureFactory; + +import com.android.settings.wifi.AppStateChangeWifiStateBridge.WifiSettingsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.applications.ApplicationsState.AppFilter; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_IGNORED; + +public class ChangeWifiStateDetails extends AppInfoWithHeader + implements OnPreferenceChangeListener { + + private static final String KEY_APP_OPS_SETTINGS_SWITCH = "app_ops_settings_switch"; + private static final String LOG_TAG = "ChangeWifiStateDetails"; + + private AppStateChangeWifiStateBridge mAppBridge; + private AppOpsManager mAppOpsManager; + private SwitchPreference mSwitchPref; + private WifiSettingsState mWifiSettingsState; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Context context = getActivity(); + mAppBridge = new AppStateChangeWifiStateBridge(context, mState, null); + mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + + // find preferences + addPreferencesFromResource(R.xml.change_wifi_state_details); + mSwitchPref = (SwitchPreference) findPreference(KEY_APP_OPS_SETTINGS_SWITCH); + + // set title/summary for all of them + mSwitchPref.setTitle(R.string.change_wifi_state_app_detail_switch); + + // install event listeners + mSwitchPref.setOnPreferenceChangeListener(this); + } + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + return null; + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.CONFIGURE_WIFI; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mSwitchPref) { + if (mWifiSettingsState != null && (Boolean) newValue + != mWifiSettingsState.isPermissible()) { + setCanChangeWifiState(!mWifiSettingsState.isPermissible()); + refreshUi(); + } + return true; + } + return false; + } + + private void setCanChangeWifiState(boolean newState) { + logSpecialPermissionChange(newState, mPackageName); + mAppOpsManager.setMode(AppOpsManager.OP_CHANGE_WIFI_STATE, + mPackageInfo.applicationInfo.uid, mPackageName, newState + ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); + } + + protected void logSpecialPermissionChange(boolean newState, String packageName) { + int logCategory = newState ? MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW + : MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY; + FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(), + logCategory, packageName); + } + + @Override + protected boolean refreshUi() { + if (mPackageInfo == null || mPackageInfo.applicationInfo == null) { + return false; + } + mWifiSettingsState = mAppBridge.getWifiSettingsInfo(mPackageName, + mPackageInfo.applicationInfo.uid); + + boolean canChange = mWifiSettingsState.isPermissible(); + mSwitchPref.setChecked(canChange); + // you can't ask a user for a permission you didn't even declare! + mSwitchPref.setEnabled(mWifiSettingsState.permissionDeclared); + return true; + } + + public static CharSequence getSummary(Context context, AppEntry entry) { + WifiSettingsState state; + if (entry.extraInfo instanceof WifiSettingsState) { + state = (WifiSettingsState) entry.extraInfo; + } else if (entry.extraInfo instanceof PermissionState) { + state = new WifiSettingsState((PermissionState) entry.extraInfo); + } else { + state = new AppStateChangeWifiStateBridge(context, null, null).getWifiSettingsInfo( + entry.info.packageName, entry.info.uid); + } + return getSummary(context, state); + } + + public static CharSequence getSummary(Context context, WifiSettingsState wifiSettingsState) { + return context.getString(wifiSettingsState.isPermissible() + ? R.string.app_permission_summary_allowed + : R.string.app_permission_summary_not_allowed); + } +} diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable index 748dc94439e..37512228898 100644 --- a/tests/robotests/assets/grandfather_not_implementing_indexable +++ b/tests/robotests/assets/grandfather_not_implementing_indexable @@ -71,3 +71,4 @@ com.android.settings.ApnEditor com.android.settings.UserCredentialsSettings com.android.settings.TestingSettings com.android.settings.applications.DirectoryAccessDetails +com.android.settings.wifi.ChangeWifiStateDetails diff --git a/tests/robotests/src/com/android/settings/wifi/AppStateChangeWifiStateBridgeTest.java b/tests/robotests/src/com/android/settings/wifi/AppStateChangeWifiStateBridgeTest.java new file mode 100644 index 00000000000..bd4c558bde9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/AppStateChangeWifiStateBridgeTest.java @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2018 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.wifi; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import android.app.AppOpsManager; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.applications.ApplicationsState.AppFilter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AppStateChangeWifiStateBridgeTest { + + @Mock + private AppEntry mEntry; + @Mock + private AppStateChangeWifiStateBridge.WifiSettingsState mState; + private Context mContext; + private AppFilter mFilter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mFilter = AppStateChangeWifiStateBridge.FILTER_CHANGE_WIFI_STATE; + } + + @Test + public void testFilterApp_inputNull_returnFalse() { + assertThat(mFilter.filterApp(null)).isFalse(); + } + + @Test + public void testFilterApp_extraInfoNull_returnFalse() { + mEntry.extraInfo = null; + assertThat(mFilter.filterApp(mEntry)).isFalse(); + } + + @Test + public void testFilterApp_permissionedDeclaredTrue_returnTrue() { + mState.permissionDeclared = true; + mEntry.extraInfo = mState; + assertThat(mFilter.filterApp(mEntry)).isTrue(); + } + + @Test + public void testFilterApp_permissionedDeclaredFalse_returnFalse() { + mState.permissionDeclared = false; + mEntry.extraInfo = mState; + assertThat(mFilter.filterApp(mEntry)).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/ChangeWifiStateDetailsTest.java b/tests/robotests/src/com/android/settings/wifi/ChangeWifiStateDetailsTest.java new file mode 100644 index 00000000000..4a8bbef8691 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/ChangeWifiStateDetailsTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2018 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.wifi; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import com.android.settingslib.applications.ApplicationsState.AppEntry; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ChangeWifiStateDetailsTest { + private static final String PACKAGE_NAME = "app"; + private FakeFeatureFactory mFeatureFactory; + private ChangeWifiStateDetails mFragment; + private Context mContext; + + @Mock + private AppEntry mAppEntry; + @Mock + private AppStateChangeWifiStateBridge.WifiSettingsState mState; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mFragment = new ChangeWifiStateDetails(); + } + + @Test + public void testLogSpecialPermissionChange_setTrue_returnAllow() { + mFragment.logSpecialPermissionChange(true /*newState*/, PACKAGE_NAME); + verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class), + eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW), + eq(PACKAGE_NAME)); + } + + @Test + public void testLogSpecialPermissionChange_setFalse_returnDeny() { + mFragment.logSpecialPermissionChange(false /*newState*/, PACKAGE_NAME); + verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class), + eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY), + eq(PACKAGE_NAME)); + } + + @Test + public void testGetSummary_permissibleTrue_returnAllowed() { + when(mState.isPermissible()).thenReturn(true); + assertThat(mFragment.getSummary(mContext, mState)).isEqualTo( + mContext.getString(R.string.app_permission_summary_allowed)); + } + + @Test + public void testGetSummary_permissibleFalse_returnNotAllowed() { + when(mState.isPermissible()).thenReturn(false); + assertThat(mFragment.getSummary(mContext, mState)).isEqualTo( + mContext.getString(R.string.app_permission_summary_not_allowed)); + } +}