diff --git a/res/xml/security_settings_v2.xml b/res/xml/security_settings_v2.xml index 155ee70d717..30e829d4e17 100644 --- a/res/xml/security_settings_v2.xml +++ b/res/xml/security_settings_v2.xml @@ -28,6 +28,22 @@ android:title="@string/security_status_title" /> + + + + + + + + agents = - mTrustAgentManager.getActiveTrustAgents(getActivity(), mLockPatternUtils); - for (TrustAgentManager.TrustAgentComponentInfo agent : agents) { - final RestrictedPreference trustAgentPreference = - new RestrictedPreference(securityCategory.getContext()); - trustAgentPreference.setKey(KEY_TRUST_AGENT); - trustAgentPreference.setTitle(agent.title); - trustAgentPreference.setSummary(agent.summary); - // Create intent for this preference. - Intent intent = new Intent(); - intent.setComponent(agent.componentName); - intent.setAction(Intent.ACTION_MAIN); - trustAgentPreference.setIntent(intent); - // Add preference to the settings menu. - securityCategory.addPreference(trustAgentPreference); - - trustAgentPreference.setDisabledByAdmin(agent.admin); - if (!trustAgentPreference.isDisabledByAdmin() && !hasSecurity) { - trustAgentPreference.setEnabled(false); - trustAgentPreference.setSummary(R.string.disabled_because_no_backup_security); - } - } - return agents.size(); - } - @Override public void onGearClick(GearPreference p) { if (KEY_UNLOCK_SET_OR_CHANGE.equals(p.getKey())) { @@ -414,14 +368,6 @@ public class SecuritySettingsV2 extends DashboardFragment } } - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (mTrustAgentClickIntent != null) { - outState.putParcelable(TRUST_AGENT_CLICK_INTENT, mTrustAgentClickIntent); - } - } - @Override public void onResume() { super.onResume(); @@ -445,7 +391,7 @@ public class SecuritySettingsV2 extends DashboardFragment final Preference encryptionStatusPref = getPreferenceScreen().findPreference( mEncryptionStatusPreferenceController.getPreferenceKey()); mEncryptionStatusPreferenceController.updateState(encryptionStatusPref); - + mTrustAgentListPreferenceController.onResume(); mLocationController.updateSummary(); } @@ -465,6 +411,9 @@ public class SecuritySettingsV2 extends DashboardFragment @Override public boolean onPreferenceTreeClick(Preference preference) { + if (mTrustAgentListPreferenceController.handlePreferenceTreeClick(preference)) { + return true; + } final String key = preference.getKey(); if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) { // TODO(b/35930129): Remove once existing password can be passed into vold directly. @@ -491,17 +440,6 @@ public class SecuritySettingsV2 extends DashboardFragment startFragment(this, ChooseLockGeneric.ChooseLockGenericFragment.class.getName(), R.string.lock_settings_picker_title_profile, SET_OR_CHANGE_LOCK_METHOD_REQUEST_PROFILE, extras); - } else if (KEY_TRUST_AGENT.equals(key)) { - ChooseLockSettingsHelper helper = - new ChooseLockSettingsHelper(this.getActivity(), this); - mTrustAgentClickIntent = preference.getIntent(); - boolean confirmationLaunched = helper.launchConfirmationActivity( - CHANGE_TRUST_AGENT_SETTINGS, preference.getTitle()); - if (!confirmationLaunched && mTrustAgentClickIntent != null) { - // If this returns false, it means no password confirmation is required. - startActivity(mTrustAgentClickIntent); - mTrustAgentClickIntent = null; - } } else { // If we didn't handle it, let preferences handle it. return super.onPreferenceTreeClick(preference); @@ -516,10 +454,7 @@ public class SecuritySettingsV2 extends DashboardFragment public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CHANGE_TRUST_AGENT_SETTINGS && resultCode == Activity.RESULT_OK) { - if (mTrustAgentClickIntent != null) { - startActivity(mTrustAgentClickIntent); - mTrustAgentClickIntent = null; - } + mTrustAgentListPreferenceController.handleActivityResult(resultCode); return; } else if (requestCode == UNIFY_LOCK_CONFIRM_DEVICE_REQUEST && resultCode == Activity.RESULT_OK) { @@ -760,15 +695,9 @@ public class SecuritySettingsV2 extends DashboardFragment @Override public List getNonIndexableKeys(Context context) { final List keys = super.getNonIndexableKeys(context); - final LockPatternUtils lockPatternUtils = new LockPatternUtils(context); new SimLockPreferenceController(context).updateNonIndexableKeys(keys); - // TrustAgent settings disappear when the user has no primary security. - if (!lockPatternUtils.isSecure(MY_USER_ID)) { - keys.add(KEY_TRUST_AGENT); - } - if (!(new EnterprisePrivacyPreferenceController(context)) .isAvailable()) { keys.add(KEY_ENTERPRISE_PRIVACY); diff --git a/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java b/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java new file mode 100644 index 00000000000..d99757be256 --- /dev/null +++ b/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java @@ -0,0 +1,172 @@ +/* + * 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.security.trustagent; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceCategory; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settings.security.SecurityFeatureProvider; +import com.android.settings.security.SecuritySettingsV2; +import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnCreate; +import com.android.settingslib.core.lifecycle.events.OnResume; +import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; + +import java.util.List; + +public class TrustAgentListPreferenceController extends AbstractPreferenceController + implements LifecycleObserver, OnSaveInstanceState, OnCreate, OnResume { + + private static final String TRUST_AGENT_CLICK_INTENT = "trust_agent_click_intent"; + @VisibleForTesting + static final String PREF_KEY_TRUST_AGENT = "trust_agent"; + @VisibleForTesting + static final String PREF_KEY_SECURITY_CATEGORY = "security_category"; + private static final int MY_USER_ID = UserHandle.myUserId(); + + private final LockPatternUtils mLockPatternUtils; + private final TrustAgentManager mTrustAgentManager; + private final Activity mActivity; + private final SecuritySettingsV2 mHost; + + private Intent mTrustAgentClickIntent; + private PreferenceCategory mSecurityCategory; + + public TrustAgentListPreferenceController(Activity activity, SecuritySettingsV2 host, + Lifecycle lifecycle) { + super(activity); + final SecurityFeatureProvider provider = FeatureFactory.getFactory(activity) + .getSecurityFeatureProvider(); + mActivity = activity; + mHost = host; + mLockPatternUtils = provider.getLockPatternUtils(activity); + mTrustAgentManager = provider.getTrustAgentManager(); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return PREF_KEY_TRUST_AGENT; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mSecurityCategory = (PreferenceCategory) screen.findPreference(PREF_KEY_SECURITY_CATEGORY); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + if (savedInstanceState != null + && savedInstanceState.containsKey(TRUST_AGENT_CLICK_INTENT)) { + mTrustAgentClickIntent = savedInstanceState.getParcelable(TRUST_AGENT_CLICK_INTENT); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + if (mTrustAgentClickIntent != null) { + outState.putParcelable(TRUST_AGENT_CLICK_INTENT, mTrustAgentClickIntent); + } + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + return super.handlePreferenceTreeClick(preference); + } + final ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(mActivity, mHost); + mTrustAgentClickIntent = preference.getIntent(); + boolean confirmationLaunched = helper.launchConfirmationActivity( + SecuritySettingsV2.CHANGE_TRUST_AGENT_SETTINGS, preference.getTitle()); + + if (!confirmationLaunched && mTrustAgentClickIntent != null) { + // If this returns false, it means no password confirmation is required. + mHost.startActivity(mTrustAgentClickIntent); + mTrustAgentClickIntent = null; + } + return true; + } + + @Override + public void onResume() { + if (mSecurityCategory == null) { + return; + } + // First remove all old trust agents. + while (true) { + final Preference oldAgent = mSecurityCategory.findPreference(PREF_KEY_TRUST_AGENT); + if (oldAgent == null) { + break; + } else { + mSecurityCategory.removePreference(oldAgent); + } + } + // Then add new ones. + final boolean hasSecurity = mLockPatternUtils.isSecure(MY_USER_ID); + final List agents = + mTrustAgentManager.getActiveTrustAgents(mContext, mLockPatternUtils); + if (agents == null) { + return; + } + for (TrustAgentManager.TrustAgentComponentInfo agent : agents) { + final RestrictedPreference trustAgentPreference = + new RestrictedPreference(mSecurityCategory.getContext()); + trustAgentPreference.setKey(PREF_KEY_TRUST_AGENT); + trustAgentPreference.setTitle(agent.title); + trustAgentPreference.setSummary(agent.summary); + // Create intent for this preference. + trustAgentPreference.setIntent(new Intent(Intent.ACTION_MAIN) + .setComponent(agent.componentName)); + trustAgentPreference.setDisabledByAdmin(agent.admin); + if (!trustAgentPreference.isDisabledByAdmin() && !hasSecurity) { + trustAgentPreference.setEnabled(false); + trustAgentPreference.setSummary(R.string.disabled_because_no_backup_security); + } + // Add preference to the settings menu. + mSecurityCategory.addPreference(trustAgentPreference); + } + } + + public void handleActivityResult(int resultCode) { + if (resultCode == Activity.RESULT_OK && mTrustAgentClickIntent != null) { + mHost.startActivity(mTrustAgentClickIntent); + mTrustAgentClickIntent = null; + } + } +} diff --git a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java new file mode 100644 index 00000000000..a97780b48e8 --- /dev/null +++ b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java @@ -0,0 +1,132 @@ +/* + * 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.security.trustagent; + + +import static com.android.settings.security.trustagent.TrustAgentListPreferenceController + .PREF_KEY_SECURITY_CATEGORY; +import static com.android.settings.security.trustagent.TrustAgentListPreferenceController + .PREF_KEY_TRUST_AGENT; +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.verify; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceCategory; +import android.support.v7.preference.PreferenceScreen; + +import com.android.internal.widget.LockPatternUtils; +import com.android.settings.TestConfig; +import com.android.settings.security.SecuritySettingsV2; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class TrustAgentListPreferenceControllerTest { + + @Mock + private TrustAgentManager mTrustAgentManager; + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private PreferenceScreen mScreen; + @Mock + private PreferenceCategory mCategory; + @Mock + private SecuritySettingsV2 mFragment; + + private Lifecycle mLifecycle; + private FakeFeatureFactory mFeatureFactory; + private Activity mActivity; + + private TrustAgentListPreferenceController mController; + + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mActivity = Robolectric.buildActivity(Activity.class).get(); + mLifecycle = new Lifecycle(() -> mLifecycle); + mFeatureFactory = FakeFeatureFactory.setupForTest(); + when(mFeatureFactory.securityFeatureProvider.getLockPatternUtils(any(Context.class))) + .thenReturn(mLockPatternUtils); + when(mFeatureFactory.securityFeatureProvider.getTrustAgentManager()) + .thenReturn(mTrustAgentManager); + when(mCategory.getKey()).thenReturn(PREF_KEY_SECURITY_CATEGORY); + when(mCategory.getContext()).thenReturn(mActivity); + when(mScreen.findPreference(PREF_KEY_SECURITY_CATEGORY)) + .thenReturn(mCategory); + mController = new TrustAgentListPreferenceController(mActivity, mFragment, mLifecycle); + } + + @Test + public void testConstants() { + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.getPreferenceKey()).isEqualTo(PREF_KEY_TRUST_AGENT); + } + + @Test + public void onResume_shouldClearOldAgents() { + final Preference oldAgent = new Preference(mActivity); + oldAgent.setKey(PREF_KEY_TRUST_AGENT); + when(mCategory.findPreference(PREF_KEY_TRUST_AGENT)) + .thenReturn(oldAgent) + .thenReturn(null); + + mController.displayPreference(mScreen); + mController.onResume(); + + verify(mCategory).removePreference(oldAgent); + } + + @Test + public void onResume_shouldAddNewAgents() { + final List agents = new ArrayList<>(); + final TrustAgentManager.TrustAgentComponentInfo agent = mock( + TrustAgentManager.TrustAgentComponentInfo.class); + agent.title = "Test_title"; + agent.summary = "test summary"; + agent.componentName = new ComponentName("pkg", "agent"); + agent.admin = null; + agents.add(agent); + when(mTrustAgentManager.getActiveTrustAgents(mActivity, mLockPatternUtils)) + .thenReturn(agents); + + mController.displayPreference(mScreen); + mController.onResume(); + + verify(mCategory).addPreference(any(Preference.class)); + } +}