| /* | |
| * Copyright (C) 2011 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 android.annotation.UiThread; | |
| import android.app.KeyguardManager; | |
| import android.app.admin.DevicePolicyManager; | |
| 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.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.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.ListView; | |
| import android.widget.ProgressBar; | |
| import android.widget.Switch; | |
| import android.widget.TabHost; | |
| import android.widget.TextView; | |
| import com.android.internal.app.UnlaunchableAppActivity; | |
| import com.android.internal.logging.MetricsProto.MetricsEvent; | |
| import com.android.internal.util.ParcelableString; | |
| import com.android.internal.widget.LockPatternUtils; | |
| import java.security.cert.CertificateEncodingException; | |
| import java.security.cert.X509Certificate; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| public class TrustedCredentialsSettings extends OptionsMenuFragment | |
| implements TrustedCredentialsDialogBuilder.DelegateInterface { | |
| public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER"; | |
| private static final String TAG = "TrustedCredentialsSettings"; | |
| private UserManager mUserManager; | |
| private KeyguardManager mKeyguardManager; | |
| private int mTrustAllCaUserId; | |
| private static final String USER_ACTION = "com.android.settings.TRUSTED_CREDENTIALS_USER"; | |
| @Override | |
| protected int getMetricsCategory() { | |
| return MetricsEvent.TRUSTED_CREDENTIALS; | |
| } | |
| private enum Tab { | |
| SYSTEM("system", | |
| R.string.trusted_credentials_system_tab, | |
| R.id.system_tab, | |
| R.id.system_progress, | |
| R.id.system_list, | |
| R.id.system_expandable_list, | |
| true), | |
| USER("user", | |
| R.string.trusted_credentials_user_tab, | |
| R.id.user_tab, | |
| R.id.user_progress, | |
| R.id.user_list, | |
| R.id.user_expandable_list, | |
| false); | |
| private final String mTag; | |
| private final int mLabel; | |
| private final int mView; | |
| private final int mProgress; | |
| private final int mList; | |
| private final int mExpandableList; | |
| private final boolean mSwitch; | |
| private Tab(String tag, int label, int view, int progress, int list, int expandableList, | |
| boolean withSwitch) { | |
| mTag = tag; | |
| mLabel = label; | |
| mView = view; | |
| mProgress = progress; | |
| mList = list; | |
| mExpandableList = expandableList; | |
| mSwitch = withSwitch; | |
| } | |
| private List<ParcelableString> getAliases(IKeyChainService service) throws RemoteException { | |
| switch (this) { | |
| case SYSTEM: { | |
| return service.getSystemCaAliases().getList(); | |
| } | |
| case USER: | |
| return service.getUserCaAliases().getList(); | |
| } | |
| throw new AssertionError(); | |
| } | |
| private boolean deleted(IKeyChainService service, String alias) throws RemoteException { | |
| switch (this) { | |
| case SYSTEM: | |
| return !service.containsCaAlias(alias); | |
| case USER: | |
| return false; | |
| } | |
| throw new AssertionError(); | |
| } | |
| private void postOperationUpdate(boolean ok, CertHolder certHolder) { | |
| if (ok) { | |
| if (certHolder.mTab.mSwitch) { | |
| certHolder.mDeleted = !certHolder.mDeleted; | |
| } else { | |
| certHolder.mAdapter.remove(certHolder); | |
| } | |
| certHolder.mAdapter.notifyDataSetChanged(); | |
| } else { | |
| // bail, reload to reset to known state | |
| certHolder.mAdapter.load(); | |
| } | |
| } | |
| } | |
| private TabHost mTabHost; | |
| private AliasOperation mAliasOperation; | |
| private HashMap<Tab, AdapterData.AliasLoader> | |
| mAliasLoaders = new HashMap<Tab, AdapterData.AliasLoader>(2); | |
| 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)) { | |
| // Reload all alias | |
| final ExpandableListView systemView = (ExpandableListView) mTabHost | |
| .findViewById(Tab.SYSTEM.mExpandableList); | |
| if (systemView != null) { | |
| ((TrustedCertificateExpandableAdapter) systemView.getExpandableListAdapter()) | |
| .load(); | |
| } | |
| final ExpandableListView userView = (ExpandableListView) mTabHost | |
| .findViewById(Tab.USER.mExpandableList); | |
| if (userView != null) { | |
| ((TrustedCertificateExpandableAdapter) userView.getExpandableListAdapter()) | |
| .load(); | |
| } | |
| } | |
| } | |
| }; | |
| @Override | |
| public void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); | |
| mKeyguardManager = (KeyguardManager) getActivity() | |
| .getSystemService(Context.KEYGUARD_SERVICE); | |
| mTrustAllCaUserId = getActivity().getIntent().getIntExtra(ARG_SHOW_NEW_FOR_USER, | |
| UserHandle.USER_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); | |
| getActivity().registerReceiver(mWorkProfileChangedReceiver, filter); | |
| } | |
| @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.values()) { | |
| aliasLoader.cancel(true); | |
| } | |
| if (mAliasOperation != null) { | |
| mAliasOperation.cancel(true); | |
| mAliasOperation = null; | |
| } | |
| closeKeyChainConnections(); | |
| super.onDestroy(); | |
| } | |
| private void closeKeyChainConnections() { | |
| 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); | |
| if (mUserManager.getUserProfiles().size() > 1) { | |
| ExpandableListView lv = (ExpandableListView) mTabHost.findViewById(tab.mExpandableList); | |
| final TrustedCertificateExpandableAdapter adapter = | |
| new TrustedCertificateExpandableAdapter(tab); | |
| lv.setAdapter(adapter); | |
| lv.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { | |
| @Override | |
| public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, | |
| long id) { | |
| final UserHandle groupUser = adapter.getGroup(groupPosition); | |
| final int groupUserId = groupUser.getIdentifier(); | |
| if (mUserManager.isQuietModeEnabled(groupUser)) { | |
| final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent( | |
| groupUserId); | |
| getActivity().startActivity(intent); | |
| return true; | |
| } else if (!mUserManager.isUserUnlocked(groupUser)) { | |
| final LockPatternUtils lockPatternUtils = new LockPatternUtils( | |
| getActivity()); | |
| if (lockPatternUtils.isSeparateProfileChallengeEnabled(groupUserId)) { | |
| startWorkChallenge(groupUserId); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| }); | |
| lv.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { | |
| @Override | |
| public boolean onChildClick(ExpandableListView parent, View v, | |
| int groupPosition, int childPosition, long id) { | |
| showCertDialog(adapter.getChild(groupPosition, childPosition)); | |
| return true; | |
| } | |
| }); | |
| } else { | |
| ListView lv = (ListView) mTabHost.findViewById(tab.mList); | |
| final TrustedCertificateAdapter adapter = new TrustedCertificateAdapter(tab); | |
| lv.setAdapter(adapter); | |
| lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { | |
| @Override public void onItemClick(AdapterView<?> parent, View view, | |
| int pos, long id) { | |
| showCertDialog(adapter.getItem(pos)); | |
| } | |
| }); | |
| } | |
| } | |
| /** | |
| * Start work challenge activity. TODO: Move and refactor this method as a util function. | |
| */ | |
| private void startWorkChallenge(int userId) { | |
| final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null, | |
| userId); | |
| newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | |
| | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |
| getActivity().startActivity(newIntent); | |
| } | |
| /** | |
| * Common interface for adapters of both expandable and non-expandable certificate lists. | |
| */ | |
| private interface TrustedCertificateAdapterCommons { | |
| /** | |
| * Remove a certificate from the list. | |
| * @param certHolder the certificate to be removed. | |
| */ | |
| void remove(CertHolder certHolder); | |
| /** | |
| * Notify the adapter that the underlying data set has changed. | |
| */ | |
| void notifyDataSetChanged(); | |
| /** | |
| * Load the certificates. | |
| */ | |
| void load(); | |
| /** | |
| * Gets the identifier of the list view the adapter is connected to. | |
| * @param tab the tab on which the list view resides. | |
| * @return identifier of the list view. | |
| */ | |
| int getListViewId(Tab tab); | |
| } | |
| /** | |
| * Adapter for expandable list view of certificates. Groups in the view correspond to profiles | |
| * whereas children correspond to certificates. | |
| */ | |
| private class TrustedCertificateExpandableAdapter extends BaseExpandableListAdapter implements | |
| TrustedCertificateAdapterCommons { | |
| private AdapterData mData; | |
| private TrustedCertificateExpandableAdapter(Tab tab) { | |
| mData = new AdapterData(tab, this); | |
| load(); | |
| } | |
| @Override | |
| public void remove(CertHolder certHolder) { | |
| mData.remove(certHolder); | |
| } | |
| @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.valueAt(groupPosition).get(childPosition); | |
| } | |
| @Override | |
| public long getGroupId(int groupPosition) { | |
| return mData.mCertHoldersByUserId.keyAt(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); | |
| final UserHandle profile = getGroup(groupPosition); | |
| final UserInfo userInfo = mUserManager.getUserInfo(profile.getIdentifier()); | |
| if (userInfo.isManagedProfile()) { | |
| title.setText(R.string.category_work); | |
| } else { | |
| title.setText(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 void load() { | |
| mData.new AliasLoader().execute(); | |
| } | |
| @Override | |
| public int getListViewId(Tab tab) { | |
| return tab.mExpandableList; | |
| } | |
| } | |
| private class TrustedCertificateAdapter extends BaseAdapter implements | |
| TrustedCertificateAdapterCommons { | |
| private final AdapterData mData; | |
| private TrustedCertificateAdapter(Tab tab) { | |
| mData = new AdapterData(tab, this); | |
| load(); | |
| } | |
| @Override | |
| public void remove(CertHolder certHolder) { | |
| mData.remove(certHolder); | |
| } | |
| @Override | |
| public int getListViewId(Tab tab) { | |
| return tab.mList; | |
| } | |
| @Override | |
| public void load() { | |
| mData.new AliasLoader().execute(); | |
| } | |
| @Override public int getCount() { | |
| List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(0); | |
| if (certHolders != null) { | |
| return certHolders.size(); | |
| } | |
| return 0; | |
| } | |
| @Override public CertHolder getItem(int position) { | |
| return mData.mCertHoldersByUserId.valueAt(0).get(position); | |
| } | |
| @Override public long getItemId(int position) { | |
| return position; | |
| } | |
| @Override public View getView(int position, View view, ViewGroup parent) { | |
| return getViewForCertificate(getItem(position), mData.mTab, view, parent); | |
| } | |
| } | |
| private class AdapterData { | |
| private final SparseArray<List<CertHolder>> mCertHoldersByUserId = | |
| new SparseArray<List<CertHolder>>(); | |
| private final Tab mTab; | |
| private final TrustedCertificateAdapterCommons mAdapter; | |
| private AdapterData(Tab tab, TrustedCertificateAdapterCommons adapter) { | |
| mAdapter = adapter; | |
| mTab = tab; | |
| } | |
| private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> { | |
| private ProgressBar mProgressBar; | |
| private View mList; | |
| private Context mContext; | |
| public AliasLoader() { | |
| mContext = getActivity(); | |
| mAliasLoaders.put(mTab, this); | |
| } | |
| 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); | |
| mList = content.findViewById(mAdapter.getListViewId(mTab)); | |
| mProgressBar.setVisibility(View.VISIBLE); | |
| mList.setVisibility(View.GONE); | |
| } | |
| @Override protected SparseArray<List<CertHolder>> doInBackground(Void... params) { | |
| SparseArray<List<CertHolder>> certHoldersByProfile = | |
| new SparseArray<List<CertHolder>>(); | |
| try { | |
| 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<ParcelableString>> aliasesByProfileId = new SparseArray< | |
| List<ParcelableString>>(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<ParcelableString> 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(); | |
| if (shouldSkipProfile(profile)) { | |
| certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0)); | |
| continue; | |
| } | |
| List<ParcelableString> aliases = aliasesByProfileId.get(profileId); | |
| if (isCancelled()) { | |
| return new SparseArray<List<CertHolder>>(); | |
| } | |
| IKeyChainService service = mKeyChainConnectionByProfileId.get(profileId) | |
| .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).string; | |
| 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); | |
| mList.setVisibility(View.VISIBLE); | |
| mProgressBar.setProgress(0); | |
| mAliasLoaders.remove(mTab); | |
| 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 TrustedCertificateAdapterCommons 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, | |
| TrustedCertificateAdapterCommons 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 View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView, | |
| ViewGroup parent) { | |
| ViewHolder holder; | |
| if (convertView == null) { | |
| LayoutInflater inflater = LayoutInflater.from(getActivity()); | |
| convertView = inflater.inflate(R.layout.trusted_credential, parent, false); | |
| holder = new ViewHolder(); | |
| 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); | |
| convertView.setTag(holder); | |
| } 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); | |
| } | |
| return convertView; | |
| } | |
| private static class ViewHolder { | |
| private TextView mSubjectPrimaryView; | |
| private TextView mSubjectSecondaryView; | |
| private Switch mSwitch; | |
| } | |
| 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 { | |
| 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(); | |
| } | |
| 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 { | |
| 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) { | |
| mCertHolder.mTab.postOperationUpdate(ok, mCertHolder); | |
| mAliasOperation = null; | |
| } | |
| } | |
| } |