diff --git a/res/drawable/ic_undo_24.xml b/res/drawable/ic_undo_24.xml
new file mode 100644
index 00000000000..0a8e1495287
--- /dev/null
+++ b/res/drawable/ic_undo_24.xml
@@ -0,0 +1,25 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/app_authentication_item.xml b/res/layout/app_authentication_item.xml
index 2df923f75f0..423722e5f70 100644
--- a/res/layout/app_authentication_item.xml
+++ b/res/layout/app_authentication_item.xml
@@ -30,6 +30,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/app_icon"
+ android:layout_toLeftOf="@id/expand"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical">
@@ -40,6 +41,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
+
+
+
+
diff --git a/res/layout/credential_management_app_policy.xml b/res/layout/credential_management_app_policy.xml
new file mode 100644
index 00000000000..15153e94195
--- /dev/null
+++ b/res/layout/credential_management_app_policy.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4ce45c04bf5..764cc293ebd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6335,6 +6335,14 @@
Allow
Show more
+
+ Certificate management app
+
+ None
+
+ Certificates installed by this app identify you to the apps and URLs below
+
+ Remove
Emergency dialing signal
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c447decd89c..e27095d962c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -816,13 +816,13 @@
+
+
+
+
diff --git a/res/xml/credential_management_app_fragment.xml b/res/xml/credential_management_app_fragment.xml
new file mode 100644
index 00000000000..93924145004
--- /dev/null
+++ b/res/xml/credential_management_app_fragment.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/encryption_and_credential.xml b/res/xml/encryption_and_credential.xml
index fe0498d9045..f107b58b107 100644
--- a/res/xml/encryption_and_credential.xml
+++ b/res/xml/encryption_and_credential.xml
@@ -69,6 +69,13 @@
+
+
diff --git a/src/com/android/settings/security/CredentialManagementAppAdapter.java b/src/com/android/settings/security/CredentialManagementAppAdapter.java
index 707f598445b..e56fc63c443 100644
--- a/src/com/android/settings/security/CredentialManagementAppAdapter.java
+++ b/src/com/android/settings/security/CredentialManagementAppAdapter.java
@@ -59,6 +59,9 @@ public class CredentialManagementAppAdapter extends RecyclerView.Adapter mExpandedApps;
public AppAuthenticationViewHolder(View view) {
super(view);
mAppIconView = view.findViewById(R.id.app_icon);
mAppNameView = view.findViewById(R.id.app_name);
+ mNumberOfUrisView = view.findViewById(R.id.number_of_uris);
+ mExpanderIconView = view.findViewById(R.id.expand);
mChildRecyclerView = view.findViewById(R.id.uris);
+ mExpandedApps = new ArrayList<>();
+
+ mExpanderIconView.setOnClickListener(view1 -> {
+ final String appName = mSortedAppNames.get(getBindingAdapterPosition());
+ if (mExpandedApps.contains(appName)) {
+ mExpandedApps.remove(appName);
+ } else {
+ mExpandedApps.add(appName);
+ }
+ bindPolicyView(appName);
+ });
}
/**
@@ -119,32 +138,63 @@ public class CredentialManagementAppAdapter extends RecyclerView.Adapter urisToAliases) {
+ private void bindChildView(Map urisToAliases) {
LinearLayoutManager layoutManager = new LinearLayoutManager(
mChildRecyclerView.getContext(), RecyclerView.VERTICAL, false);
layoutManager.setInitialPrefetchItemCount(urisToAliases.size());
UriAuthenticationPolicyAdapter childItemAdapter =
new UriAuthenticationPolicyAdapter(new ArrayList<>(urisToAliases.keySet()));
mChildRecyclerView.setLayoutManager(layoutManager);
+ mChildRecyclerView.setVisibility(View.VISIBLE);
mChildRecyclerView.setAdapter(childItemAdapter);
mChildRecyclerView.setRecycledViewPool(mViewPool);
}
+
+ private String getNumberOfUrlsText(Map urisToAliases) {
+ String url = urisToAliases.size() > 1 ? " URLs" : " URL";
+ return urisToAliases.size() + url;
+ }
}
public CredentialManagementAppAdapter(Context context, String credentialManagerPackage,
- Map> appUriAuthentication) {
+ Map> appUriAuthentication,
+ boolean includeHeader, boolean includeExpander) {
mContext = context;
mCredentialManagerPackage = credentialManagerPackage;
mPackageManager = context.getPackageManager();
mAppUriAuthentication = appUriAuthentication;
mSortedAppNames = sortPackageNames(mAppUriAuthentication);
mViewPool = new RecyclerView.RecycledViewPool();
+ mIncludeHeader = includeHeader;
+ mIncludeExpander = includeExpander;
}
/**
@@ -198,19 +248,20 @@ public class CredentialManagementAppAdapter extends RecyclerView.Adapter {
+ try {
+ IKeyChainService service = KeyChain.bind(mContext).getService();
+ mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+ mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+ } catch (InterruptedException | RemoteException e) {
+ Log.e(TAG, "Unable to display credential management app buttons");
+ }
+ mHandler.post(() -> displayButtons(screen));
+ });
+ }
+
+ private void displayButtons(PreferenceScreen screen) {
+ if (mHasCredentialManagerPackage) {
+ ((ActionButtonsPreference) screen.findPreference(getPreferenceKey()))
+ .setButton1Text(R.string.remove_credential_management_app)
+ .setButton1Icon(R.drawable.ic_undo_24)
+ .setButton1OnClickListener(view -> removeCredentialManagementApp());
+ }
+ }
+
+ private void removeCredentialManagementApp() {
+ try {
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
+ mCredentialManagerPackageName, 0);
+ mAppOpsManager.setMode(AppOpsManager.OP_MANAGE_CREDENTIALS,
+ appInfo.uid, mCredentialManagerPackageName, AppOpsManager.MODE_DEFAULT);
+ mExecutor.execute(() -> {
+ try {
+ IKeyChainService service = KeyChain.bind(mContext).getService();
+ service.removeCredentialManagementApp();
+ } catch (InterruptedException | RemoteException e) {
+ Log.e(TAG, "Unable to remove the credential management app");
+ }
+ });
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to remove the credential management app");
+ }
+ }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppFragment.java b/src/com/android/settings/security/CredentialManagementAppFragment.java
new file mode 100644
index 00000000000..5544ee6cc7e
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppFragment.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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;
+
+import android.app.settings.SettingsEnums;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Settings fragment containing the credential management app. The credential management app has
+ * the ability to manage the user's credentials on unmanaged devices.
+ */
+@SearchIndexable
+public class CredentialManagementAppFragment extends DashboardFragment {
+
+ private static final String TAG = "CredentialManagementApp";
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.credential_management_app_fragment;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.CREDENTIAL_MANAGEMENT_APP;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.credential_management_app_fragment);
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppHeaderController.java b/src/com/android/settings/security/CredentialManagementAppHeaderController.java
new file mode 100644
index 00000000000..975c49d0a04
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppHeaderController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.LayoutPreference;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Controller that shows the header of the credential management app, which includes credential
+ * management app's name, icon and a description.
+ */
+public class CredentialManagementAppHeaderController extends BasePreferenceController {
+
+ private static final String TAG = "CredentialManagementApp";
+
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ public CredentialManagementAppHeaderController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mPackageManager = context.getPackageManager();
+ }
+
+ private final PackageManager mPackageManager;
+ private boolean mHasCredentialManagerPackage;
+ private String mCredentialManagerPackageName;
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mExecutor.execute(() -> {
+ try {
+ IKeyChainService service = KeyChain.bind(mContext).getService();
+ mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+ mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+ } catch (InterruptedException | RemoteException e) {
+ Log.e(TAG, "Unable to display credential management app header");
+ }
+ mHandler.post(() -> displayHeader(screen));
+ });
+ }
+
+ private void displayHeader(PreferenceScreen screen) {
+ LayoutPreference headerPref = screen.findPreference(getPreferenceKey());
+ ImageView mAppIconView = headerPref.findViewById(R.id.entity_header_icon);
+ TextView mTitleView = headerPref.findViewById(R.id.entity_header_title);
+ TextView mDescriptionView = headerPref.findViewById(R.id.entity_header_summary);
+
+ try {
+ ApplicationInfo applicationInfo =
+ mPackageManager.getApplicationInfo(mCredentialManagerPackageName, 0);
+ mAppIconView.setImageDrawable(mPackageManager.getApplicationIcon(applicationInfo));
+ mTitleView.setText(applicationInfo.loadLabel(mPackageManager));
+ } catch (PackageManager.NameNotFoundException e) {
+ mAppIconView.setImageDrawable(null);
+ mTitleView.setText(mCredentialManagerPackageName);
+ }
+ // TODO (b/165641221): The description should be multi-lined, which is currently a
+ // limitation of using Settings entity header. However, the Settings entity header
+ // should be used to be consistent with the rest of Settings.
+ mDescriptionView.setText(
+ mContext.getString(R.string.request_manage_credentials_description));
+ }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppPolicyController.java b/src/com/android/settings/security/CredentialManagementAppPolicyController.java
new file mode 100644
index 00000000000..9561c5f13f1
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppPolicyController.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
+
+import android.content.Context;
+
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Controller that shows the credential management app's authentication policy.
+ */
+public class CredentialManagementAppPolicyController extends BasePreferenceController {
+
+ public CredentialManagementAppPolicyController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ PreferenceGroup group = screen.findPreference(getPreferenceKey());
+ group.addPreference(new CredentialManagementAppPolicyPreference(group.getContext()));
+ }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppPolicyPreference.java b/src/com/android/settings/security/CredentialManagementAppPolicyPreference.java
new file mode 100644
index 00000000000..1747be30530
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppPolicyPreference.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.AppUriAuthenticationPolicy;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Preference that shows the credential management app's authentication policy.
+ */
+public class CredentialManagementAppPolicyPreference extends Preference {
+
+ private static final String TAG = "CredentialManagementApp";
+
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ private final Context mContext;
+
+ private boolean mHasCredentialManagerPackage;
+ private String mCredentialManagerPackageName;
+ private AppUriAuthenticationPolicy mCredentialManagerPolicy;
+
+ public CredentialManagementAppPolicyPreference(Context context) {
+ super(context);
+ setLayoutResource(R.layout.credential_management_app_policy);
+ mContext = context;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+
+ mExecutor.execute(() -> {
+ try {
+ IKeyChainService service = KeyChain.bind(mContext).getService();
+ mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+ mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+ mCredentialManagerPolicy = service.getCredentialManagementAppPolicy();
+ } catch (InterruptedException | RemoteException e) {
+ Log.e(TAG, "Unable to display credential management app policy");
+ }
+ mHandler.post(() -> displayPolicy(view));
+ });
+ }
+
+ private void displayPolicy(PreferenceViewHolder view) {
+ if (mHasCredentialManagerPackage) {
+ RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
+ recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+
+ CredentialManagementAppAdapter recyclerViewAdapter = new CredentialManagementAppAdapter(
+ mContext, mCredentialManagerPackageName,
+ mCredentialManagerPolicy.getAppAndUriMappings(),
+ /* include header= */ false, /* include expander= */ true);
+ recyclerView.setAdapter(recyclerViewAdapter);
+ }
+ }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppPreferenceController.java b/src/com/android/settings/security/CredentialManagementAppPreferenceController.java
new file mode 100644
index 00000000000..107b8f28b6a
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppPreferenceController.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 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;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Controller that shows and updates the credential management app summary.
+ */
+public class CredentialManagementAppPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "CredentialManagementApp";
+
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ private final PackageManager mPackageManager;
+ private boolean mHasCredentialManagerPackage;
+ private String mCredentialManagerPackageName;
+
+ public CredentialManagementAppPreferenceController(Context context, String key) {
+ super(context, key);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ mExecutor.execute(() -> {
+ try {
+ IKeyChainService service = KeyChain.bind(mContext).getService();
+ mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+ mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+ } catch (InterruptedException | RemoteException e) {
+ Log.e(TAG, "Unable to display credential management app preference");
+ }
+ mHandler.post(() -> displayPreference(preference));
+ });
+ }
+
+ private void displayPreference(Preference preference) {
+ if (mHasCredentialManagerPackage) {
+ preference.setEnabled(true);
+ try {
+ ApplicationInfo applicationInfo =
+ mPackageManager.getApplicationInfo(mCredentialManagerPackageName, 0);
+ preference.setSummary(applicationInfo.loadLabel(mPackageManager));
+ } catch (PackageManager.NameNotFoundException e) {
+ preference.setSummary(mCredentialManagerPackageName);
+ }
+ } else {
+ preference.setEnabled(false);
+ preference.setSummary(R.string.no_certificate_management_app);
+ }
+ }
+}
diff --git a/src/com/android/settings/security/RequestManageCredentials.java b/src/com/android/settings/security/RequestManageCredentials.java
index 9d2d51e315d..36b055ab322 100644
--- a/src/com/android/settings/security/RequestManageCredentials.java
+++ b/src/com/android/settings/security/RequestManageCredentials.java
@@ -93,7 +93,8 @@ public class RequestManageCredentials extends Activity {
mRecyclerView.setLayoutManager(mLayoutManager);
CredentialManagementAppAdapter recyclerViewAdapter = new CredentialManagementAppAdapter(
- this, mCredentialManagerPackage, mAuthenticationPolicy.getAppAndUriMappings());
+ this, mCredentialManagerPackage, mAuthenticationPolicy.getAppAndUriMappings(),
+ /* include header= */ true, /* include expander= */ false);
mRecyclerView.setAdapter(recyclerViewAdapter);
}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppButtonsControllerTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppButtonsControllerTest.java
new file mode 100644
index 00000000000..ecc2f2dd1ca
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppButtonsControllerTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class CredentialManagementAppButtonsControllerTest {
+
+ private Context mContext;
+ private CredentialManagementAppButtonsController mController;
+
+ private static final String PREF_KEY_CREDENTIAL_MANAGEMENT_APP = "certificate_management_app";
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mController = new CredentialManagementAppButtonsController(
+ mContext, PREF_KEY_CREDENTIAL_MANAGEMENT_APP);
+ }
+
+ @Test
+ public void getAvailabilityStatus_shouldAlwaysReturnAvailableUnsearchable() {
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.AVAILABLE_UNSEARCHABLE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppControllerTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppControllerTest.java
new file mode 100644
index 00000000000..1e6f203012a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppControllerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class CredentialManagementAppControllerTest {
+
+ private Context mContext;
+ private CredentialManagementAppPreferenceController mController;
+ private Preference mPreference;
+
+ private static final String PREF_KEY_CREDENTIAL_MANAGEMENT_APP = "certificate_management_app";
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mController = new CredentialManagementAppPreferenceController(
+ mContext, PREF_KEY_CREDENTIAL_MANAGEMENT_APP);
+ mPreference = new Preference(mContext);
+ }
+
+ @Test
+ public void getAvailabilityStatus_shouldAlwaysReturnAvailable() {
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void updateState_noCredentialManagementApp_shouldDisablePreference() {
+ mController.updateState(mPreference);
+
+ assertThat(mPreference.isEnabled()).isEqualTo(false);
+ assertThat(mPreference.getSummary()).isEqualTo(
+ mContext.getText(R.string.no_certificate_management_app));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppFragmentTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppFragmentTest.java
new file mode 100644
index 00000000000..de19e5cce90
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppFragmentTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static android.app.settings.SettingsEnums.CREDENTIAL_MANAGEMENT_APP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDashboardFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowDashboardFragment.class)
+public class CredentialManagementAppFragmentTest {
+
+ private CredentialManagementAppFragment mFragment;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mFragment = spy(new CredentialManagementAppFragment());
+
+ doReturn(mContext).when(mFragment).getContext();
+ }
+
+ @Test
+ public void searchIndexProvider_shouldIndexResource() {
+ final List indexRes =
+ CredentialManagementAppFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getXmlResourcesToIndex(mContext, true /* enabled */);
+
+ assertThat(indexRes).isNotNull();
+ assertThat(indexRes.get(0).xmlResId).isEqualTo(R.xml.credential_management_app_fragment);
+ }
+
+ @Test
+ public void getMetricsCategory_shouldReturnInstallCertificateFromStorage() {
+ assertThat(mFragment.getMetricsCategory()).isEqualTo(CREDENTIAL_MANAGEMENT_APP);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppHeaderControllerTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppHeaderControllerTest.java
new file mode 100644
index 00000000000..e77e4c18152
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppHeaderControllerTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class CredentialManagementAppHeaderControllerTest {
+
+ private Context mContext;
+ private CredentialManagementAppHeaderController mController;
+
+ private static final String PREF_KEY_CREDENTIAL_MANAGEMENT_APP = "certificate_management_app";
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mController = new CredentialManagementAppHeaderController(
+ mContext, PREF_KEY_CREDENTIAL_MANAGEMENT_APP);
+ }
+
+ @Test
+ public void getAvailabilityStatus_shouldAlwaysReturnAvailableUnsearchable() {
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.AVAILABLE_UNSEARCHABLE);
+ }
+}