Merge "Use the custom name in the battery attribution if it is unknown item" into tm-dev
diff --git a/res/layout/preference_expand_divider.xml b/res/layout/preference_expand_divider.xml
index ce3d2e7..9b76688 100644
--- a/res/layout/preference_expand_divider.xml
+++ b/res/layout/preference_expand_divider.xml
@@ -34,7 +34,6 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingEnd="4dp"
- android:singleLine="true"
android:textAlignment="viewStart"
style="@style/PreferenceCategoryTitleTextStyle"/>
diff --git a/res/layout/trusted_credentials.xml b/res/layout/trusted_credentials.xml
index 31f5f40..3663a79 100644
--- a/res/layout/trusted_credentials.xml
+++ b/res/layout/trusted_credentials.xml
@@ -13,78 +13,26 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<TabHost
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <ProgressBar
+ android:id="@+id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:visibility="gone"/>
+
<LinearLayout
+ android:id="@+id/content"
android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <TabWidget
- android:id="@android:id/tabs"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" />
-
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <FrameLayout
- android:id="@+id/system_tab"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <ProgressBar
- android:id="@+id/system_progress"
- style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:visibility="gone" />
-
- <LinearLayout
- android:id="@+id/system_content"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- android:animateLayoutChanges="true">
- </LinearLayout>
-
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/user_tab"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <ProgressBar
- android:id="@+id/user_progress"
- style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:visibility="gone" />
-
- <LinearLayout
- android:id="@+id/user_content"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- android:animateLayoutChanges="true">
- </LinearLayout>
-
- </FrameLayout>
-
- </FrameLayout>
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone">
</LinearLayout>
-</TabHost>
+</FrameLayout>
\ No newline at end of file
diff --git a/src/com/android/settings/TrustedCredentialsDialogBuilder.java b/src/com/android/settings/TrustedCredentialsDialogBuilder.java
index 806da92..0dc8c25 100644
--- a/src/com/android/settings/TrustedCredentialsDialogBuilder.java
+++ b/src/com/android/settings/TrustedCredentialsDialogBuilder.java
@@ -34,7 +34,7 @@
import androidx.appcompat.app.AlertDialog;
import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.TrustedCredentialsSettings.CertHolder;
+import com.android.settings.TrustedCredentialsFragment.CertHolder;
import com.android.settingslib.RestrictedLockUtils;
import java.security.cert.X509Certificate;
diff --git a/src/com/android/settings/TrustedCredentialsFragment.java b/src/com/android/settings/TrustedCredentialsFragment.java
new file mode 100644
index 0000000..ca565a4
--- /dev/null
+++ b/src/com/android/settings/TrustedCredentialsFragment.java
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (C) 2022 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;
+
+import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
+import static android.widget.LinearLayout.LayoutParams.MATCH_PARENT;
+import static android.widget.LinearLayout.LayoutParams.WRAP_CONTENT;
+
+import android.annotation.UiThread;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.net.http.SslCertificate;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.UnlaunchableAppActivity;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.TrustedCredentialsSettings.Tab;
+import com.android.settings.core.InstrumentedFragment;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.IntConsumer;
+
+/**
+ * Fragment to display trusted credentials settings for one tab.
+ */
+public class TrustedCredentialsFragment extends InstrumentedFragment
+ implements TrustedCredentialsDialogBuilder.DelegateInterface {
+
+ public static final String ARG_POSITION = "tab";
+ public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
+
+ private static final String TAG = "TrustedCredentialsFragment";
+
+ private DevicePolicyManager mDevicePolicyManager;
+ private UserManager mUserManager;
+ private KeyguardManager mKeyguardManager;
+ private int mTrustAllCaUserId;
+
+ private static final String SAVED_CONFIRMED_CREDENTIAL_USERS = "ConfirmedCredentialUsers";
+ private static final String SAVED_CONFIRMING_CREDENTIAL_USER = "ConfirmingCredentialUser";
+ private static final int REQUEST_CONFIRM_CREDENTIALS = 1;
+
+ private GroupAdapter mGroupAdapter;
+ private AliasOperation mAliasOperation;
+ private ArraySet<Integer> mConfirmedCredentialUsers;
+ private int mConfirmingCredentialUser;
+ private IntConsumer mConfirmingCredentialListener;
+ private final Set<AdapterData.AliasLoader> mAliasLoaders = new ArraySet<>(2);
+ @GuardedBy("mKeyChainConnectionByProfileId")
+ private final SparseArray<KeyChainConnection>
+ mKeyChainConnectionByProfileId = new SparseArray<>();
+ private ViewGroup mFragmentView;
+
+ private final BroadcastReceiver mWorkProfileChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
+ mGroupAdapter.load();
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Activity activity = getActivity();
+ mDevicePolicyManager = activity.getSystemService(DevicePolicyManager.class);
+ mUserManager = activity.getSystemService(UserManager.class);
+ mKeyguardManager = activity.getSystemService(KeyguardManager.class);
+ mTrustAllCaUserId = activity.getIntent().getIntExtra(ARG_SHOW_NEW_FOR_USER,
+ UserHandle.USER_NULL);
+ mConfirmedCredentialUsers = new ArraySet<>(2);
+ mConfirmingCredentialUser = UserHandle.USER_NULL;
+ if (savedInstanceState != null) {
+ mConfirmingCredentialUser = savedInstanceState.getInt(SAVED_CONFIRMING_CREDENTIAL_USER,
+ UserHandle.USER_NULL);
+ ArrayList<Integer> users = savedInstanceState.getIntegerArrayList(
+ SAVED_CONFIRMED_CREDENTIAL_USERS);
+ if (users != null) {
+ mConfirmedCredentialUsers.addAll(users);
+ }
+ }
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ activity.registerReceiver(mWorkProfileChangedReceiver, filter);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putIntegerArrayList(SAVED_CONFIRMED_CREDENTIAL_USERS, new ArrayList<>(
+ mConfirmedCredentialUsers));
+ outState.putInt(SAVED_CONFIRMING_CREDENTIAL_USER, mConfirmingCredentialUser);
+ mGroupAdapter.saveState(outState);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+ mFragmentView = (ViewGroup) inflater.inflate(R.layout.trusted_credentials, parent, false);
+
+ ViewGroup contentView = mFragmentView.findViewById(R.id.content);
+
+ mGroupAdapter = new GroupAdapter(
+ requireArguments().getInt(ARG_POSITION) == 0 ? Tab.SYSTEM : Tab.USER);
+ int profilesSize = mGroupAdapter.getGroupCount();
+ for (int i = 0; i < profilesSize; i++) {
+ Bundle childState = savedInstanceState == null ? null
+ : savedInstanceState.getBundle(mGroupAdapter.getKey(i));
+ createChildView(inflater, contentView, childState, i);
+ }
+ return mFragmentView;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.TRUSTED_CREDENTIALS;
+ }
+
+ private void createChildView(
+ LayoutInflater inflater, ViewGroup parent, Bundle childState, int i) {
+ boolean isWork = mGroupAdapter.getUserInfoByGroup(i).isManagedProfile();
+ ChildAdapter adapter = mGroupAdapter.createChildAdapter(i);
+
+ LinearLayout containerView = (LinearLayout) inflater.inflate(
+ R.layout.trusted_credential_list_container, parent, false);
+ adapter.setContainerView(containerView, childState);
+
+ int profilesSize = mGroupAdapter.getGroupCount();
+ adapter.showHeader(profilesSize > 1);
+ adapter.showDivider(isWork);
+ adapter.setExpandIfAvailable(profilesSize <= 2 || !isWork, childState);
+ if (isWork) {
+ parent.addView(containerView);
+ } else {
+ parent.addView(containerView, 0);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mFragmentView.requestLayout();
+ }
+
+ @Override
+ public void onDestroy() {
+ getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
+ for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
+ aliasLoader.cancel(true);
+ }
+ mAliasLoaders.clear();
+ if (mAliasOperation != null) {
+ mAliasOperation.cancel(true);
+ mAliasOperation = null;
+ }
+ closeKeyChainConnections();
+ super.onDestroy();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CONFIRM_CREDENTIALS) {
+ int userId = mConfirmingCredentialUser;
+ IntConsumer listener = mConfirmingCredentialListener;
+ // reset them before calling the listener because the listener may call back to start
+ // activity again. (though it should never happen.)
+ mConfirmingCredentialUser = UserHandle.USER_NULL;
+ mConfirmingCredentialListener = null;
+ if (resultCode == Activity.RESULT_OK) {
+ mConfirmedCredentialUsers.add(userId);
+ if (listener != null) {
+ listener.accept(userId);
+ }
+ }
+ }
+ }
+
+ private void closeKeyChainConnections() {
+ synchronized (mKeyChainConnectionByProfileId) {
+ int n = mKeyChainConnectionByProfileId.size();
+ for (int i = 0; i < n; ++i) {
+ mKeyChainConnectionByProfileId.valueAt(i).close();
+ }
+ mKeyChainConnectionByProfileId.clear();
+ }
+ }
+
+ /**
+ * Start work challenge activity.
+ *
+ * @return true if screenlock exists
+ */
+ private boolean startConfirmCredential(int userId) {
+ Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null, userId);
+ if (newIntent == null) {
+ return false;
+ }
+ mConfirmingCredentialUser = userId;
+ startActivityForResult(newIntent, REQUEST_CONFIRM_CREDENTIALS);
+ return true;
+ }
+
+ /**
+ * Adapter for expandable list view of certificates. Groups in the view correspond to profiles
+ * whereas children correspond to certificates.
+ */
+ private class GroupAdapter extends BaseExpandableListAdapter implements
+ ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener {
+ private final AdapterData mData;
+ private final ArrayList<ChildAdapter> mChildAdapters = new ArrayList<>();
+
+ private GroupAdapter(Tab tab) {
+ mData = new AdapterData(tab, this);
+ load();
+ }
+
+ @Override
+ public int getGroupCount() {
+ return mData.mCertHoldersByUserId.size();
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(groupPosition);
+ if (certHolders != null) {
+ return certHolders.size();
+ }
+ return 0;
+ }
+
+ @Override
+ public UserHandle getGroup(int groupPosition) {
+ return new UserHandle(mData.mCertHoldersByUserId.keyAt(groupPosition));
+ }
+
+ @Override
+ public CertHolder getChild(int groupPosition, int childPosition) {
+ return mData.mCertHoldersByUserId.get(getUserIdByGroup(groupPosition)).get(
+ childPosition);
+ }
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return getUserIdByGroup(groupPosition);
+ }
+
+ private int getUserIdByGroup(int groupPosition) {
+ return mData.mCertHoldersByUserId.keyAt(groupPosition);
+ }
+
+ public UserInfo getUserInfoByGroup(int groupPosition) {
+ return mUserManager.getUserInfo(getUserIdByGroup(groupPosition));
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ @Override
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ if (convertView == null) {
+ LayoutInflater inflater = (LayoutInflater) getActivity()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = Utils.inflateCategoryHeader(inflater, parent);
+ }
+
+ TextView title = convertView.findViewById(android.R.id.title);
+ if (getUserInfoByGroup(groupPosition).isManagedProfile()) {
+ title.setText(mDevicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER,
+ () -> getString(R.string.category_work)));
+ } else {
+ title.setText(mDevicePolicyManager.getResources().getString(
+ PERSONAL_CATEGORY_HEADER,
+ () -> getString(R.string.category_personal)));
+
+ }
+ title.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
+
+ return convertView;
+ }
+
+ @Override
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ return getViewForCertificate(getChild(groupPosition, childPosition), mData.mTab,
+ convertView, parent);
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ @Override
+ public boolean onChildClick(ExpandableListView expandableListView, View view,
+ int groupPosition, int childPosition, long id) {
+ showCertDialog(getChild(groupPosition, childPosition));
+ return true;
+ }
+
+ @Override
+ public boolean onGroupClick(ExpandableListView expandableListView, View view,
+ int groupPosition, long id) {
+ return !checkGroupExpandableAndStartWarningActivity(groupPosition);
+ }
+
+ public void load() {
+ mData.new AliasLoader().execute();
+ }
+
+ public void remove(CertHolder certHolder) {
+ mData.remove(certHolder);
+ }
+
+ ChildAdapter createChildAdapter(int groupPosition) {
+ ChildAdapter childAdapter = new ChildAdapter(this, groupPosition);
+ mChildAdapters.add(childAdapter);
+ return childAdapter;
+ }
+
+ public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition) {
+ return checkGroupExpandableAndStartWarningActivity(groupPosition, true);
+ }
+
+ public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition,
+ boolean startActivity) {
+ UserHandle groupUser = getGroup(groupPosition);
+ int groupUserId = groupUser.getIdentifier();
+ if (mUserManager.isQuietModeEnabled(groupUser)) {
+ if (startActivity) {
+ Intent intent =
+ UnlaunchableAppActivity.createInQuietModeDialogIntent(groupUserId);
+ getActivity().startActivity(intent);
+ }
+ return false;
+ } else if (!mUserManager.isUserUnlocked(groupUser)) {
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(getActivity());
+ if (lockPatternUtils.isSeparateProfileChallengeEnabled(groupUserId)) {
+ if (startActivity) {
+ startConfirmCredential(groupUserId);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView,
+ ViewGroup parent) {
+ ViewHolder holder;
+ if (convertView == null) {
+ holder = new ViewHolder();
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ convertView = inflater.inflate(R.layout.trusted_credential, parent, false);
+ convertView.setTag(holder);
+ holder.mSubjectPrimaryView =
+ convertView.findViewById(R.id.trusted_credential_subject_primary);
+ holder.mSubjectSecondaryView =
+ convertView.findViewById(R.id.trusted_credential_subject_secondary);
+ holder.mSwitch = convertView.findViewById(R.id.trusted_credential_status);
+ holder.mSwitch.setOnClickListener(view -> {
+ removeOrInstallCert((CertHolder) view.getTag());
+ });
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary);
+ holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary);
+ if (mTab.mSwitch) {
+ holder.mSwitch.setChecked(!certHolder.mDeleted);
+ holder.mSwitch.setEnabled(!mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_CONFIG_CREDENTIALS,
+ new UserHandle(certHolder.mProfileId)));
+ holder.mSwitch.setVisibility(View.VISIBLE);
+ holder.mSwitch.setTag(certHolder);
+ }
+ return convertView;
+ }
+
+ private void saveState(Bundle outState) {
+ for (int groupPosition = 0, mChildAdaptersSize = mChildAdapters.size();
+ groupPosition < mChildAdaptersSize; groupPosition++) {
+ ChildAdapter childAdapter = mChildAdapters.get(groupPosition);
+ outState.putBundle(getKey(groupPosition), childAdapter.saveState());
+ }
+ }
+
+ @NonNull
+ private String getKey(int groupPosition) {
+ return "Group" + getUserIdByGroup(groupPosition);
+ }
+
+ private class ViewHolder {
+ private TextView mSubjectPrimaryView;
+ private TextView mSubjectSecondaryView;
+ private Switch mSwitch;
+ }
+ }
+
+ private class ChildAdapter extends BaseAdapter implements View.OnClickListener,
+ AdapterView.OnItemClickListener {
+ private static final String KEY_CONTAINER = "Container";
+ private static final String KEY_IS_LIST_EXPANDED = "IsListExpanded";
+ private final int[] mGroupExpandedStateSet = {com.android.internal.R.attr.state_expanded};
+ private final int[] mEmptyStateSet = {};
+ private final LinearLayout.LayoutParams mHideContainerLayoutParams =
+ new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0f);
+ private final LinearLayout.LayoutParams mHideListLayoutParams =
+ new LinearLayout.LayoutParams(MATCH_PARENT, 0);
+ private final LinearLayout.LayoutParams mShowLayoutParams = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT, MATCH_PARENT, 1f);
+ private final GroupAdapter mParent;
+ private final int mGroupPosition;
+ /*
+ * This class doesn't hold the actual data. Events should notify parent.
+ * When notifying DataSet events in this class, events should be forwarded to mParent.
+ * i.e. this.notifyDataSetChanged -> mParent.notifyDataSetChanged -> mObserver.onChanged
+ * -> outsideObservers.onChanged() (e.g. ListView)
+ */
+ private final DataSetObserver mObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ TrustedCredentialsFragment.ChildAdapter.super.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onInvalidated() {
+ super.onInvalidated();
+ TrustedCredentialsFragment.ChildAdapter.super.notifyDataSetInvalidated();
+ }
+ };
+
+ private boolean mIsListExpanded = true;
+ private LinearLayout mContainerView;
+ private ViewGroup mHeaderView;
+ private ListView mListView;
+ private ImageView mIndicatorView;
+
+ private ChildAdapter(GroupAdapter parent, int groupPosition) {
+ mParent = parent;
+ mGroupPosition = groupPosition;
+ mParent.registerDataSetObserver(mObserver);
+ }
+
+ @Override
+ public int getCount() {
+ return mParent.getChildrenCount(mGroupPosition);
+ }
+
+ @Override
+ public CertHolder getItem(int position) {
+ return mParent.getChild(mGroupPosition, position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mParent.getChildId(mGroupPosition, position);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return mParent.getChildView(mGroupPosition, position, false, convertView, parent);
+ }
+
+ // DataSet events
+ @Override
+ public void notifyDataSetChanged() {
+ // Don't call super as the parent will propagate this event back later in mObserver
+ mParent.notifyDataSetChanged();
+ }
+
+ @Override
+ public void notifyDataSetInvalidated() {
+ // Don't call super as the parent will propagate this event back later in mObserver
+ mParent.notifyDataSetInvalidated();
+ }
+
+ // View related codes
+ @Override
+ public void onClick(View view) {
+ mIsListExpanded = checkGroupExpandableAndStartWarningActivity() && !mIsListExpanded;
+ refreshViews();
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
+ showCertDialog(getItem(pos));
+ }
+
+ public void setContainerView(LinearLayout containerView, Bundle savedState) {
+ mContainerView = containerView;
+ // Handle manually because multiple groups with same id elements.
+ mContainerView.setSaveFromParentEnabled(false);
+
+ mListView = mContainerView.findViewById(R.id.cert_list);
+ mListView.setAdapter(this);
+ mListView.setOnItemClickListener(this);
+ mListView.setItemsCanFocus(true);
+
+ mHeaderView = mContainerView.findViewById(R.id.header_view);
+ mHeaderView.setOnClickListener(this);
+
+ mIndicatorView = mHeaderView.findViewById(R.id.group_indicator);
+ mIndicatorView.setImageDrawable(getGroupIndicator());
+
+ FrameLayout headerContentContainer =
+ mHeaderView.findViewById(R.id.header_content_container);
+ headerContentContainer.addView(
+ mParent.getGroupView(mGroupPosition, true /* parent ignores it */, null,
+ headerContentContainer));
+
+ if (savedState != null) {
+ SparseArray<Parcelable> containerStates =
+ savedState.getSparseParcelableArray(KEY_CONTAINER, Parcelable.class);
+ if (containerStates != null) {
+ mContainerView.restoreHierarchyState(containerStates);
+ }
+ }
+ }
+
+ public void showHeader(boolean showHeader) {
+ mHeaderView.setVisibility(showHeader ? View.VISIBLE : View.GONE);
+ }
+
+ public void showDivider(boolean showDivider) {
+ View dividerView = mHeaderView.findViewById(R.id.header_divider);
+ dividerView.setVisibility(showDivider ? View.VISIBLE : View.GONE);
+ }
+
+ public void setExpandIfAvailable(boolean expanded, Bundle savedState) {
+ if (savedState != null) {
+ expanded = savedState.getBoolean(KEY_IS_LIST_EXPANDED);
+ }
+ mIsListExpanded = expanded && mParent.checkGroupExpandableAndStartWarningActivity(
+ mGroupPosition, false /* startActivity */);
+ refreshViews();
+ }
+
+ private boolean checkGroupExpandableAndStartWarningActivity() {
+ return mParent.checkGroupExpandableAndStartWarningActivity(mGroupPosition);
+ }
+
+ private void refreshViews() {
+ mIndicatorView.setImageState(mIsListExpanded ? mGroupExpandedStateSet
+ : mEmptyStateSet, false);
+ mListView.setLayoutParams(mIsListExpanded ? mShowLayoutParams
+ : mHideListLayoutParams);
+ mContainerView.setLayoutParams(mIsListExpanded ? mShowLayoutParams
+ : mHideContainerLayoutParams);
+ }
+
+ // Get group indicator from styles of ExpandableListView
+ private Drawable getGroupIndicator() {
+ TypedArray a = getActivity().obtainStyledAttributes(null,
+ com.android.internal.R.styleable.ExpandableListView,
+ com.android.internal.R.attr.expandableListViewStyle, 0);
+ Drawable groupIndicator = a.getDrawable(
+ com.android.internal.R.styleable.ExpandableListView_groupIndicator);
+ a.recycle();
+ return groupIndicator;
+ }
+
+ private Bundle saveState() {
+ Bundle bundle = new Bundle();
+ SparseArray<Parcelable> states = new SparseArray<>();
+ mContainerView.saveHierarchyState(states);
+ bundle.putSparseParcelableArray(KEY_CONTAINER, states);
+ bundle.putBoolean(KEY_IS_LIST_EXPANDED, mIsListExpanded);
+ return bundle;
+ }
+ }
+
+ private class AdapterData {
+ private final SparseArray<List<CertHolder>> mCertHoldersByUserId =
+ new SparseArray<>();
+ private final Tab mTab;
+ private final GroupAdapter mAdapter;
+
+ private AdapterData(Tab tab, GroupAdapter adapter) {
+ mAdapter = adapter;
+ mTab = tab;
+ }
+
+ private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> {
+ private ProgressBar mProgressBar;
+ private View mContentView;
+ private Context mContext;
+
+ AliasLoader() {
+ mContext = getActivity();
+ mAliasLoaders.add(this);
+ List<UserHandle> profiles = mUserManager.getUserProfiles();
+ for (UserHandle profile : profiles) {
+ mCertHoldersByUserId.put(profile.getIdentifier(), new ArrayList<>());
+ }
+ }
+
+ private boolean shouldSkipProfile(UserHandle userHandle) {
+ return mUserManager.isQuietModeEnabled(userHandle)
+ || !mUserManager.isUserUnlocked(userHandle.getIdentifier());
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mProgressBar = mFragmentView.findViewById(R.id.progress);
+ mContentView = mFragmentView.findViewById(R.id.content);
+ mProgressBar.setVisibility(View.VISIBLE);
+ mContentView.setVisibility(View.GONE);
+ }
+
+ @Override
+ protected SparseArray<List<CertHolder>> doInBackground(Void... params) {
+ SparseArray<List<CertHolder>> certHoldersByProfile =
+ new SparseArray<>();
+ try {
+ synchronized (mKeyChainConnectionByProfileId) {
+ List<UserHandle> profiles = mUserManager.getUserProfiles();
+ // First we get all aliases for all profiles in order to show progress
+ // correctly. Otherwise this could all be in a single loop.
+ SparseArray<List<String>> aliasesByProfileId =
+ new SparseArray<>(profiles.size());
+ int max = 0;
+ int progress = 0;
+ for (UserHandle profile : profiles) {
+ int profileId = profile.getIdentifier();
+ if (shouldSkipProfile(profile)) {
+ continue;
+ }
+ KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
+ profile);
+ // Saving the connection for later use on the certificate dialog.
+ mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
+ IKeyChainService service = keyChainConnection.getService();
+ List<String> aliases = mTab.getAliases(service);
+ if (isCancelled()) {
+ return new SparseArray<>();
+ }
+ max += aliases.size();
+ aliasesByProfileId.put(profileId, aliases);
+ }
+ for (UserHandle profile : profiles) {
+ int profileId = profile.getIdentifier();
+ List<String> aliases = aliasesByProfileId.get(profileId);
+ if (isCancelled()) {
+ return new SparseArray<>();
+ }
+ KeyChainConnection keyChainConnection =
+ mKeyChainConnectionByProfileId.get(
+ profileId);
+ if (shouldSkipProfile(profile) || aliases == null
+ || keyChainConnection == null) {
+ certHoldersByProfile.put(profileId, new ArrayList<>(0));
+ continue;
+ }
+ IKeyChainService service = keyChainConnection.getService();
+ List<CertHolder> certHolders = new ArrayList<>(max);
+ for (String alias : aliases) {
+ byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
+ true);
+ X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
+ certHolders.add(new CertHolder(service, mAdapter,
+ mTab, alias, cert, profileId));
+ publishProgress(++progress, max);
+ }
+ Collections.sort(certHolders);
+ certHoldersByProfile.put(profileId, certHolders);
+ }
+ return certHoldersByProfile;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception while loading aliases.", e);
+ return new SparseArray<>();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "InterruptedException while loading aliases.", e);
+ return new SparseArray<>();
+ }
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... progressAndMax) {
+ int progress = progressAndMax[0];
+ int max = progressAndMax[1];
+ if (max != mProgressBar.getMax()) {
+ mProgressBar.setMax(max);
+ }
+ mProgressBar.setProgress(progress);
+ }
+
+ @Override
+ protected void onPostExecute(SparseArray<List<CertHolder>> certHolders) {
+ mCertHoldersByUserId.clear();
+ int n = certHolders.size();
+ for (int i = 0; i < n; ++i) {
+ mCertHoldersByUserId.put(certHolders.keyAt(i), certHolders.valueAt(i));
+ }
+ mAdapter.notifyDataSetChanged();
+ mProgressBar.setVisibility(View.GONE);
+ mContentView.setVisibility(View.VISIBLE);
+ mProgressBar.setProgress(0);
+ mAliasLoaders.remove(this);
+ showTrustAllCaDialogIfNeeded();
+ }
+
+ private boolean isUserTabAndTrustAllCertMode() {
+ return isTrustAllCaCertModeInProgress() && mTab == Tab.USER;
+ }
+
+ @UiThread
+ private void showTrustAllCaDialogIfNeeded() {
+ if (!isUserTabAndTrustAllCertMode()) {
+ return;
+ }
+ List<CertHolder> certHolders = mCertHoldersByUserId.get(mTrustAllCaUserId);
+ if (certHolders == null) {
+ return;
+ }
+
+ List<CertHolder> unapprovedUserCertHolders = new ArrayList<>();
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ for (CertHolder cert : certHolders) {
+ if (cert != null && !dpm.isCaCertApproved(cert.mAlias, mTrustAllCaUserId)) {
+ unapprovedUserCertHolders.add(cert);
+ }
+ }
+
+ if (unapprovedUserCertHolders.size() == 0) {
+ Log.w(TAG, "no cert is pending approval for user " + mTrustAllCaUserId);
+ return;
+ }
+ showTrustAllCaDialog(unapprovedUserCertHolders);
+ }
+ }
+
+ public void remove(CertHolder certHolder) {
+ if (mCertHoldersByUserId != null) {
+ List<CertHolder> certs = mCertHoldersByUserId.get(certHolder.mProfileId);
+ if (certs != null) {
+ certs.remove(certHolder);
+ }
+ }
+ }
+ }
+
+ /* package */ static class CertHolder implements Comparable<CertHolder> {
+ public int mProfileId;
+ private final IKeyChainService mService;
+ private final GroupAdapter mAdapter;
+ private final Tab mTab;
+ private final String mAlias;
+ private final X509Certificate mX509Cert;
+
+ private final SslCertificate mSslCert;
+ private final String mSubjectPrimary;
+ private final String mSubjectSecondary;
+ private boolean mDeleted;
+
+ private CertHolder(IKeyChainService service,
+ GroupAdapter adapter,
+ Tab tab,
+ String alias,
+ X509Certificate x509Cert,
+ int profileId) {
+ mProfileId = profileId;
+ mService = service;
+ mAdapter = adapter;
+ mTab = tab;
+ mAlias = alias;
+ mX509Cert = x509Cert;
+
+ mSslCert = new SslCertificate(x509Cert);
+
+ String cn = mSslCert.getIssuedTo().getCName();
+ String o = mSslCert.getIssuedTo().getOName();
+ String ou = mSslCert.getIssuedTo().getUName();
+ // if we have a O, use O as primary subject, secondary prefer CN over OU
+ // if we don't have an O, use CN as primary, empty secondary
+ // if we don't have O or CN, use DName as primary, empty secondary
+ if (!o.isEmpty()) {
+ if (!cn.isEmpty()) {
+ mSubjectPrimary = o;
+ mSubjectSecondary = cn;
+ } else {
+ mSubjectPrimary = o;
+ mSubjectSecondary = ou;
+ }
+ } else {
+ if (!cn.isEmpty()) {
+ mSubjectPrimary = cn;
+ mSubjectSecondary = "";
+ } else {
+ mSubjectPrimary = mSslCert.getIssuedTo().getDName();
+ mSubjectSecondary = "";
+ }
+ }
+ try {
+ mDeleted = mTab.deleted(mService, mAlias);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception while checking if alias " + mAlias + " is deleted.",
+ e);
+ mDeleted = false;
+ }
+ }
+
+ @Override
+ public int compareTo(CertHolder o) {
+ int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary);
+ if (primary != 0) {
+ return primary;
+ }
+ return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof CertHolder)) {
+ return false;
+ }
+ CertHolder other = (CertHolder) o;
+ return mAlias.equals(other.mAlias);
+ }
+
+ @Override
+ public int hashCode() {
+ return mAlias.hashCode();
+ }
+
+ public int getUserId() {
+ return mProfileId;
+ }
+
+ public String getAlias() {
+ return mAlias;
+ }
+
+ public boolean isSystemCert() {
+ return mTab == Tab.SYSTEM;
+ }
+
+ public boolean isDeleted() {
+ return mDeleted;
+ }
+ }
+
+
+ private boolean isTrustAllCaCertModeInProgress() {
+ return mTrustAllCaUserId != UserHandle.USER_NULL;
+ }
+
+ private void showTrustAllCaDialog(List<CertHolder> unapprovedCertHolders) {
+ CertHolder[] arr =
+ unapprovedCertHolders.toArray(new CertHolder[unapprovedCertHolders.size()]);
+ new TrustedCredentialsDialogBuilder(getActivity(), this)
+ .setCertHolders(arr)
+ .setOnDismissListener(dialogInterface -> {
+ // Avoid starting dialog again after Activity restart.
+ getActivity().getIntent().removeExtra(ARG_SHOW_NEW_FOR_USER);
+ mTrustAllCaUserId = UserHandle.USER_NULL;
+ })
+ .show();
+ }
+
+ private void showCertDialog(final CertHolder certHolder) {
+ new TrustedCredentialsDialogBuilder(getActivity(), this)
+ .setCertHolder(certHolder)
+ .show();
+ }
+
+ @Override
+ public List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder) {
+ List<X509Certificate> certificates = null;
+ try {
+ synchronized (mKeyChainConnectionByProfileId) {
+ KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
+ certHolder.mProfileId);
+ IKeyChainService service = keyChainConnection.getService();
+ List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
+ certificates = new ArrayList<>(chain.size());
+ for (String s : chain) {
+ byte[] encodedCertificate = service.getEncodedCaCertificate(s, true);
+ X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
+ certificates.add(certificate);
+ }
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "RemoteException while retrieving certificate chain for root "
+ + certHolder.mAlias, ex);
+ }
+ return certificates;
+ }
+
+ @Override
+ public void removeOrInstallCert(CertHolder certHolder) {
+ new AliasOperation(certHolder).execute();
+ }
+
+ @Override
+ public boolean startConfirmCredentialIfNotConfirmed(int userId,
+ IntConsumer onCredentialConfirmedListener) {
+ if (mConfirmedCredentialUsers.contains(userId)) {
+ // Credential has been confirmed. Don't start activity.
+ return false;
+ }
+
+ boolean result = startConfirmCredential(userId);
+ if (result) {
+ mConfirmingCredentialListener = onCredentialConfirmedListener;
+ }
+ return result;
+ }
+
+ private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
+ private final CertHolder mCertHolder;
+
+ private AliasOperation(CertHolder certHolder) {
+ mCertHolder = certHolder;
+ mAliasOperation = this;
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ try {
+ synchronized (mKeyChainConnectionByProfileId) {
+ KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
+ mCertHolder.mProfileId);
+ IKeyChainService service = keyChainConnection.getService();
+ if (mCertHolder.mDeleted) {
+ byte[] bytes = mCertHolder.mX509Cert.getEncoded();
+ service.installCaCertificate(bytes);
+ return true;
+ } else {
+ return service.deleteCaCertificate(mCertHolder.mAlias);
+ }
+ }
+ } catch (CertificateEncodingException | SecurityException | IllegalStateException
+ | RemoteException e) {
+ Log.w(TAG, "Error while toggling alias " + mCertHolder.mAlias, e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean ok) {
+ if (ok) {
+ if (mCertHolder.mTab.mSwitch) {
+ mCertHolder.mDeleted = !mCertHolder.mDeleted;
+ } else {
+ mCertHolder.mAdapter.remove(mCertHolder);
+ }
+ mCertHolder.mAdapter.notifyDataSetChanged();
+ } else {
+ // bail, reload to reset to known state
+ mCertHolder.mAdapter.load();
+ }
+ mAliasOperation = null;
+ }
+ }
+}
diff --git a/src/com/android/settings/TrustedCredentialsSettings.java b/src/com/android/settings/TrustedCredentialsSettings.java
index 735cb3b..a88019e 100644
--- a/src/com/android/settings/TrustedCredentialsSettings.java
+++ b/src/com/android/settings/TrustedCredentialsSettings.java
@@ -16,121 +16,114 @@
package com.android.settings;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
-import static android.widget.LinearLayout.LayoutParams.MATCH_PARENT;
-import static android.widget.LinearLayout.LayoutParams.WRAP_CONTENT;
-
-import android.animation.LayoutTransition;
-import android.annotation.UiThread;
-import android.app.Activity;
-import android.app.KeyguardManager;
-import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.net.http.SslCertificate;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.security.IKeyChainService;
-import android.security.KeyChain;
-import android.security.KeyChain.KeyChainConnection;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.ExpandableListView;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.ProgressBar;
-import android.widget.Switch;
-import android.widget.TabHost;
-import android.widget.TextView;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.UnlaunchableAppActivity;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.core.InstrumentedFragment;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+import androidx.viewpager2.widget.ViewPager2;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collections;
+import com.android.settings.dashboard.DashboardFragment;
+
+import com.google.android.material.tabs.TabLayout;
+import com.google.android.material.tabs.TabLayoutMediator;
+import com.google.common.collect.ImmutableList;
+
import java.util.List;
-import java.util.Set;
-import java.util.function.IntConsumer;
-public class TrustedCredentialsSettings extends InstrumentedFragment
- implements TrustedCredentialsDialogBuilder.DelegateInterface {
-
- public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
+/**
+ * Main fragment to display trusted credentials settings.
+ */
+public class TrustedCredentialsSettings extends DashboardFragment {
private static final String TAG = "TrustedCredentialsSettings";
- private DevicePolicyManager mDevicePolicyManager;
- private UserManager mUserManager;
- private KeyguardManager mKeyguardManager;
- private int mTrustAllCaUserId;
+ public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
- private static final String SAVED_CONFIRMED_CREDENTIAL_USERS = "ConfirmedCredentialUsers";
- private static final String SAVED_CONFIRMING_CREDENTIAL_USER = "ConfirmingCredentialUser";
+ static final ImmutableList<Tab> TABS = ImmutableList.of(Tab.SYSTEM, Tab.USER);
+
private static final String USER_ACTION = "com.android.settings.TRUSTED_CREDENTIALS_USER";
- private static final int REQUEST_CONFIRM_CREDENTIALS = 1;
@Override
public int getMetricsCategory() {
return SettingsEnums.TRUSTED_CREDENTIALS;
}
- private enum Tab {
- SYSTEM("system",
- R.string.trusted_credentials_system_tab,
- R.id.system_tab,
- R.id.system_progress,
- R.id.system_content,
- true),
- USER("user",
- R.string.trusted_credentials_user_tab,
- R.id.user_tab,
- R.id.user_progress,
- R.id.user_content,
- false);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActivity().setTitle(R.string.trusted_credentials);
+ }
- private final String mTag;
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.placeholder_preference_screen;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ View tabContainer = view.findViewById(R.id.tab_container);
+ tabContainer.setVisibility(View.VISIBLE);
+
+ ViewPager2 viewPager = tabContainer.findViewById(R.id.view_pager);
+ viewPager.setAdapter(new FragmentAdapter(this));
+ viewPager.setUserInputEnabled(false);
+
+ Intent intent = getActivity().getIntent();
+ if (intent != null && USER_ACTION.equals(intent.getAction())) {
+ viewPager.setCurrentItem(TABS.indexOf(Tab.USER), false);
+ }
+
+ TabLayout tabLayout = tabContainer.findViewById(R.id.tabs);
+ new TabLayoutMediator(tabLayout, viewPager, false, false,
+ (tab, position) -> tab.setText(TABS.get(position).mLabel)).attach();
+ }
+
+ private static class FragmentAdapter extends FragmentStateAdapter {
+ FragmentAdapter(Fragment fragment) {
+ super(fragment);
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ TrustedCredentialsFragment fragment = new TrustedCredentialsFragment();
+ Bundle args = new Bundle();
+ args.putInt(TrustedCredentialsFragment.ARG_POSITION, position);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public int getItemCount() {
+ return TrustedCredentialsSettings.TABS.size();
+ }
+ }
+
+ enum Tab {
+ SYSTEM(R.string.trusted_credentials_system_tab, true),
+ USER(R.string.trusted_credentials_user_tab, false);
+
private final int mLabel;
- private final int mView;
- private final int mProgress;
- private final int mContentView;
- private final boolean mSwitch;
+ final boolean mSwitch;
- private Tab(String tag, int label, int view, int progress, int contentView,
- boolean withSwitch) {
- mTag = tag;
+ Tab(int label, boolean withSwitch) {
mLabel = label;
- mView = view;
- mProgress = progress;
- mContentView = contentView;
mSwitch = withSwitch;
}
- private List<String> getAliases(IKeyChainService service) throws RemoteException {
+ List<String> getAliases(IKeyChainService service) throws RemoteException {
switch (this) {
case SYSTEM: {
return service.getSystemCaAliases().getList();
@@ -140,7 +133,8 @@
}
throw new AssertionError();
}
- private boolean deleted(IKeyChainService service, String alias) throws RemoteException {
+
+ boolean deleted(IKeyChainService service, String alias) throws RemoteException {
switch (this) {
case SYSTEM:
return !service.containsCaAlias(alias);
@@ -150,895 +144,4 @@
throw new AssertionError();
}
}
-
- private TabHost mTabHost;
- private ArrayList<GroupAdapter> mGroupAdapters = new ArrayList<>(2);
- private AliasOperation mAliasOperation;
- private ArraySet<Integer> mConfirmedCredentialUsers;
- private int mConfirmingCredentialUser;
- private IntConsumer mConfirmingCredentialListener;
- private Set<AdapterData.AliasLoader> mAliasLoaders = new ArraySet<AdapterData.AliasLoader>(2);
- @GuardedBy("mKeyChainConnectionByProfileId")
- private final SparseArray<KeyChainConnection>
- mKeyChainConnectionByProfileId = new SparseArray<KeyChainConnection>();
-
- private BroadcastReceiver mWorkProfileChangedReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
- Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
- for (GroupAdapter adapter : mGroupAdapters) {
- adapter.load();
- }
- }
- }
-
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Activity activity = getActivity();
- mDevicePolicyManager = activity.getSystemService(DevicePolicyManager.class);
- mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
- mKeyguardManager = (KeyguardManager) activity
- .getSystemService(Context.KEYGUARD_SERVICE);
- mTrustAllCaUserId = activity.getIntent().getIntExtra(ARG_SHOW_NEW_FOR_USER,
- UserHandle.USER_NULL);
- mConfirmedCredentialUsers = new ArraySet<>(2);
- mConfirmingCredentialUser = UserHandle.USER_NULL;
- if (savedInstanceState != null) {
- mConfirmingCredentialUser = savedInstanceState.getInt(SAVED_CONFIRMING_CREDENTIAL_USER,
- UserHandle.USER_NULL);
- ArrayList<Integer> users = savedInstanceState.getIntegerArrayList(
- SAVED_CONFIRMED_CREDENTIAL_USERS);
- if (users != null) {
- mConfirmedCredentialUsers.addAll(users);
- }
- }
-
- mConfirmingCredentialListener = null;
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- activity.registerReceiver(mWorkProfileChangedReceiver, filter);
-
- activity.setTitle(R.string.trusted_credentials);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putIntegerArrayList(SAVED_CONFIRMED_CREDENTIAL_USERS, new ArrayList<>(
- mConfirmedCredentialUsers));
- outState.putInt(SAVED_CONFIRMING_CREDENTIAL_USER, mConfirmingCredentialUser);
- }
-
- @Override public View onCreateView(
- LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
- mTabHost = (TabHost) inflater.inflate(R.layout.trusted_credentials, parent, false);
- mTabHost.setup();
- addTab(Tab.SYSTEM);
- // TODO add Install button on Tab.USER to go to CertInstaller like KeyChainActivity
- addTab(Tab.USER);
- if (getActivity().getIntent() != null &&
- USER_ACTION.equals(getActivity().getIntent().getAction())) {
- mTabHost.setCurrentTabByTag(Tab.USER.mTag);
- }
- return mTabHost;
- }
- @Override
- public void onDestroy() {
- getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
- for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
- aliasLoader.cancel(true);
- }
- mAliasLoaders.clear();
- mGroupAdapters.clear();
- if (mAliasOperation != null) {
- mAliasOperation.cancel(true);
- mAliasOperation = null;
- }
- closeKeyChainConnections();
- super.onDestroy();
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CONFIRM_CREDENTIALS) {
- int userId = mConfirmingCredentialUser;
- IntConsumer listener = mConfirmingCredentialListener;
- // reset them before calling the listener because the listener may call back to start
- // activity again. (though it should never happen.)
- mConfirmingCredentialUser = UserHandle.USER_NULL;
- mConfirmingCredentialListener = null;
- if (resultCode == Activity.RESULT_OK) {
- mConfirmedCredentialUsers.add(userId);
- if (listener != null) {
- listener.accept(userId);
- }
- }
- }
- }
-
- private void closeKeyChainConnections() {
- synchronized (mKeyChainConnectionByProfileId) {
- final int n = mKeyChainConnectionByProfileId.size();
- for (int i = 0; i < n; ++i) {
- mKeyChainConnectionByProfileId.valueAt(i).close();
- }
- mKeyChainConnectionByProfileId.clear();
- }
- }
-
- private void addTab(Tab tab) {
- TabHost.TabSpec systemSpec = mTabHost.newTabSpec(tab.mTag)
- .setIndicator(getActivity().getString(tab.mLabel))
- .setContent(tab.mView);
- mTabHost.addTab(systemSpec);
-
- final GroupAdapter groupAdapter = new GroupAdapter(tab);
- mGroupAdapters.add(groupAdapter);
- final int profilesSize = groupAdapter.getGroupCount();
-
- // Add a transition for non-visibility events like resizing the pane.
- final ViewGroup contentView = (ViewGroup) mTabHost.findViewById(tab.mContentView);
- contentView.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
-
- final LayoutInflater inflater = LayoutInflater.from(getActivity());
- for (int i = 0; i < groupAdapter.getGroupCount(); i++) {
- final boolean isWork = groupAdapter.getUserInfoByGroup(i).isManagedProfile();
- final ChildAdapter adapter = groupAdapter.getChildAdapter(i);
-
- final LinearLayout containerView = (LinearLayout) inflater
- .inflate(R.layout.trusted_credential_list_container, contentView, false);
- adapter.setContainerView(containerView);
-
- adapter.showHeader(profilesSize > 1);
- adapter.showDivider(isWork);
- adapter.setExpandIfAvailable(profilesSize <= 2 ? true : !isWork);
- if (isWork) {
- contentView.addView(containerView);
- } else {
- contentView.addView(containerView, 0);
- }
- }
- }
-
- /**
- * Start work challenge activity.
- * @return true if screenlock exists
- */
- private boolean startConfirmCredential(int userId) {
- final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
- userId);
- if (newIntent == null) {
- return false;
- }
- mConfirmingCredentialUser = userId;
- startActivityForResult(newIntent, REQUEST_CONFIRM_CREDENTIALS);
- return true;
- }
-
- /**
- * Adapter for expandable list view of certificates. Groups in the view correspond to profiles
- * whereas children correspond to certificates.
- */
- private class GroupAdapter extends BaseExpandableListAdapter implements
- ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener,
- View.OnClickListener {
- private final AdapterData mData;
-
- private GroupAdapter(Tab tab) {
- mData = new AdapterData(tab, this);
- load();
- }
-
- @Override
- public int getGroupCount() {
- return mData.mCertHoldersByUserId.size();
- }
- @Override
- public int getChildrenCount(int groupPosition) {
- List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(groupPosition);
- if (certHolders != null) {
- return certHolders.size();
- }
- return 0;
- }
- @Override
- public UserHandle getGroup(int groupPosition) {
- return new UserHandle(mData.mCertHoldersByUserId.keyAt(groupPosition));
- }
- @Override
- public CertHolder getChild(int groupPosition, int childPosition) {
- return mData.mCertHoldersByUserId.get(getUserIdByGroup(groupPosition)).get(
- childPosition);
- }
- @Override
- public long getGroupId(int groupPosition) {
- return getUserIdByGroup(groupPosition);
- }
- private int getUserIdByGroup(int groupPosition) {
- return mData.mCertHoldersByUserId.keyAt(groupPosition);
- }
- public UserInfo getUserInfoByGroup(int groupPosition) {
- return mUserManager.getUserInfo(getUserIdByGroup(groupPosition));
- }
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
- @Override
- public boolean hasStableIds() {
- return false;
- }
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- if (convertView == null) {
- LayoutInflater inflater = (LayoutInflater) getActivity()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = Utils.inflateCategoryHeader(inflater, parent);
- }
-
- final TextView title = (TextView) convertView.findViewById(android.R.id.title);
- if (getUserInfoByGroup(groupPosition).isManagedProfile()) {
- title.setText(mDevicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER,
- () -> getString(R.string.category_work)));
- } else {
- title.setText(mDevicePolicyManager.getResources().getString(
- PERSONAL_CATEGORY_HEADER,
- () -> getString(R.string.category_personal)));
-
- }
- title.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
-
- return convertView;
- }
- @Override
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- return getViewForCertificate(getChild(groupPosition, childPosition), mData.mTab,
- convertView, parent);
- }
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- @Override
- public boolean onChildClick(ExpandableListView expandableListView, View view,
- int groupPosition, int childPosition, long id) {
- showCertDialog(getChild(groupPosition, childPosition));
- return true;
- }
-
- /**
- * Called when the switch on a system certificate is clicked. This will toggle whether it
- * is trusted as a credential.
- */
- @Override
- public void onClick(View view) {
- CertHolder holder = (CertHolder) view.getTag();
- removeOrInstallCert(holder);
- }
-
- @Override
- public boolean onGroupClick(ExpandableListView expandableListView, View view,
- int groupPosition, long id) {
- return !checkGroupExpandableAndStartWarningActivity(groupPosition);
- }
-
- public void load() {
- mData.new AliasLoader().execute();
- }
-
- public void remove(CertHolder certHolder) {
- mData.remove(certHolder);
- }
-
- public void setExpandableListView(ExpandableListView lv) {
- lv.setAdapter(this);
- lv.setOnGroupClickListener(this);
- lv.setOnChildClickListener(this);
- lv.setVisibility(View.VISIBLE);
- }
-
- public ChildAdapter getChildAdapter(int groupPosition) {
- return new ChildAdapter(this, groupPosition);
- }
-
- public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition) {
- return checkGroupExpandableAndStartWarningActivity(groupPosition, true);
- }
-
- public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition,
- boolean startActivity) {
- final UserHandle groupUser = getGroup(groupPosition);
- final int groupUserId = groupUser.getIdentifier();
- if (mUserManager.isQuietModeEnabled(groupUser)) {
- final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
- groupUserId);
- if (startActivity) {
- getActivity().startActivity(intent);
- }
- return false;
- } else if (!mUserManager.isUserUnlocked(groupUser)) {
- final LockPatternUtils lockPatternUtils = new LockPatternUtils(
- getActivity());
- if (lockPatternUtils.isSeparateProfileChallengeEnabled(groupUserId)) {
- if (startActivity) {
- startConfirmCredential(groupUserId);
- }
- return false;
- }
- }
- return true;
- }
-
- private View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView,
- ViewGroup parent) {
- ViewHolder holder;
- if (convertView == null) {
- holder = new ViewHolder();
- LayoutInflater inflater = LayoutInflater.from(getActivity());
- convertView = inflater.inflate(R.layout.trusted_credential, parent, false);
- convertView.setTag(holder);
- holder.mSubjectPrimaryView = (TextView)
- convertView.findViewById(R.id.trusted_credential_subject_primary);
- holder.mSubjectSecondaryView = (TextView)
- convertView.findViewById(R.id.trusted_credential_subject_secondary);
- holder.mSwitch = (Switch) convertView.findViewById(
- R.id.trusted_credential_status);
- holder.mSwitch.setOnClickListener(this);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary);
- holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary);
- if (mTab.mSwitch) {
- holder.mSwitch.setChecked(!certHolder.mDeleted);
- holder.mSwitch.setEnabled(!mUserManager.hasUserRestriction(
- UserManager.DISALLOW_CONFIG_CREDENTIALS,
- new UserHandle(certHolder.mProfileId)));
- holder.mSwitch.setVisibility(View.VISIBLE);
- holder.mSwitch.setTag(certHolder);
- }
- return convertView;
- }
-
- private class ViewHolder {
- private TextView mSubjectPrimaryView;
- private TextView mSubjectSecondaryView;
- private Switch mSwitch;
- }
- }
-
- private class ChildAdapter extends BaseAdapter implements View.OnClickListener,
- AdapterView.OnItemClickListener {
- private final int[] GROUP_EXPANDED_STATE_SET = {com.android.internal.R.attr.state_expanded};
- private final int[] EMPTY_STATE_SET = {};
- private final LinearLayout.LayoutParams HIDE_CONTAINER_LAYOUT_PARAMS =
- new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0f);
- private final LinearLayout.LayoutParams HIDE_LIST_LAYOUT_PARAMS =
- new LinearLayout.LayoutParams(MATCH_PARENT, 0);
- private final LinearLayout.LayoutParams SHOW_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT, MATCH_PARENT, 1f);
- private final GroupAdapter mParent;
- private final int mGroupPosition;
- /*
- * This class doesn't hold the actual data. Events should notify parent.
- * When notifying DataSet events in this class, events should be forwarded to mParent.
- * i.e. this.notifyDataSetChanged -> mParent.notifyDataSetChanged -> mObserver.onChanged
- * -> outsideObservers.onChanged() (e.g. ListView)
- */
- private final DataSetObserver mObserver = new DataSetObserver() {
- @Override
- public void onChanged() {
- super.onChanged();
- ChildAdapter.super.notifyDataSetChanged();
- }
- @Override
- public void onInvalidated() {
- super.onInvalidated();
- ChildAdapter.super.notifyDataSetInvalidated();
- }
- };
-
- private boolean mIsListExpanded = true;
- private LinearLayout mContainerView;
- private ViewGroup mHeaderView;
- private ListView mListView;
- private ImageView mIndicatorView;
-
- private ChildAdapter(GroupAdapter parent, int groupPosition) {
- mParent = parent;
- mGroupPosition = groupPosition;
- mParent.registerDataSetObserver(mObserver);
- }
-
- @Override public int getCount() {
- return mParent.getChildrenCount(mGroupPosition);
- }
- @Override public CertHolder getItem(int position) {
- return mParent.getChild(mGroupPosition, position);
- }
- @Override public long getItemId(int position) {
- return mParent.getChildId(mGroupPosition, position);
- }
- @Override public View getView(int position, View convertView, ViewGroup parent) {
- return mParent.getChildView(mGroupPosition, position, false, convertView, parent);
- }
- // DataSet events
- @Override
- public void notifyDataSetChanged() {
- // Don't call super as the parent will propagate this event back later in mObserver
- mParent.notifyDataSetChanged();
- }
- @Override
- public void notifyDataSetInvalidated() {
- // Don't call super as the parent will propagate this event back later in mObserver
- mParent.notifyDataSetInvalidated();
- }
-
- // View related codes
- @Override
- public void onClick(View view) {
- mIsListExpanded = checkGroupExpandableAndStartWarningActivity() && !mIsListExpanded;
- refreshViews();
- }
-
- @Override
- public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
- showCertDialog(getItem(pos));
- }
-
- public void setContainerView(LinearLayout containerView) {
- mContainerView = containerView;
-
- mListView = (ListView) mContainerView.findViewById(R.id.cert_list);
- mListView.setAdapter(this);
- mListView.setOnItemClickListener(this);
- mListView.setItemsCanFocus(true);
-
- mHeaderView = (ViewGroup) mContainerView.findViewById(R.id.header_view);
- mHeaderView.setOnClickListener(this);
-
- mIndicatorView = (ImageView) mHeaderView.findViewById(R.id.group_indicator);
- mIndicatorView.setImageDrawable(getGroupIndicator());
-
- FrameLayout headerContentContainer = (FrameLayout)
- mHeaderView.findViewById(R.id.header_content_container);
- headerContentContainer.addView(
- mParent.getGroupView(mGroupPosition, true /* parent ignores it */, null,
- headerContentContainer));
- }
-
- public void showHeader(boolean showHeader) {
- mHeaderView.setVisibility(showHeader ? View.VISIBLE : View.GONE);
- }
-
- public void showDivider(boolean showDivider) {
- View dividerView = mHeaderView.findViewById(R.id.header_divider);
- dividerView.setVisibility(showDivider ? View.VISIBLE : View.GONE );
- }
-
- public void setExpandIfAvailable(boolean expanded) {
- mIsListExpanded = expanded && mParent.checkGroupExpandableAndStartWarningActivity(
- mGroupPosition, false /* startActivity */);
- refreshViews();
- }
-
- private boolean checkGroupExpandableAndStartWarningActivity() {
- return mParent.checkGroupExpandableAndStartWarningActivity(mGroupPosition);
- }
-
- private void refreshViews() {
- mIndicatorView.setImageState(mIsListExpanded ? GROUP_EXPANDED_STATE_SET
- : EMPTY_STATE_SET, false);
- mListView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
- : HIDE_LIST_LAYOUT_PARAMS);
- mContainerView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
- : HIDE_CONTAINER_LAYOUT_PARAMS);
- }
-
- // Get group indicator from styles of ExpandableListView
- private Drawable getGroupIndicator() {
- final TypedArray a = getActivity().obtainStyledAttributes(null,
- com.android.internal.R.styleable.ExpandableListView,
- com.android.internal.R.attr.expandableListViewStyle, 0);
- Drawable groupIndicator = a.getDrawable(
- com.android.internal.R.styleable.ExpandableListView_groupIndicator);
- a.recycle();
- return groupIndicator;
- }
- }
-
- private class AdapterData {
- private final SparseArray<List<CertHolder>> mCertHoldersByUserId =
- new SparseArray<List<CertHolder>>();
- private final Tab mTab;
- private final GroupAdapter mAdapter;
-
- private AdapterData(Tab tab, GroupAdapter adapter) {
- mAdapter = adapter;
- mTab = tab;
- }
-
- private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> {
- private ProgressBar mProgressBar;
- private View mContentView;
- private Context mContext;
-
- public AliasLoader() {
- mContext = getActivity();
- mAliasLoaders.add(this);
- List<UserHandle> profiles = mUserManager.getUserProfiles();
- for (UserHandle profile : profiles) {
- mCertHoldersByUserId.put(profile.getIdentifier(), new ArrayList<CertHolder>());
- }
- }
-
- private boolean shouldSkipProfile(UserHandle userHandle) {
- return mUserManager.isQuietModeEnabled(userHandle)
- || !mUserManager.isUserUnlocked(userHandle.getIdentifier());
- }
-
- @Override protected void onPreExecute() {
- View content = mTabHost.getTabContentView();
- mProgressBar = (ProgressBar) content.findViewById(mTab.mProgress);
- mContentView = content.findViewById(mTab.mContentView);
- mProgressBar.setVisibility(View.VISIBLE);
- mContentView.setVisibility(View.GONE);
- }
- @Override protected SparseArray<List<CertHolder>> doInBackground(Void... params) {
- SparseArray<List<CertHolder>> certHoldersByProfile =
- new SparseArray<List<CertHolder>>();
- try {
- synchronized(mKeyChainConnectionByProfileId) {
- List<UserHandle> profiles = mUserManager.getUserProfiles();
- final int n = profiles.size();
- // First we get all aliases for all profiles in order to show progress
- // correctly. Otherwise this could all be in a single loop.
- SparseArray<List<String>> aliasesByProfileId = new SparseArray<
- List<String>>(n);
- int max = 0;
- int progress = 0;
- for (int i = 0; i < n; ++i) {
- UserHandle profile = profiles.get(i);
- int profileId = profile.getIdentifier();
- if (shouldSkipProfile(profile)) {
- continue;
- }
- KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
- profile);
- // Saving the connection for later use on the certificate dialog.
- mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
- IKeyChainService service = keyChainConnection.getService();
- List<String> aliases = mTab.getAliases(service);
- if (isCancelled()) {
- return new SparseArray<List<CertHolder>>();
- }
- max += aliases.size();
- aliasesByProfileId.put(profileId, aliases);
- }
- for (int i = 0; i < n; ++i) {
- UserHandle profile = profiles.get(i);
- int profileId = profile.getIdentifier();
- List<String> aliases = aliasesByProfileId.get(profileId);
- if (isCancelled()) {
- return new SparseArray<List<CertHolder>>();
- }
- KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
- profileId);
- if (shouldSkipProfile(profile) || aliases == null
- || keyChainConnection == null) {
- certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0));
- continue;
- }
- IKeyChainService service = keyChainConnection.getService();
- List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
- final int aliasMax = aliases.size();
- for (int j = 0; j < aliasMax; ++j) {
- String alias = aliases.get(j);
- byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
- true);
- X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
- certHolders.add(new CertHolder(service, mAdapter,
- mTab, alias, cert, profileId));
- publishProgress(++progress, max);
- }
- Collections.sort(certHolders);
- certHoldersByProfile.put(profileId, certHolders);
- }
- return certHoldersByProfile;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception while loading aliases.", e);
- return new SparseArray<List<CertHolder>>();
- } catch (InterruptedException e) {
- Log.e(TAG, "InterruptedException while loading aliases.", e);
- return new SparseArray<List<CertHolder>>();
- }
- }
- @Override protected void onProgressUpdate(Integer... progressAndMax) {
- int progress = progressAndMax[0];
- int max = progressAndMax[1];
- if (max != mProgressBar.getMax()) {
- mProgressBar.setMax(max);
- }
- mProgressBar.setProgress(progress);
- }
- @Override protected void onPostExecute(SparseArray<List<CertHolder>> certHolders) {
- mCertHoldersByUserId.clear();
- final int n = certHolders.size();
- for (int i = 0; i < n; ++i) {
- mCertHoldersByUserId.put(certHolders.keyAt(i), certHolders.valueAt(i));
- }
- mAdapter.notifyDataSetChanged();
- mProgressBar.setVisibility(View.GONE);
- mContentView.setVisibility(View.VISIBLE);
- mProgressBar.setProgress(0);
- mAliasLoaders.remove(this);
- showTrustAllCaDialogIfNeeded();
- }
-
- private boolean isUserTabAndTrustAllCertMode() {
- return isTrustAllCaCertModeInProgress() && mTab == Tab.USER;
- }
-
- @UiThread
- private void showTrustAllCaDialogIfNeeded() {
- if (!isUserTabAndTrustAllCertMode()) {
- return;
- }
- List<CertHolder> certHolders = mCertHoldersByUserId.get(mTrustAllCaUserId);
- if (certHolders == null) {
- return;
- }
-
- List<CertHolder> unapprovedUserCertHolders = new ArrayList<>();
- final DevicePolicyManager dpm = mContext.getSystemService(
- DevicePolicyManager.class);
- for (CertHolder cert : certHolders) {
- if (cert != null && !dpm.isCaCertApproved(cert.mAlias, mTrustAllCaUserId)) {
- unapprovedUserCertHolders.add(cert);
- }
- }
-
- if (unapprovedUserCertHolders.size() == 0) {
- Log.w(TAG, "no cert is pending approval for user " + mTrustAllCaUserId);
- return;
- }
- showTrustAllCaDialog(unapprovedUserCertHolders);
- }
- }
-
- public void remove(CertHolder certHolder) {
- if (mCertHoldersByUserId != null) {
- final List<CertHolder> certs = mCertHoldersByUserId.get(certHolder.mProfileId);
- if (certs != null) {
- certs.remove(certHolder);
- }
- }
- }
- }
-
- /* package */ static class CertHolder implements Comparable<CertHolder> {
- public int mProfileId;
- private final IKeyChainService mService;
- private final GroupAdapter mAdapter;
- private final Tab mTab;
- private final String mAlias;
- private final X509Certificate mX509Cert;
-
- private final SslCertificate mSslCert;
- private final String mSubjectPrimary;
- private final String mSubjectSecondary;
- private boolean mDeleted;
-
- private CertHolder(IKeyChainService service,
- GroupAdapter adapter,
- Tab tab,
- String alias,
- X509Certificate x509Cert,
- int profileId) {
- mProfileId = profileId;
- mService = service;
- mAdapter = adapter;
- mTab = tab;
- mAlias = alias;
- mX509Cert = x509Cert;
-
- mSslCert = new SslCertificate(x509Cert);
-
- String cn = mSslCert.getIssuedTo().getCName();
- String o = mSslCert.getIssuedTo().getOName();
- String ou = mSslCert.getIssuedTo().getUName();
- // if we have a O, use O as primary subject, secondary prefer CN over OU
- // if we don't have an O, use CN as primary, empty secondary
- // if we don't have O or CN, use DName as primary, empty secondary
- if (!o.isEmpty()) {
- if (!cn.isEmpty()) {
- mSubjectPrimary = o;
- mSubjectSecondary = cn;
- } else {
- mSubjectPrimary = o;
- mSubjectSecondary = ou;
- }
- } else {
- if (!cn.isEmpty()) {
- mSubjectPrimary = cn;
- mSubjectSecondary = "";
- } else {
- mSubjectPrimary = mSslCert.getIssuedTo().getDName();
- mSubjectSecondary = "";
- }
- }
- try {
- mDeleted = mTab.deleted(mService, mAlias);
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception while checking if alias " + mAlias + " is deleted.",
- e);
- mDeleted = false;
- }
- }
- @Override public int compareTo(CertHolder o) {
- int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary);
- if (primary != 0) {
- return primary;
- }
- return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary);
- }
- @Override public boolean equals(Object o) {
- if (!(o instanceof CertHolder)) {
- return false;
- }
- CertHolder other = (CertHolder) o;
- return mAlias.equals(other.mAlias);
- }
- @Override public int hashCode() {
- return mAlias.hashCode();
- }
-
- public int getUserId() {
- return mProfileId;
- }
-
- public String getAlias() {
- return mAlias;
- }
-
- public boolean isSystemCert() {
- return mTab == Tab.SYSTEM;
- }
-
- public boolean isDeleted() {
- return mDeleted;
- }
- }
-
-
- private boolean isTrustAllCaCertModeInProgress() {
- return mTrustAllCaUserId != UserHandle.USER_NULL;
- }
-
- private void showTrustAllCaDialog(List<CertHolder> unapprovedCertHolders) {
- final CertHolder[] arr = unapprovedCertHolders.toArray(
- new CertHolder[unapprovedCertHolders.size()]);
- new TrustedCredentialsDialogBuilder(getActivity(), this)
- .setCertHolders(arr)
- .setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialogInterface) {
- // Avoid starting dialog again after Activity restart.
- getActivity().getIntent().removeExtra(ARG_SHOW_NEW_FOR_USER);
- mTrustAllCaUserId = UserHandle.USER_NULL;
- }
- })
- .show();
- }
-
- private void showCertDialog(final CertHolder certHolder) {
- new TrustedCredentialsDialogBuilder(getActivity(), this)
- .setCertHolder(certHolder)
- .show();
- }
-
- @Override
- public List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder) {
- List<X509Certificate> certificates = null;
- try {
- synchronized (mKeyChainConnectionByProfileId) {
- KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
- certHolder.mProfileId);
- IKeyChainService service = keyChainConnection.getService();
- List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
- final int n = chain.size();
- certificates = new ArrayList<X509Certificate>(n);
- for (int i = 0; i < n; ++i) {
- byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
- X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
- certificates.add(certificate);
- }
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException while retrieving certificate chain for root "
- + certHolder.mAlias, ex);
- }
- return certificates;
- }
-
- @Override
- public void removeOrInstallCert(CertHolder certHolder) {
- new AliasOperation(certHolder).execute();
- }
-
- @Override
- public boolean startConfirmCredentialIfNotConfirmed(int userId,
- IntConsumer onCredentialConfirmedListener) {
- if (mConfirmedCredentialUsers.contains(userId)) {
- // Credential has been confirmed. Don't start activity.
- return false;
- }
-
- boolean result = startConfirmCredential(userId);
- if (result) {
- mConfirmingCredentialListener = onCredentialConfirmedListener;
- }
- return result;
- }
-
- private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
- private final CertHolder mCertHolder;
-
- private AliasOperation(CertHolder certHolder) {
- mCertHolder = certHolder;
- mAliasOperation = this;
- }
-
- @Override
- protected Boolean doInBackground(Void... params) {
- try {
- synchronized (mKeyChainConnectionByProfileId) {
- KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
- mCertHolder.mProfileId);
- IKeyChainService service = keyChainConnection.getService();
- if (mCertHolder.mDeleted) {
- byte[] bytes = mCertHolder.mX509Cert.getEncoded();
- service.installCaCertificate(bytes);
- return true;
- } else {
- return service.deleteCaCertificate(mCertHolder.mAlias);
- }
- }
- } catch (CertificateEncodingException | SecurityException | IllegalStateException
- | RemoteException e) {
- Log.w(TAG, "Error while toggling alias " + mCertHolder.mAlias, e);
- return false;
- }
- }
-
- @Override
- protected void onPostExecute(Boolean ok) {
- if (ok) {
- if (mCertHolder.mTab.mSwitch) {
- mCertHolder.mDeleted = !mCertHolder.mDeleted;
- } else {
- mCertHolder.mAdapter.remove(mCertHolder);
- }
- mCertHolder.mAdapter.notifyDataSetChanged();
- } else {
- // bail, reload to reset to known state
- mCertHolder.mAdapter.load();
- }
- mAliasOperation = null;
- }
- }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index 36b5718..f0809ad 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -92,7 +92,7 @@
mProfilesContainer = (PreferenceCategory)screen.findPreference(getPreferenceKey());
mProfilesContainer.setLayoutResource(R.layout.preference_bluetooth_profile_category);
mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
- SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, false);
+ SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
// Call refresh here even though it will get called later in onResume, to avoid the
// list of switches appearing to "pop" into the page.
refresh();
@@ -515,4 +515,4 @@
public String getPreferenceKey() {
return KEY_PROFILES_GROUP;
}
-}
\ No newline at end of file
+}