diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0418d51dfc0..4beaf8fc5f7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -178,7 +178,6 @@ Power button ends call Large mouse pointer + + Remove animations Mono audio @@ -7318,8 +7320,8 @@ Media - - Includes system feedback + + Includes system feedback like touch and charging sounds Reminders @@ -9132,4 +9134,9 @@ This feature is not available on this device + + Force Full GnssMeasurement + + Disable GNSS duty-cycling, track all constellations and frequencies. + diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml index d7d2e02e4e1..b5da848eb30 100644 --- a/res/xml/accessibility_settings.xml +++ b/res/xml/accessibility_settings.xml @@ -62,6 +62,10 @@ + + + + diff --git a/res/xml/security_settings_unification.xml b/res/xml/security_settings_unification.xml index 43b8b09be6c..b9c59e599f7 100644 --- a/res/xml/security_settings_unification.xml +++ b/res/xml/security_settings_unification.xml @@ -18,7 +18,7 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/security_settings_title"> - getCredentialsForUid(KeyStore keyStore, int uid) { final SortedMap aliasMap = new TreeMap<>(); for (final Credential.Type type : Credential.Type.values()) { - for (final String alias : keyStore.list(type.prefix, uid)) { - if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { - // Do not show work profile keys in user credentials - if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) || - alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) { + for (final String prefix : type.prefix) { + for (final String alias : keyStore.list(prefix, uid)) { + if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { + // Do not show work profile keys in user credentials + if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) || + alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) { + continue; + } + // Do not show synthetic password keys in user credential + if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) { + continue; + } + } + try { + if (type == Credential.Type.USER_KEY && + !isAsymmetric(keyStore, prefix + alias, uid)) { + continue; + } + } catch (UnrecoverableKeyException e) { + Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e); continue; } - // Do not show synthetic password keys in user credential - if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) { - continue; + Credential c = aliasMap.get(alias); + if (c == null) { + c = new Credential(alias, uid); + aliasMap.put(alias, c); } + c.storedTypes.add(type); } - Credential c = aliasMap.get(alias); - if (c == null) { - c = new Credential(alias, uid); - aliasMap.put(alias, c); - } - c.storedTypes.add(type); } } return aliasMap; @@ -344,7 +372,7 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment */ private static final SparseArray credentialViewTypes = new SparseArray<>(); static { - credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_PRIVATE_KEY); + credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_KEY); credentialViewTypes.put(R.id.contents_usercrt, Credential.Type.USER_CERTIFICATE); credentialViewTypes.put(R.id.contents_cacrt, Credential.Type.CA_CERTIFICATE); } @@ -380,12 +408,11 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment static enum Type { CA_CERTIFICATE (Credentials.CA_CERTIFICATE), USER_CERTIFICATE (Credentials.USER_CERTIFICATE), - USER_PRIVATE_KEY (Credentials.USER_PRIVATE_KEY), - USER_SECRET_KEY (Credentials.USER_SECRET_KEY); + USER_KEY(Credentials.USER_PRIVATE_KEY, Credentials.USER_SECRET_KEY); - final String prefix; + final String[] prefix; - Type(String prefix) { + Type(String... prefix) { this.prefix = prefix; } } @@ -407,8 +434,7 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment *
    *
  • {@link Credentials.CA_CERTIFICATE}
  • *
  • {@link Credentials.USER_CERTIFICATE}
  • - *
  • {@link Credentials.USER_PRIVATE_KEY}
  • - *
  • {@link Credentials.USER_SECRET_KEY}
  • + *
  • {@link Credentials.USER_KEY}
  • *
*/ final EnumSet storedTypes = EnumSet.noneOf(Type.class); diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 21614155029..160176e1c19 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -97,6 +97,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements "toggle_lock_screen_rotation_preference"; private static final String TOGGLE_LARGE_POINTER_ICON = "toggle_large_pointer_icon"; + private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations"; private static final String TOGGLE_MASTER_MONO = "toggle_master_mono"; private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE = @@ -135,6 +136,14 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements // presentation. private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; + // Settings that should be changed when toggling animations + private static final String[] TOGGLE_ANIMATION_TARGETS = { + Settings.Global.WINDOW_ANIMATION_SCALE, Settings.Global.TRANSITION_ANIMATION_SCALE, + Settings.Global.ANIMATOR_DURATION_SCALE + }; + private static final String ANIMATION_ON_VALUE = "1"; + private static final String ANIMATION_OFF_VALUE = "0"; + private final Map mLongPressTimeoutValueToTitleMap = new HashMap<>(); private final Handler mHandler = new Handler(); @@ -194,6 +203,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements private SwitchPreference mTogglePowerButtonEndsCallPreference; private SwitchPreference mToggleLockScreenRotationPreference; private SwitchPreference mToggleLargePointerIconPreference; + private SwitchPreference mToggleDisableAnimationsPreference; private SwitchPreference mToggleMasterMonoPreference; private ListPreference mSelectLongPressTimeoutPreference; private Preference mNoServicesMessagePreference; @@ -317,6 +327,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } else if (mToggleLargePointerIconPreference == preference) { handleToggleLargePointerIconPreferenceClick(); return true; + } else if (mToggleDisableAnimationsPreference == preference) { + handleToggleDisableAnimations(); + return true; } else if (mToggleMasterMonoPreference == preference) { handleToggleMasterMonoPreferenceClick(); return true; @@ -349,6 +362,14 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleLargePointerIconPreference.isChecked() ? 1 : 0); } + private void handleToggleDisableAnimations() { + String newAnimationValue = mToggleDisableAnimationsPreference.isChecked() + ? ANIMATION_OFF_VALUE : ANIMATION_ON_VALUE; + for (String animationPreference : TOGGLE_ANIMATION_TARGETS) { + Settings.Global.putString(getContentResolver(), animationPreference, newAnimationValue); + } + } + private void handleToggleMasterMonoPreferenceClick() { Settings.System.putIntForUser(getContentResolver(), Settings.System.MASTER_MONO, mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT); @@ -389,6 +410,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleLargePointerIconPreference = (SwitchPreference) findPreference(TOGGLE_LARGE_POINTER_ICON); + mToggleDisableAnimationsPreference = + (SwitchPreference) findPreference(TOGGLE_DISABLE_ANIMATIONS); + // Master Mono mToggleMasterMonoPreference = (SwitchPreference) findPreference(TOGGLE_MASTER_MONO); @@ -620,6 +644,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleLargePointerIconPreference.setChecked(Settings.Secure.getInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 0) != 0); + updateDisableAnimationsToggle(); + // Master mono updateMasterMono(); @@ -702,6 +728,19 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } } + private void updateDisableAnimationsToggle() { + boolean allAnimationsDisabled = true; + for (String animationSetting : TOGGLE_ANIMATION_TARGETS) { + if (!TextUtils.equals( + Settings.Global.getString(getContentResolver(), animationSetting), + ANIMATION_OFF_VALUE)) { + allAnimationsDisabled = false; + break; + } + } + mToggleDisableAnimationsPreference.setChecked(allAnimationsDisabled); + } + private void updateMasterMono() { final boolean masterMono = Settings.System.getIntForUser( getContentResolver(), Settings.System.MASTER_MONO, diff --git a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java index a4f6e5cf17d..02b1012b88c 100644 --- a/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragment.java @@ -67,12 +67,6 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment return R.xml.connected_devices_advanced; } - @Override - public String getCategoryKey() { - //TODO(b/69926683): remove this method and change DashboardFragmentRegistry directly for P - return CategoryKey.CATEGORY_DEVICE; - } - @Override protected List getPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 3b38df64a7f..b7c73f388c0 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -265,6 +265,7 @@ public class SettingsGateway { Settings.SoundSettingsActivity.class.getName(), Settings.StorageDashboardActivity.class.getName(), Settings.PowerUsageSummaryActivity.class.getName(), + Settings.PowerUsageSummaryLegacyActivity.class.getName(), Settings.UserAndAccountDashboardActivity.class.getName(), Settings.SecuritySettingsActivity.class.getName(), Settings.AccessibilitySettingsActivity.class.getName(), diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java index af00dc6088c..e841496d65e 100644 --- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java +++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java @@ -23,6 +23,7 @@ import com.android.settings.accounts.AccountDetailDashboardFragment; import com.android.settings.accounts.UserAndAccountDashboardFragment; import com.android.settings.applications.AppAndNotificationDashboardFragment; import com.android.settings.applications.DefaultAppSettings; +import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld; import com.android.settings.development.DevelopmentSettingsDashboardFragment; import com.android.settings.deviceinfo.StorageDashboardFragment; @@ -62,6 +63,8 @@ public class DashboardFragmentRegistry { //TODO(b/69471219): update ConnectedDeviceDashboardFragment once new feature is done. PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragmentOld.class.getName(), CategoryKey.CATEGORY_DEVICE); + PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(), + CategoryKey.CATEGORY_DEVICE); PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(), CategoryKey.CATEGORY_APPS); PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(), diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 8f114fc4758..e736798f85c 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -446,6 +446,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new ResizableActivityPreferenceController(context)); controllers.add(new FreeformWindowsPreferenceController(context)); controllers.add(new ShortcutManagerThrottlingPreferenceController(context)); + controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context)); return controllers; } diff --git a/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java b/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java new file mode 100644 index 00000000000..09770f6c8e7 --- /dev/null +++ b/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 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.development; + +import android.content.Context; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class EnableGnssRawMeasFullTrackingPreferenceController extends + DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener, + PreferenceControllerMixin { + + private static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING_KEY = + "enable_gnss_raw_meas_full_tracking"; + + static final int SETTING_VALUE_ON = 1; + static final int SETTING_VALUE_OFF = 0; + + private SwitchPreference mPreference; + + public EnableGnssRawMeasFullTrackingPreferenceController(Context context) { + super(context); + } + + @Override + public String getPreferenceKey() { + return ENABLE_GNSS_RAW_MEAS_FULL_TRACKING_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (Boolean) newValue; + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, + isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + + return true; + } + + @Override + public void updateState(Preference preference) { + final int enableGnssRawMeasFullTrackingMode = + Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF); + mPreference.setChecked(enableGnssRawMeasFullTrackingMode != SETTING_VALUE_OFF); + } + + @Override + protected void onDeveloperOptionsSwitchEnabled() { + mPreference.setEnabled(true); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF); + mPreference.setEnabled(false); + mPreference.setChecked(false); + } +} diff --git a/src/com/android/settings/location/LocationMode.java b/src/com/android/settings/location/LocationMode.java index 34f082b226a..5931f9e364a 100644 --- a/src/com/android/settings/location/LocationMode.java +++ b/src/com/android/settings/location/LocationMode.java @@ -94,6 +94,11 @@ public class LocationMode extends DashboardFragment { return Arrays.asList(sir); } + @Override + protected boolean isPageSearchEnabled(Context context) { + return context.getResources().getBoolean(R.bool.config_location_mode_available); + } + @Override public List getPreferenceControllers(Context context) { diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java index 31f1ec6d1e2..fc47b5c1901 100644 --- a/src/com/android/settings/security/SecuritySettings.java +++ b/src/com/android/settings/security/SecuritySettings.java @@ -73,6 +73,7 @@ import com.android.settings.security.trustagent.TrustAgentManager.TrustAgentComp import com.android.settings.widget.GearPreference; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.drawer.CategoryKey; import java.util.ArrayList; @@ -144,7 +145,7 @@ public class SecuritySettings extends SettingsPreferenceFragment private ManagedLockPasswordProvider mManagedPasswordProvider; private SwitchPreference mVisiblePatternProfile; - private SwitchPreference mUnifyProfile; + private RestrictedSwitchPreference mUnifyProfile; private SwitchPreference mShowPassword; @@ -319,7 +320,7 @@ public class SecuritySettings extends SettingsPreferenceFragment mVisiblePatternProfile = (SwitchPreference) root.findPreference(KEY_VISIBLE_PATTERN_PROFILE); - mUnifyProfile = (SwitchPreference) root.findPreference(KEY_UNIFICATION); + mUnifyProfile = (RestrictedSwitchPreference) root.findPreference(KEY_UNIFICATION); // Append the rest of the settings addPreferencesFromResource(R.xml.security_settings_misc); @@ -560,10 +561,17 @@ public class SecuritySettings extends SettingsPreferenceFragment mLocationcontroller.updateSummary(); } - private void updateUnificationPreference() { + @VisibleForTesting + void updateUnificationPreference() { if (mUnifyProfile != null) { - mUnifyProfile.setChecked(!mLockPatternUtils.isSeparateProfileChallengeEnabled( - mProfileChallengeUserId)); + final boolean separate = + mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileChallengeUserId); + mUnifyProfile.setChecked(!separate); + if (separate) { + mUnifyProfile.setDisabledByAdmin(RestrictedLockUtils.checkIfRestrictionEnforced( + getContext(), UserManager.DISALLOW_UNIFIED_PASSWORD, + mProfileChallengeUserId)); + } } } @@ -930,14 +938,11 @@ public class SecuritySettings extends SettingsPreferenceFragment .setPositiveButton( compliant ? R.string.lock_settings_profile_unification_dialog_confirm : R.string.lock_settings_profile_unification_dialog_uncompliant_confirm, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int whichButton) { - if (compliant) { - parentFragment.launchConfirmDeviceLockForUnification(); - } else { - parentFragment.unifyUncompliantLocks(); - } + (dialog, whichButton) -> { + if (compliant) { + parentFragment.launchConfirmDeviceLockForUnification(); + } else { + parentFragment.unifyUncompliantLocks(); } } ) diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java index 9092c0db069..9bb7958e233 100644 --- a/src/com/android/settings/tts/TextToSpeechSettings.java +++ b/src/com/android/settings/tts/TextToSpeechSettings.java @@ -770,7 +770,11 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment if (KEY_TTS_ENGINE_PREFERENCE.equals(p.getKey())) { EngineInfo info = mEnginesHelper.getEngineInfo(mCurrentEngine); final Intent settingsIntent = mEnginesHelper.getSettingsIntent(info.name); - startActivity(settingsIntent); + if (settingsIntent != null) { + startActivity(settingsIntent); + } else { + Log.e(TAG, "settingsIntent is null"); + } } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java index 27675701845..37ccb760b08 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java @@ -147,6 +147,11 @@ public class AdvancedConnectedDeviceDashboardFragmentTest { assertThat(keys).doesNotContain(mSmsMirroringPreferenceController.getPreferenceKey()); } + @Test + public void testGetCategoryKey_returnCategoryDevice() { + assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_DEVICE); + } + @Test public void testNonIndexableKeys_existInXmlLayout() { final Context context = RuntimeEnvironment.application; diff --git a/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java new file mode 100644 index 00000000000..95fd1116ef5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 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.development; + + +import static com.android.settings.development.EnableGnssRawMeasFullTrackingPreferenceController + .SETTING_VALUE_OFF; +import static com.android.settings.development.EnableGnssRawMeasFullTrackingPreferenceController + .SETTING_VALUE_ON; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.Settings; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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 EnableGnssRawMeasFullTrackingPreferenceControllerTest { + + @Mock + private SwitchPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + + private Context mContext; + private EnableGnssRawMeasFullTrackingPreferenceController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = new EnableGnssRawMeasFullTrackingPreferenceController(mContext); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void onPreferenceChange_settingEnabled_enableGnssRawMeasFullTrackingShouldBeOn() { + mController.onPreferenceChange(mPreference, true /* new value */); + + final int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */); + + assertThat(mode).isEqualTo(SETTING_VALUE_ON); + } + + @Test + public void onPreferenceChange_settingDisabled_enableGnssRawMeasFullTrackingShouldBeOff() { + mController.onPreferenceChange(mPreference, false /* new value */); + + final int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */); + + assertThat(mode).isEqualTo(SETTING_VALUE_OFF); + } + + @Test + public void updateState_settingDisabled_preferenceShouldNotBeChecked() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF); + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } + + @Test + public void updateState_settingEnabled_preferenceShouldBeChecked() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_ON); + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + } + + @Test + public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() { + mController.onDeveloperOptionsSwitchDisabled(); + + final int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */); + + assertThat(mode).isEqualTo(SETTING_VALUE_OFF); + verify(mPreference).setChecked(false); + verify(mPreference).setEnabled(false); + } + + @Test + public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() { + mController.onDeveloperOptionsSwitchEnabled(); + + verify(mPreference).setEnabled(true); + } +} diff --git a/tests/robotests/src/com/android/settings/location/LocationModeTest.java b/tests/robotests/src/com/android/settings/location/LocationModeTest.java new file mode 100644 index 00000000000..0e7a9d72ed0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/location/LocationModeTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 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.location; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.XmlTestUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class LocationModeTest { + + private Context mContext; + private LocationMode mFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mFragment = new LocationMode(); + } + + @Test + public void testSearchIndexProvider_shouldIndexResource() { + final List indexRes = + mFragment.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(mContext, + true /* enabled */); + + assertThat(indexRes).isNotNull(); + assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId()); + } + + @Test + @Config(qualifiers = "mcc999") + public void testSearchIndexProvider_ifPageDisabled_shouldNotIndexResource() { + final List niks = LocationMode.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(mContext); + final int xmlId = mFragment.getPreferenceScreenResId(); + + final List keys = XmlTestUtils.getKeysFromPreferenceXml(mContext, xmlId); + assertThat(niks).containsAllIn(keys); + } +} diff --git a/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java b/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java index 1ecdd0f5c7c..231cce95a82 100644 --- a/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java +++ b/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java @@ -17,9 +17,11 @@ package com.android.settings.security; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -29,7 +31,9 @@ import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManager.EnforcingUser; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; @@ -43,6 +47,9 @@ import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; +import com.android.settings.testutils.shadow.ShadowUserManager; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; import org.junit.Test; @@ -55,12 +62,15 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import org.robolectric.util.ReflectionHelpers; +import java.util.Arrays; +import java.util.Collections; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = { - ShadowLockPatternUtils.class + ShadowLockPatternUtils.class, + ShadowUserManager.class, }) public class SecuritySettingsTest { @@ -187,4 +197,49 @@ public class SecuritySettingsTest { assertThat(keys).containsAllIn(niks); } + + @Test + public void testUnifyLockRestriction() { + // Set up instance under test. + final Context context = spy(RuntimeEnvironment.application); + final SecuritySettings securitySettings = spy(new SecuritySettings()); + when(securitySettings.getContext()).thenReturn(context); + + final int userId = 123; + ReflectionHelpers.setField(securitySettings, "mProfileChallengeUserId", userId); + + final LockPatternUtils utils = mock(LockPatternUtils.class); + when(utils.isSeparateProfileChallengeEnabled(userId)).thenReturn(true); + ReflectionHelpers.setField(securitySettings, "mLockPatternUtils", utils); + + final RestrictedSwitchPreference unifyProfile = mock(RestrictedSwitchPreference.class); + ReflectionHelpers.setField(securitySettings, "mUnifyProfile", unifyProfile); + + // Pretend that no admins enforce the restriction. + ShadowUserManager.getShadow().setUserRestrictionSources( + UserManager.DISALLOW_UNIFIED_PASSWORD, + UserHandle.of(userId), + Collections.emptyList()); + + securitySettings.updateUnificationPreference(); + + verify(unifyProfile).setDisabledByAdmin(null); + + reset(unifyProfile); + + // Pretend that the restriction is enforced by several admins. Having just one would + // require more mocking of implementation details. + final EnforcingUser enforcer1 = new EnforcingUser( + userId, UserManager.RESTRICTION_SOURCE_PROFILE_OWNER); + final EnforcingUser enforcer2 = new EnforcingUser( + UserHandle.USER_SYSTEM, UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); + ShadowUserManager.getShadow().setUserRestrictionSources( + UserManager.DISALLOW_UNIFIED_PASSWORD, + UserHandle.of(userId), + Arrays.asList(enforcer1, enforcer2)); + + securitySettings.updateUnificationPreference(); + + verify(unifyProfile).setDisabledByAdmin(EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java index f24837dfa72..2df2b043ca3 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManager.EnforcingUser; import android.util.SparseArray; import org.robolectric.RuntimeEnvironment; @@ -31,14 +32,18 @@ import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Implements(UserManager.class) public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { private SparseArray mUserInfos = new SparseArray<>(); private boolean mAdminUser; - private List mRestrictions = new ArrayList<>(); + private final List mRestrictions = new ArrayList<>(); + private final Map> mRestrictionSources = new HashMap<>(); + public void setIsAdminUser(boolean isAdminUser) { mAdminUser = isAdminUser; @@ -91,4 +96,15 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager return (ShadowUserManager) Shadow.extract( RuntimeEnvironment.application.getSystemService(UserManager.class)); } + + @Implementation + public List getUserRestrictionSources( + String restrictionKey, UserHandle userHandle) { + return mRestrictionSources.get(restrictionKey + userHandle.getIdentifier()); + } + + public void setUserRestrictionSources( + String restrictionKey, UserHandle userHandle, List enforcers) { + mRestrictionSources.put(restrictionKey + userHandle.getIdentifier(), enforcers); + } } diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml index 65ed661eff8..b22c01bde6e 100644 --- a/tests/unit/AndroidManifest.xml +++ b/tests/unit/AndroidManifest.xml @@ -23,6 +23,7 @@ + diff --git a/tests/unit/src/com/android/settings/UserCredentialsTest.java b/tests/unit/src/com/android/settings/UserCredentialsTest.java index 41ef4dec2f5..8a72797e2cb 100644 --- a/tests/unit/src/com/android/settings/UserCredentialsTest.java +++ b/tests/unit/src/com/android/settings/UserCredentialsTest.java @@ -40,7 +40,7 @@ public class UserCredentialsTest extends InstrumentationTestCase { Credential c = new Credential(alias, Process.SYSTEM_UID); c.storedTypes.add(Credential.Type.CA_CERTIFICATE); - c.storedTypes.add(Credential.Type.USER_SECRET_KEY); + c.storedTypes.add(Credential.Type.USER_KEY); Parcel p = Parcel.obtain(); c.writeToParcel(p, /* flags */ 0); diff --git a/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java b/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java new file mode 100644 index 00000000000..d89d4a3b059 --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2017 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; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.net.Uri; +import android.os.UserHandle; +import android.os.UserManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; +import android.support.v7.widget.RecyclerView; +import android.widget.Switch; +import android.widget.TextView; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.MODE_ERRORED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * An abstract parent for testing settings activities that manage an AppOps permission. + */ +abstract public class AppOpsSettingsTest { + private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard"; + private static final long START_ACTIVITY_TIMEOUT = 5000; + + private Context mContext; + private UiDevice mUiDevice; + private PackageManager mPackageManager; + private AppOpsManager mAppOpsManager; + private List mProfiles; + private String mPackageName; + + // These depend on which app op's settings UI is being tested. + private final String mActivityAction; + private final int mAppOpCode; + + protected AppOpsSettingsTest(String activityAction, int appOpCode) { + mActivityAction = activityAction; + mAppOpCode = appOpCode; + } + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + mPackageName = InstrumentationRegistry.getContext().getPackageName(); + mPackageManager = mContext.getPackageManager(); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mProfiles = mContext.getSystemService(UserManager.class).getProfiles(UserHandle.myUserId()); + resetAppOpModeForAllProfiles(); + mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + mUiDevice.wakeUp(); + mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND); + } + + private void resetAppOpModeForAllProfiles() throws Exception { + for (UserInfo user : mProfiles) { + final int uid = mPackageManager.getPackageUidAsUser(mPackageName, user.id); + mAppOpsManager.setMode(mAppOpCode, uid, mPackageName, MODE_DEFAULT); + } + } + + /** + * Creates an intent for showing the permission settings for all apps. + */ + private Intent createManageAllAppsIntent() { + return new Intent(mActivityAction); + } + + /** + * Creates an intent for showing the permission setting for a single app. + */ + private Intent createManageSingleAppIntent(String packageName) { + final Intent intent = createManageAllAppsIntent(); + intent.setData(Uri.parse("package:" + packageName)); + return intent; + } + + private String getApplicationLabel(String packageName) throws Exception { + final ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); + return mPackageManager.getApplicationLabel(info).toString(); + } + + private UiObject2 findAndVerifySwitchState(boolean checked) { + final BySelector switchSelector = By.clazz(Switch.class).res("android:id/switch_widget"); + final UiObject2 switchPref = mUiDevice.wait(Until.findObject(switchSelector), + START_ACTIVITY_TIMEOUT); + assertNotNull("Switch not shown", switchPref); + assertTrue("Switch in invalid state", switchPref.isChecked() == checked); + return switchPref; + } + + @Test + public void testAppList() throws Exception { + final String testAppLabel = getApplicationLabel(mPackageName); + + mContext.startActivity(createManageAllAppsIntent()); + final BySelector preferenceListSelector = + By.clazz(RecyclerView.class).res("com.android.settings:id/apps_list"); + final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector), + START_ACTIVITY_TIMEOUT); + assertNotNull("App list not shown", preferenceList); + + final BySelector appLabelTextViewSelector = By.clazz(TextView.class) + .res("android:id/title") + .text(testAppLabel); + List listOfMatchingTextViews; + do { + listOfMatchingTextViews = preferenceList.findObjects(appLabelTextViewSelector); + // assuming the number of profiles will be sufficiently small so that all the entries + // for the same package will fit in one screen at some time during the scroll. + } while (listOfMatchingTextViews.size() != mProfiles.size() && + preferenceList.scroll(Direction.DOWN, 0.2f)); + assertEquals("Test app not listed for each profile", mProfiles.size(), + listOfMatchingTextViews.size()); + + for (UiObject2 matchingObject : listOfMatchingTextViews) { + matchingObject.click(); + findAndVerifySwitchState(true); + mUiDevice.pressBack(); + } + } + + private void testAppDetailScreenForAppOp(int appOpMode, int userId) throws Exception { + final String testAppLabel = getApplicationLabel(mPackageName); + final BySelector appDetailTitleSelector = By.clazz(TextView.class) + .res("com.android.settings:id/app_detail_title") + .text(testAppLabel); + + mAppOpsManager.setMode(mAppOpCode, + mPackageManager.getPackageUidAsUser(mPackageName, userId), mPackageName, appOpMode); + mContext.startActivityAsUser(createManageSingleAppIntent(mPackageName), + UserHandle.of(userId)); + mUiDevice.wait(Until.findObject(appDetailTitleSelector), START_ACTIVITY_TIMEOUT); + findAndVerifySwitchState(appOpMode == MODE_ALLOWED || appOpMode == MODE_DEFAULT); + mUiDevice.pressBack(); + } + + @Test + public void testSingleApp() throws Exception { + // App op MODE_DEFAULT is already tested in #testAppList + for (UserInfo user : mProfiles) { + testAppDetailScreenForAppOp(MODE_ALLOWED, user.id); + testAppDetailScreenForAppOp(MODE_ERRORED, user.id); + } + } + + private void testSwitchToggle(int fromAppOp, int toAppOp) throws Exception { + final int packageUid = mPackageManager.getPackageUid(mPackageName, 0); + final boolean initialState = (fromAppOp == MODE_ALLOWED || fromAppOp == MODE_DEFAULT); + + mAppOpsManager.setMode(mAppOpCode, packageUid, mPackageName, fromAppOp); + mContext.startActivity(createManageSingleAppIntent(mPackageName)); + final UiObject2 switchPref = findAndVerifySwitchState(initialState); + switchPref.click(); + Thread.sleep(1000); + assertEquals("Toggling switch did not change app op", toAppOp, + mAppOpsManager.checkOpNoThrow(mAppOpCode, packageUid, + mPackageName)); + mUiDevice.pressBack(); + } + + @Test + public void testIfSwitchTogglesAppOp() throws Exception { + testSwitchToggle(MODE_ALLOWED, MODE_ERRORED); + testSwitchToggle(MODE_ERRORED, MODE_ALLOWED); + } + + @After + public void tearDown() throws Exception { + mUiDevice.pressHome(); + resetAppOpModeForAllProfiles(); + } +} diff --git a/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java b/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java new file mode 100644 index 00000000000..24760ae7c2e --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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; + +import android.app.AppOpsManager; +import android.provider.Settings; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class DrawOverlaySettingsTest extends AppOpsSettingsTest { + + public DrawOverlaySettingsTest() { + super(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, AppOpsManager.OP_SYSTEM_ALERT_WINDOW); + } + + // Test cases are in the superclass. +} \ No newline at end of file diff --git a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java index 22d4bf6e04e..6ac21afcb8c 100644 --- a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java +++ b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java @@ -16,186 +16,21 @@ package com.android.settings.applications; -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_DEFAULT; -import static android.app.AppOpsManager.MODE_ERRORED; -import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import android.app.AppOpsManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.net.Uri; -import android.os.UserHandle; -import android.os.UserManager; import android.provider.Settings; -import android.support.test.InstrumentationRegistry; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.BySelector; -import android.support.test.uiautomator.Direction; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject2; -import android.support.test.uiautomator.Until; -import android.support.v7.widget.RecyclerView; -import android.widget.Switch; -import android.widget.TextView; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; import org.junit.runner.RunWith; -import java.util.List; - @RunWith(AndroidJUnit4.class) @LargeTest -public class ExternalSourcesSettingsTest { +public class ExternalSourcesSettingsTest extends AppOpsSettingsTest { - private static final String TAG = ExternalSourcesSettingsTest.class.getSimpleName(); - private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard"; - private static final long START_ACTIVITY_TIMEOUT = 5000; - - private Context mContext; - private UiDevice mUiDevice; - private PackageManager mPackageManager; - private AppOpsManager mAppOpsManager; - private List mProfiles; - private String mPackageName; - - @Before - public void setUp() throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); - mPackageName = InstrumentationRegistry.getContext().getPackageName(); - mPackageManager = mContext.getPackageManager(); - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); - mProfiles = mContext.getSystemService(UserManager.class).getProfiles(UserHandle.myUserId()); - resetAppOpModeForAllProfiles(); - mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - mUiDevice.wakeUp(); - mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND); + public ExternalSourcesSettingsTest() { + super(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, + AppOpsManager.OP_REQUEST_INSTALL_PACKAGES); } - private void resetAppOpModeForAllProfiles() throws Exception { - for (UserInfo user : mProfiles) { - final int uid = mPackageManager.getPackageUidAsUser(mPackageName, user.id); - mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, uid, mPackageName, MODE_DEFAULT); - } - } - - private Intent createManageExternalSourcesListIntent() { - final Intent manageExternalSourcesIntent = new Intent(); - manageExternalSourcesIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); - return manageExternalSourcesIntent; - } - - private Intent createManageExternalSourcesAppIntent(String packageName) { - final Intent intent = createManageExternalSourcesListIntent(); - intent.setData(Uri.parse("package:" + packageName)); - return intent; - } - - private String getApplicationLabel(String packageName) throws Exception { - final ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); - return mPackageManager.getApplicationLabel(info).toString(); - } - - private UiObject2 findAndVerifySwitchState(boolean checked) { - final BySelector switchSelector = By.clazz(Switch.class).res("android:id/switch_widget"); - final UiObject2 switchPref = mUiDevice.wait(Until.findObject(switchSelector), - START_ACTIVITY_TIMEOUT); - assertNotNull("Switch not shown", switchPref); - assertTrue("Switch in invalid state", switchPref.isChecked() == checked); - return switchPref; - } - - @Test - public void testManageExternalSourcesList() throws Exception { - final String testAppLabel = getApplicationLabel(mPackageName); - - mContext.startActivity(createManageExternalSourcesListIntent()); - final BySelector preferenceListSelector = - By.clazz(RecyclerView.class).res("com.android.settings:id/apps_list"); - final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector), - START_ACTIVITY_TIMEOUT); - assertNotNull("App list not shown", preferenceList); - - final BySelector appLabelTextViewSelector = By.clazz(TextView.class) - .res("android:id/title") - .text(testAppLabel); - List listOfMatchingTextViews; - do { - listOfMatchingTextViews = preferenceList.findObjects(appLabelTextViewSelector); - // assuming the number of profiles will be sufficiently small so that all the entries - // for the same package will fit in one screen at some time during the scroll. - } while (listOfMatchingTextViews.size() != mProfiles.size() && - preferenceList.scroll(Direction.DOWN, 0.2f)); - assertEquals("Test app not listed for each profile", mProfiles.size(), - listOfMatchingTextViews.size()); - - for (UiObject2 matchingObject : listOfMatchingTextViews) { - matchingObject.click(); - findAndVerifySwitchState(true); - mUiDevice.pressBack(); - } - } - - private void testAppDetailScreenForAppOp(int appOpMode, int userId) throws Exception { - final String testAppLabel = getApplicationLabel(mPackageName); - final BySelector appDetailTitleSelector = By.clazz(TextView.class) - .res("com.android.settings:id/app_detail_title") - .text(testAppLabel); - - mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, - mPackageManager.getPackageUidAsUser(mPackageName, userId), mPackageName, appOpMode); - mContext.startActivityAsUser(createManageExternalSourcesAppIntent(mPackageName), - UserHandle.of(userId)); - mUiDevice.wait(Until.findObject(appDetailTitleSelector), START_ACTIVITY_TIMEOUT); - findAndVerifySwitchState(appOpMode == MODE_ALLOWED || appOpMode == MODE_DEFAULT); - mUiDevice.pressBack(); - } - - @Test - public void testManageExternalSourcesForApp() throws Exception { - // App op MODE_DEFAULT is already tested in #testManageExternalSourcesList - for (UserInfo user : mProfiles) { - testAppDetailScreenForAppOp(MODE_ALLOWED, user.id); - testAppDetailScreenForAppOp(MODE_ERRORED, user.id); - } - } - - private void testSwitchToggle(int fromAppOp, int toAppOp) throws Exception { - final int packageUid = mPackageManager.getPackageUid(mPackageName, 0); - final boolean initialState = (fromAppOp == MODE_ALLOWED || fromAppOp == MODE_DEFAULT); - - mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, packageUid, mPackageName, fromAppOp); - mContext.startActivity(createManageExternalSourcesAppIntent(mPackageName)); - final UiObject2 switchPref = findAndVerifySwitchState(initialState); - switchPref.click(); - Thread.sleep(1000); - assertEquals("Toggling switch did not change app op", toAppOp, - mAppOpsManager.checkOpNoThrow(OP_REQUEST_INSTALL_PACKAGES, packageUid, - mPackageName)); - mUiDevice.pressBack(); - } - - @Test - public void testIfSwitchTogglesAppOp() throws Exception { - testSwitchToggle(MODE_ALLOWED, MODE_ERRORED); - testSwitchToggle(MODE_ERRORED, MODE_ALLOWED); - } - - @After - public void tearDown() throws Exception { - mUiDevice.pressHome(); - resetAppOpModeForAllProfiles(); - } + // Test cases are in the superclass. }