1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 package com.android.settings.datausage; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.ArgumentMatchers.anyBoolean; 22 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.ArgumentMatchers.nullable; 24 import static org.mockito.Mockito.doAnswer; 25 import static org.mockito.Mockito.doNothing; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.spy; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 32 import android.content.Context; 33 import android.content.pm.ApplicationInfo; 34 import android.os.Process; 35 36 import androidx.preference.PreferenceManager; 37 import androidx.preference.PreferenceScreen; 38 39 import com.android.internal.logging.nano.MetricsProto; 40 import com.android.settings.R; 41 import com.android.settings.applications.AppStateBaseBridge; 42 import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; 43 import com.android.settings.testutils.FakeFeatureFactory; 44 import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils; 45 import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; 46 import com.android.settingslib.applications.ApplicationsState; 47 import com.android.settingslib.applications.ApplicationsState.AppEntry; 48 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 import org.robolectric.RobolectricTestRunner; 55 import org.robolectric.RuntimeEnvironment; 56 import org.robolectric.annotation.Config; 57 import org.robolectric.annotation.Implements; 58 import org.robolectric.util.ReflectionHelpers; 59 60 import java.util.ArrayList; 61 62 @RunWith(RobolectricTestRunner.class) 63 @Config(shadows = { 64 ShadowRestrictedLockUtils.class, 65 ShadowRestrictedLockUtilsInternal.class, 66 UnrestrictedDataAccessPreferenceControllerTest.ShadowAppStateBaseBridge.class 67 }) 68 public class UnrestrictedDataAccessPreferenceControllerTest { 69 @Mock 70 private ApplicationsState mState; 71 @Mock 72 private ApplicationsState.Session mSession; 73 74 private Context mContext; 75 private FakeFeatureFactory mFeatureFactory; 76 private PreferenceManager mPreferenceManager; 77 private PreferenceScreen mPreferenceScreen; 78 private UnrestrictedDataAccess mFragment; 79 private UnrestrictedDataAccessPreferenceController mController; 80 81 @Before setUp()82 public void setUp() { 83 MockitoAnnotations.initMocks(this); 84 mContext = RuntimeEnvironment.application; 85 mFeatureFactory = FakeFeatureFactory.setupForTest(); 86 ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", mState); 87 when(mState.newSession(any())).thenReturn(mSession); 88 mController = spy(new UnrestrictedDataAccessPreferenceController(mContext, "pref_key")); 89 } 90 91 @Test shouldAddPreference_forApps_shouldBeTrue()92 public void shouldAddPreference_forApps_shouldBeTrue() { 93 final int uid = Process.FIRST_APPLICATION_UID + 10; 94 final AppEntry entry = createAppEntry(uid); 95 assertThat(UnrestrictedDataAccessPreferenceController.shouldAddPreference(entry)).isTrue(); 96 } 97 98 @Test shouldAddPreference_forNonApps_shouldBeFalse()99 public void shouldAddPreference_forNonApps_shouldBeFalse() { 100 final int uid = Process.FIRST_APPLICATION_UID - 10; 101 final AppEntry entry = createAppEntry(uid); 102 assertThat(UnrestrictedDataAccessPreferenceController.shouldAddPreference(entry)).isFalse(); 103 } 104 105 @Test logSpecialPermissionChange()106 public void logSpecialPermissionChange() { 107 mController.logSpecialPermissionChange(true, "app"); 108 verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class), 109 eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW), eq("app")); 110 111 mController.logSpecialPermissionChange(false, "app"); 112 verify(mFeatureFactory.metricsFeatureProvider).action(nullable(Context.class), 113 eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_DENY), eq("app")); 114 } 115 116 @Test onRebuildComplete_restricted_shouldBeDisabled()117 public void onRebuildComplete_restricted_shouldBeDisabled() { 118 mFragment = spy(new UnrestrictedDataAccess()); 119 doNothing().when(mFragment).setLoading(anyBoolean(), anyBoolean()); 120 mController.setParentFragment(mFragment); 121 mPreferenceManager = new PreferenceManager(mContext); 122 mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(mContext)); 123 doReturn(mPreferenceManager).when(mFragment).getPreferenceManager(); 124 doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen(); 125 doReturn(0).when(mPreferenceScreen).getPreferenceCount(); 126 final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class); 127 ReflectionHelpers.setField(mController, "mDataSaverBackend", dataSaverBackend); 128 ReflectionHelpers.setField(mController, "mScreen", mPreferenceScreen); 129 130 final String testPkg1 = "com.example.one"; 131 final String testPkg2 = "com.example.two"; 132 ShadowRestrictedLockUtilsInternal.setRestrictedPkgs(testPkg2); 133 134 doAnswer((invocation) -> { 135 final UnrestrictedDataAccessPreference preference = invocation.getArgument(0); 136 final AppEntry entry = preference.getEntry(); 137 // Verify preference is disabled by admin and the summary is changed accordingly. 138 if (testPkg1.equals(entry.info.packageName)) { 139 assertThat(preference.isDisabledByAdmin()).isFalse(); 140 assertThat(preference.getSummary()).isEqualTo(""); 141 } else if (testPkg2.equals(entry.info.packageName)) { 142 assertThat(preference.isDisabledByAdmin()).isTrue(); 143 assertThat(preference.getSummary()).isEqualTo( 144 mContext.getString(R.string.disabled_by_admin)); 145 } 146 assertThat(preference.isChecked()).isFalse(); 147 preference.performClick(); 148 // Verify that when the preference is clicked, support details intent is launched 149 // if the preference is disabled by admin, otherwise the switch is toggled. 150 if (testPkg1.equals(entry.info.packageName)) { 151 assertThat(preference.isChecked()).isTrue(); 152 assertThat(ShadowRestrictedLockUtils.hasAdminSupportDetailsIntentLaunched()) 153 .isFalse(); 154 } else if (testPkg2.equals(entry.info.packageName)) { 155 assertThat(preference.isChecked()).isFalse(); 156 assertThat(ShadowRestrictedLockUtils.hasAdminSupportDetailsIntentLaunched()) 157 .isTrue(); 158 } 159 ShadowRestrictedLockUtils.clearAdminSupportDetailsIntentLaunch(); 160 return null; 161 }).when(mPreferenceScreen).addPreference(any(UnrestrictedDataAccessPreference.class)); 162 163 mController.onRebuildComplete(createAppEntries(testPkg1, testPkg2)); 164 } 165 createAppEntries(String... packageNames)166 private ArrayList<AppEntry> createAppEntries(String... packageNames) { 167 final ArrayList<AppEntry> appEntries = new ArrayList<>(); 168 for (int i = 0; i < packageNames.length; ++i) { 169 final ApplicationInfo info = new ApplicationInfo(); 170 info.packageName = packageNames[i]; 171 info.uid = Process.FIRST_APPLICATION_UID + i; 172 info.sourceDir = info.packageName; 173 final AppEntry appEntry = spy(new AppEntry(mContext, info, i)); 174 appEntry.extraInfo = new DataUsageState(false, false); 175 doNothing().when(appEntry).ensureLabel(any(Context.class)); 176 ReflectionHelpers.setField(appEntry, "info", info); 177 appEntries.add(appEntry); 178 } 179 return appEntries; 180 } 181 createAppEntry(int uid)182 private AppEntry createAppEntry(int uid) { 183 final ApplicationInfo info = new ApplicationInfo(); 184 info.packageName = "com.example.three"; 185 info.uid = uid; 186 info.sourceDir = info.packageName; 187 return new AppEntry(mContext, info, uid); 188 } 189 190 @Implements(AppStateBaseBridge.class) 191 public static class ShadowAppStateBaseBridge { 192 __constructor__(ApplicationsState appState, AppStateBaseBridge.Callback callback)193 public void __constructor__(ApplicationsState appState, 194 AppStateBaseBridge.Callback callback) { 195 } 196 } 197 } 198