Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.Dialog;
     21 import android.app.Fragment;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.pm.UserInfo;
     25 import android.net.http.SslCertificate;
     26 import android.os.AsyncTask;
     27 import android.os.Bundle;
     28 import android.os.RemoteException;
     29 import android.os.UserHandle;
     30 import android.os.UserManager;
     31 import android.security.IKeyChainService;
     32 import android.security.KeyChain;
     33 import android.security.KeyChain.KeyChainConnection;
     34 import android.util.SparseArray;
     35 import android.util.Log;
     36 import android.view.LayoutInflater;
     37 import android.view.View;
     38 import android.view.ViewGroup;
     39 import android.widget.AdapterView;
     40 import android.widget.AdapterView.OnItemSelectedListener;
     41 import android.widget.ArrayAdapter;
     42 import android.widget.BaseAdapter;
     43 import android.widget.BaseExpandableListAdapter;
     44 import android.widget.Button;
     45 import android.widget.ExpandableListView;
     46 import android.widget.LinearLayout;
     47 import android.widget.ListView;
     48 import android.widget.ProgressBar;
     49 import android.widget.Spinner;
     50 import android.widget.Switch;
     51 import android.widget.TabHost;
     52 import android.widget.TextView;
     53 
     54 import com.android.internal.logging.MetricsLogger;
     55 import com.android.internal.util.ParcelableString;
     56 
     57 import java.security.cert.CertificateEncodingException;
     58 import java.security.cert.X509Certificate;
     59 import java.util.ArrayList;
     60 import java.util.Collections;
     61 import java.util.List;
     62 import java.util.HashMap;
     63 
     64 public class TrustedCredentialsSettings extends InstrumentedFragment {
     65 
     66     private static final String TAG = "TrustedCredentialsSettings";
     67 
     68     private UserManager mUserManager;
     69 
     70     private static final String USER_ACTION = "com.android.settings.TRUSTED_CREDENTIALS_USER";
     71 
     72     @Override
     73     protected int getMetricsCategory() {
     74         return MetricsLogger.TRUSTED_CREDENTIALS;
     75     }
     76 
     77     private enum Tab {
     78         SYSTEM("system",
     79                R.string.trusted_credentials_system_tab,
     80                R.id.system_tab,
     81                R.id.system_progress,
     82                R.id.system_list,
     83                R.id.system_expandable_list,
     84                true),
     85         USER("user",
     86              R.string.trusted_credentials_user_tab,
     87              R.id.user_tab,
     88              R.id.user_progress,
     89              R.id.user_list,
     90              R.id.user_expandable_list,
     91              false);
     92 
     93         private final String mTag;
     94         private final int mLabel;
     95         private final int mView;
     96         private final int mProgress;
     97         private final int mList;
     98         private final int mExpandableList;
     99         private final boolean mSwitch;
    100 
    101         private Tab(String tag, int label, int view, int progress, int list, int expandableList,
    102                 boolean withSwitch) {
    103             mTag = tag;
    104             mLabel = label;
    105             mView = view;
    106             mProgress = progress;
    107             mList = list;
    108             mExpandableList = expandableList;
    109             mSwitch = withSwitch;
    110         }
    111 
    112         private List<ParcelableString> getAliases(IKeyChainService service) throws RemoteException {
    113             switch (this) {
    114                 case SYSTEM: {
    115                     return service.getSystemCaAliases().getList();
    116                 }
    117                 case USER:
    118                     return service.getUserCaAliases().getList();
    119             }
    120             throw new AssertionError();
    121         }
    122         private boolean deleted(IKeyChainService service, String alias) throws RemoteException {
    123             switch (this) {
    124                 case SYSTEM:
    125                     return !service.containsCaAlias(alias);
    126                 case USER:
    127                     return false;
    128             }
    129             throw new AssertionError();
    130         }
    131         private int getButtonLabel(CertHolder certHolder) {
    132             switch (this) {
    133                 case SYSTEM:
    134                     if (certHolder.mDeleted) {
    135                         return R.string.trusted_credentials_enable_label;
    136                     }
    137                     return R.string.trusted_credentials_disable_label;
    138                 case USER:
    139                     return R.string.trusted_credentials_remove_label;
    140             }
    141             throw new AssertionError();
    142         }
    143         private int getButtonConfirmation(CertHolder certHolder) {
    144             switch (this) {
    145                 case SYSTEM:
    146                     if (certHolder.mDeleted) {
    147                         return R.string.trusted_credentials_enable_confirmation;
    148                     }
    149                     return R.string.trusted_credentials_disable_confirmation;
    150                 case USER:
    151                     return R.string.trusted_credentials_remove_confirmation;
    152             }
    153             throw new AssertionError();
    154         }
    155         private void postOperationUpdate(boolean ok, CertHolder certHolder) {
    156             if (ok) {
    157                 if (certHolder.mTab.mSwitch) {
    158                     certHolder.mDeleted = !certHolder.mDeleted;
    159                 } else {
    160                     certHolder.mAdapter.remove(certHolder);
    161                 }
    162                 certHolder.mAdapter.notifyDataSetChanged();
    163             } else {
    164                 // bail, reload to reset to known state
    165                 certHolder.mAdapter.load();
    166             }
    167         }
    168     }
    169 
    170     private TabHost mTabHost;
    171     private AliasOperation mAliasOperation;
    172     private HashMap<Tab, AdapterData.AliasLoader>
    173             mAliasLoaders = new HashMap<Tab, AdapterData.AliasLoader>(2);
    174     private final SparseArray<KeyChainConnection>
    175             mKeyChainConnectionByProfileId = new SparseArray<KeyChainConnection>();
    176 
    177     @Override
    178     public void onCreate(Bundle savedInstanceState) {
    179         super.onCreate(savedInstanceState);
    180         mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
    181     }
    182 
    183 
    184     @Override public View onCreateView(
    185             LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
    186         mTabHost = (TabHost) inflater.inflate(R.layout.trusted_credentials, parent, false);
    187         mTabHost.setup();
    188         addTab(Tab.SYSTEM);
    189         // TODO add Install button on Tab.USER to go to CertInstaller like KeyChainActivity
    190         addTab(Tab.USER);
    191         if (getActivity().getIntent() != null &&
    192                 USER_ACTION.equals(getActivity().getIntent().getAction())) {
    193             mTabHost.setCurrentTabByTag(Tab.USER.mTag);
    194         }
    195         return mTabHost;
    196     }
    197     @Override
    198     public void onDestroy() {
    199         for (AdapterData.AliasLoader aliasLoader : mAliasLoaders.values()) {
    200             aliasLoader.cancel(true);
    201         }
    202         if (mAliasOperation != null) {
    203             mAliasOperation.cancel(true);
    204             mAliasOperation = null;
    205         }
    206         closeKeyChainConnections();
    207         super.onDestroy();
    208     }
    209 
    210     private void closeKeyChainConnections() {
    211         final int n = mKeyChainConnectionByProfileId.size();
    212         for (int i = 0; i < n; ++i) {
    213             mKeyChainConnectionByProfileId.valueAt(i).close();
    214         }
    215         mKeyChainConnectionByProfileId.clear();
    216     }
    217 
    218     private void addTab(Tab tab) {
    219         TabHost.TabSpec systemSpec = mTabHost.newTabSpec(tab.mTag)
    220                 .setIndicator(getActivity().getString(tab.mLabel))
    221                 .setContent(tab.mView);
    222         mTabHost.addTab(systemSpec);
    223 
    224         if (mUserManager.getUserProfiles().size() > 1) {
    225             ExpandableListView lv = (ExpandableListView) mTabHost.findViewById(tab.mExpandableList);
    226             final TrustedCertificateExpandableAdapter adapter =
    227                     new TrustedCertificateExpandableAdapter(tab);
    228             lv.setAdapter(adapter);
    229             lv.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
    230                     @Override
    231                 public boolean onChildClick(ExpandableListView parent, View v,
    232                         int groupPosition, int childPosition, long id) {
    233                     showCertDialog(adapter.getChild(groupPosition, childPosition));
    234                     return true;
    235                 }
    236             });
    237         } else {
    238             ListView lv = (ListView) mTabHost.findViewById(tab.mList);
    239             final TrustedCertificateAdapter adapter = new TrustedCertificateAdapter(tab);
    240             lv.setAdapter(adapter);
    241             lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    242                 @Override public void onItemClick(AdapterView<?> parent, View view,
    243                         int pos, long id) {
    244                     showCertDialog(adapter.getItem(pos));
    245                 }
    246             });
    247         }
    248     }
    249 
    250     /**
    251      * Common interface for adapters of both expandable and non-expandable certificate lists.
    252      */
    253     private interface TrustedCertificateAdapterCommons {
    254         /**
    255          * Remove a certificate from the list.
    256          * @param certHolder the certificate to be removed.
    257          */
    258         void remove(CertHolder certHolder);
    259         /**
    260          * Notify the adapter that the underlying data set has changed.
    261          */
    262         void notifyDataSetChanged();
    263         /**
    264          * Load the certificates.
    265          */
    266         void load();
    267         /**
    268          * Gets the identifier of the list view the adapter is connected to.
    269          * @param tab the tab on which the list view resides.
    270          * @return identifier of the list view.
    271          */
    272         int getListViewId(Tab tab);
    273     }
    274 
    275     /**
    276      * Adapter for expandable list view of certificates. Groups in the view correspond to profiles
    277      * whereas children correspond to certificates.
    278      */
    279     private class TrustedCertificateExpandableAdapter extends BaseExpandableListAdapter implements
    280             TrustedCertificateAdapterCommons {
    281         private AdapterData mData;
    282 
    283         private TrustedCertificateExpandableAdapter(Tab tab) {
    284             mData = new AdapterData(tab, this);
    285             load();
    286         }
    287         @Override
    288         public void remove(CertHolder certHolder) {
    289             mData.remove(certHolder);
    290         }
    291         @Override
    292         public int getGroupCount() {
    293             return mData.mCertHoldersByUserId.size();
    294         }
    295         @Override
    296         public int getChildrenCount(int groupPosition) {
    297             List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(groupPosition);
    298             if (certHolders != null) {
    299                 return certHolders.size();
    300             }
    301             return 0;
    302         }
    303         @Override
    304         public UserHandle getGroup(int groupPosition) {
    305             return new UserHandle(mData.mCertHoldersByUserId.keyAt(groupPosition));
    306         }
    307         @Override
    308         public CertHolder getChild(int groupPosition, int childPosition) {
    309             return mData.mCertHoldersByUserId.valueAt(groupPosition).get(childPosition);
    310         }
    311         @Override
    312         public long getGroupId(int groupPosition) {
    313             return mData.mCertHoldersByUserId.keyAt(groupPosition);
    314         }
    315         @Override
    316         public long getChildId(int groupPosition, int childPosition) {
    317             return childPosition;
    318         }
    319         @Override
    320         public boolean hasStableIds() {
    321             return false;
    322         }
    323         @Override
    324         public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
    325                 ViewGroup parent) {
    326             if (convertView == null) {
    327                 LayoutInflater inflater = (LayoutInflater) getActivity()
    328                         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    329                 convertView = Utils.inflateCategoryHeader(inflater, parent);
    330             }
    331 
    332             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
    333             final UserHandle profile = getGroup(groupPosition);
    334             final UserInfo userInfo = mUserManager.getUserInfo(profile.getIdentifier());
    335             if (userInfo.isManagedProfile()) {
    336                 title.setText(R.string.category_work);
    337             } else {
    338                 title.setText(R.string.category_personal);
    339             }
    340             title.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
    341 
    342             return convertView;
    343         }
    344         @Override
    345         public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
    346                 View convertView, ViewGroup parent) {
    347             return getViewForCertificate(getChild(groupPosition, childPosition), mData.mTab,
    348                     convertView, parent);
    349         }
    350         @Override
    351         public boolean isChildSelectable(int groupPosition, int childPosition) {
    352             return true;
    353         }
    354         @Override
    355         public void load() {
    356             mData.new AliasLoader().execute();
    357         }
    358         @Override
    359         public int getListViewId(Tab tab) {
    360             return tab.mExpandableList;
    361         }
    362     }
    363 
    364     private class TrustedCertificateAdapter extends BaseAdapter implements
    365             TrustedCertificateAdapterCommons {
    366         private final AdapterData mData;
    367         private TrustedCertificateAdapter(Tab tab) {
    368             mData = new AdapterData(tab, this);
    369             load();
    370         }
    371         @Override
    372         public void remove(CertHolder certHolder) {
    373             mData.remove(certHolder);
    374         }
    375         @Override
    376         public int getListViewId(Tab tab) {
    377             return tab.mList;
    378         }
    379         @Override
    380         public void load() {
    381             mData.new AliasLoader().execute();
    382         }
    383         @Override public int getCount() {
    384             List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(0);
    385             if (certHolders != null) {
    386                 return certHolders.size();
    387             }
    388             return 0;
    389         }
    390         @Override public CertHolder getItem(int position) {
    391             return mData.mCertHoldersByUserId.valueAt(0).get(position);
    392         }
    393         @Override public long getItemId(int position) {
    394             return position;
    395         }
    396         @Override public View getView(int position, View view, ViewGroup parent) {
    397             return getViewForCertificate(getItem(position), mData.mTab, view, parent);
    398         }
    399     }
    400 
    401     private class AdapterData {
    402         private final SparseArray<List<CertHolder>> mCertHoldersByUserId =
    403                 new SparseArray<List<CertHolder>>();
    404         private final Tab mTab;
    405         private final TrustedCertificateAdapterCommons mAdapter;
    406 
    407         private AdapterData(Tab tab, TrustedCertificateAdapterCommons adapter) {
    408             mAdapter = adapter;
    409             mTab = tab;
    410         }
    411 
    412         private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> {
    413             private ProgressBar mProgressBar;
    414             private View mList;
    415             private Context mContext;
    416 
    417             public AliasLoader() {
    418                 mContext = getActivity();
    419                 mAliasLoaders.put(mTab, this);
    420             }
    421 
    422             @Override protected void onPreExecute() {
    423                 View content = mTabHost.getTabContentView();
    424                 mProgressBar = (ProgressBar) content.findViewById(mTab.mProgress);
    425                 mList = content.findViewById(mAdapter.getListViewId(mTab));
    426                 mProgressBar.setVisibility(View.VISIBLE);
    427                 mList.setVisibility(View.GONE);
    428             }
    429             @Override protected SparseArray<List<CertHolder>> doInBackground(Void... params) {
    430                 SparseArray<List<CertHolder>> certHoldersByProfile =
    431                         new SparseArray<List<CertHolder>>();
    432                 try {
    433                     List<UserHandle> profiles = mUserManager.getUserProfiles();
    434                     final int n = profiles.size();
    435                     // First we get all aliases for all profiles in order to show progress
    436                     // correctly. Otherwise this could all be in a single loop.
    437                     SparseArray<List<ParcelableString>> aliasesByProfileId = new SparseArray<
    438                             List<ParcelableString>>(n);
    439                     int max = 0;
    440                     int progress = 0;
    441                     for (int i = 0; i < n; ++i) {
    442                         UserHandle profile = profiles.get(i);
    443                         int profileId = profile.getIdentifier();
    444                         KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
    445                                 profile);
    446                         // Saving the connection for later use on the certificate dialog.
    447                         mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
    448                         IKeyChainService service = keyChainConnection.getService();
    449                         List<ParcelableString> aliases = mTab.getAliases(service);
    450                         if (isCancelled()) {
    451                             return new SparseArray<List<CertHolder>>();
    452                         }
    453                         max += aliases.size();
    454                         aliasesByProfileId.put(profileId, aliases);
    455                     }
    456                     for (int i = 0; i < n; ++i) {
    457                         UserHandle profile = profiles.get(i);
    458                         int profileId = profile.getIdentifier();
    459                         List<ParcelableString> aliases = aliasesByProfileId.get(profileId);
    460                         if (isCancelled()) {
    461                             return new SparseArray<List<CertHolder>>();
    462                         }
    463                         IKeyChainService service = mKeyChainConnectionByProfileId.get(profileId)
    464                                 .getService();
    465                         List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
    466                         final int aliasMax = aliases.size();
    467                         for (int j = 0; j < aliasMax; ++j) {
    468                             String alias = aliases.get(j).string;
    469                             byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
    470                                     true);
    471                             X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
    472                             certHolders.add(new CertHolder(service, mAdapter,
    473                                     mTab, alias, cert, profileId));
    474                             publishProgress(++progress, max);
    475                         }
    476                         Collections.sort(certHolders);
    477                         certHoldersByProfile.put(profileId, certHolders);
    478                     }
    479                     return certHoldersByProfile;
    480                 } catch (RemoteException e) {
    481                     Log.e(TAG, "Remote exception while loading aliases.", e);
    482                     return new SparseArray<List<CertHolder>>();
    483                 } catch (InterruptedException e) {
    484                     Log.e(TAG, "InterruptedException while loading aliases.", e);
    485                     return new SparseArray<List<CertHolder>>();
    486                 }
    487             }
    488             @Override protected void onProgressUpdate(Integer... progressAndMax) {
    489                 int progress = progressAndMax[0];
    490                 int max = progressAndMax[1];
    491                 if (max != mProgressBar.getMax()) {
    492                     mProgressBar.setMax(max);
    493                 }
    494                 mProgressBar.setProgress(progress);
    495             }
    496             @Override protected void onPostExecute(SparseArray<List<CertHolder>> certHolders) {
    497                 mCertHoldersByUserId.clear();
    498                 final int n = certHolders.size();
    499                 for (int i = 0; i < n; ++i) {
    500                     mCertHoldersByUserId.put(certHolders.keyAt(i), certHolders.valueAt(i));
    501                 }
    502                 mAdapter.notifyDataSetChanged();
    503                 mProgressBar.setVisibility(View.GONE);
    504                 mList.setVisibility(View.VISIBLE);
    505                 mProgressBar.setProgress(0);
    506                 mAliasLoaders.remove(mTab);
    507             }
    508         }
    509 
    510         public void remove(CertHolder certHolder) {
    511             if (mCertHoldersByUserId != null) {
    512                 final List<CertHolder> certs = mCertHoldersByUserId.get(certHolder.mProfileId);
    513                 if (certs != null) {
    514                     certs.remove(certHolder);
    515                 }
    516             }
    517         }
    518     }
    519 
    520     private static class CertHolder implements Comparable<CertHolder> {
    521         public int mProfileId;
    522         private final IKeyChainService mService;
    523         private final TrustedCertificateAdapterCommons mAdapter;
    524         private final Tab mTab;
    525         private final String mAlias;
    526         private final X509Certificate mX509Cert;
    527 
    528         private final SslCertificate mSslCert;
    529         private final String mSubjectPrimary;
    530         private final String mSubjectSecondary;
    531         private boolean mDeleted;
    532 
    533         private CertHolder(IKeyChainService service,
    534                            TrustedCertificateAdapterCommons adapter,
    535                            Tab tab,
    536                            String alias,
    537                            X509Certificate x509Cert,
    538                            int profileId) {
    539             mProfileId = profileId;
    540             mService = service;
    541             mAdapter = adapter;
    542             mTab = tab;
    543             mAlias = alias;
    544             mX509Cert = x509Cert;
    545 
    546             mSslCert = new SslCertificate(x509Cert);
    547 
    548             String cn = mSslCert.getIssuedTo().getCName();
    549             String o = mSslCert.getIssuedTo().getOName();
    550             String ou = mSslCert.getIssuedTo().getUName();
    551             // if we have a O, use O as primary subject, secondary prefer CN over OU
    552             // if we don't have an O, use CN as primary, empty secondary
    553             // if we don't have O or CN, use DName as primary, empty secondary
    554             if (!o.isEmpty()) {
    555                 if (!cn.isEmpty()) {
    556                     mSubjectPrimary = o;
    557                     mSubjectSecondary = cn;
    558                 } else {
    559                     mSubjectPrimary = o;
    560                     mSubjectSecondary = ou;
    561                 }
    562             } else {
    563                 if (!cn.isEmpty()) {
    564                     mSubjectPrimary = cn;
    565                     mSubjectSecondary = "";
    566                 } else {
    567                     mSubjectPrimary = mSslCert.getIssuedTo().getDName();
    568                     mSubjectSecondary = "";
    569                 }
    570             }
    571             try {
    572                 mDeleted = mTab.deleted(mService, mAlias);
    573             } catch (RemoteException e) {
    574                 Log.e(TAG, "Remote exception while checking if alias " + mAlias + " is deleted.",
    575                         e);
    576                 mDeleted = false;
    577             }
    578         }
    579         @Override public int compareTo(CertHolder o) {
    580             int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary);
    581             if (primary != 0) {
    582                 return primary;
    583             }
    584             return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary);
    585         }
    586         @Override public boolean equals(Object o) {
    587             if (!(o instanceof CertHolder)) {
    588                 return false;
    589             }
    590             CertHolder other = (CertHolder) o;
    591             return mAlias.equals(other.mAlias);
    592         }
    593         @Override public int hashCode() {
    594             return mAlias.hashCode();
    595         }
    596     }
    597 
    598     private View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView,
    599             ViewGroup parent) {
    600         ViewHolder holder;
    601         if (convertView == null) {
    602             LayoutInflater inflater = LayoutInflater.from(getActivity());
    603             convertView = inflater.inflate(R.layout.trusted_credential, parent, false);
    604             holder = new ViewHolder();
    605             holder.mSubjectPrimaryView = (TextView)
    606                     convertView.findViewById(R.id.trusted_credential_subject_primary);
    607             holder.mSubjectSecondaryView = (TextView)
    608                     convertView.findViewById(R.id.trusted_credential_subject_secondary);
    609             holder.mSwitch = (Switch) convertView.findViewById(
    610                     R.id.trusted_credential_status);
    611             convertView.setTag(holder);
    612         } else {
    613             holder = (ViewHolder) convertView.getTag();
    614         }
    615         holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary);
    616         holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary);
    617         if (mTab.mSwitch) {
    618             holder.mSwitch.setChecked(!certHolder.mDeleted);
    619             holder.mSwitch.setEnabled(!mUserManager.hasUserRestriction(
    620                     UserManager.DISALLOW_CONFIG_CREDENTIALS,
    621                     new UserHandle(certHolder.mProfileId)));
    622             holder.mSwitch.setVisibility(View.VISIBLE);
    623         }
    624         return convertView;
    625     }
    626 
    627     private static class ViewHolder {
    628         private TextView mSubjectPrimaryView;
    629         private TextView mSubjectSecondaryView;
    630         private Switch mSwitch;
    631     }
    632 
    633     private void showCertDialog(final CertHolder certHolder) {
    634         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    635         builder.setTitle(com.android.internal.R.string.ssl_certificate);
    636 
    637         final ArrayList<View> views =  new ArrayList<View>();
    638         final ArrayList<String> titles = new ArrayList<String>();
    639         addCertChain(certHolder, views, titles);
    640 
    641         ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(),
    642                 android.R.layout.simple_spinner_item,
    643                 titles);
    644         arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    645         Spinner spinner = new Spinner(getActivity());
    646         spinner.setAdapter(arrayAdapter);
    647         spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    648                 @Override
    649                 public void onItemSelected(AdapterView<?> parent, View view, int position,
    650                         long id) {
    651                     for(int i = 0; i < views.size(); i++) {
    652                         views.get(i).setVisibility(i == position ? View.VISIBLE : View.GONE);
    653                     }
    654                 }
    655                @Override
    656                public void onNothingSelected(AdapterView<?> parent) { }
    657             });
    658 
    659         LinearLayout container = new LinearLayout(getActivity());
    660         container.setOrientation(LinearLayout.VERTICAL);
    661         container.addView(spinner);
    662         for (int i = 0; i < views.size(); ++i) {
    663             View certificateView = views.get(i);
    664             if (i != 0) {
    665                 certificateView.setVisibility(View.GONE);
    666             }
    667             container.addView(certificateView);
    668         }
    669         builder.setView(container);
    670         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    671             @Override public void onClick(DialogInterface dialog, int id) {
    672                 dialog.dismiss();
    673             }
    674         });
    675         final Dialog certDialog = builder.create();
    676 
    677         ViewGroup body = (ViewGroup) container.findViewById(com.android.internal.R.id.body);
    678         LayoutInflater inflater = LayoutInflater.from(getActivity());
    679         Button removeButton = (Button) inflater.inflate(R.layout.trusted_credential_details,
    680                                                         body,
    681                                                         false);
    682         if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS,
    683                 new UserHandle(certHolder.mProfileId))) {
    684             body.addView(removeButton);
    685         }
    686         removeButton.setText(certHolder.mTab.getButtonLabel(certHolder));
    687         removeButton.setOnClickListener(new View.OnClickListener() {
    688             @Override public void onClick(View v) {
    689                 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    690                 builder.setMessage(certHolder.mTab.getButtonConfirmation(certHolder));
    691                 builder.setPositiveButton(
    692                         android.R.string.yes, new DialogInterface.OnClickListener() {
    693                     @Override public void onClick(DialogInterface dialog, int id) {
    694                         new AliasOperation(certHolder).execute();
    695                         dialog.dismiss();
    696                         certDialog.dismiss();
    697                     }
    698                 });
    699                 builder.setNegativeButton(
    700                         android.R.string.no, new DialogInterface.OnClickListener() {
    701                     @Override public void onClick(DialogInterface dialog, int id) {
    702                         dialog.cancel();
    703                     }
    704                 });
    705                 AlertDialog alert = builder.create();
    706                 alert.show();
    707             }
    708         });
    709 
    710         certDialog.show();
    711     }
    712 
    713     private void addCertChain(final CertHolder certHolder,
    714             final ArrayList<View> views, final ArrayList<String> titles) {
    715 
    716         List<X509Certificate> certificates = null;
    717         try {
    718             KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
    719                     certHolder.mProfileId);
    720             IKeyChainService service = keyChainConnection.getService();
    721             List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
    722             final int n = chain.size();
    723             certificates = new ArrayList<X509Certificate>(n);
    724             for (int i = 0; i < n; ++i) {
    725                 byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
    726                 X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
    727                 certificates.add(certificate);
    728             }
    729         } catch (RemoteException ex) {
    730             Log.e(TAG, "RemoteException while retrieving certificate chain for root "
    731                     + certHolder.mAlias, ex);
    732             return;
    733         }
    734         for (X509Certificate certificate : certificates) {
    735             addCertDetails(certificate, views, titles);
    736         }
    737     }
    738 
    739     private void addCertDetails(X509Certificate certificate, final ArrayList<View> views,
    740             final ArrayList<String> titles) {
    741         SslCertificate sslCert = new SslCertificate(certificate);
    742         views.add(sslCert.inflateCertificateView(getActivity()));
    743         titles.add(sslCert.getIssuedTo().getCName());
    744     }
    745 
    746     private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
    747         private final CertHolder mCertHolder;
    748 
    749         private AliasOperation(CertHolder certHolder) {
    750             mCertHolder = certHolder;
    751             mAliasOperation = this;
    752         }
    753 
    754         @Override
    755         protected Boolean doInBackground(Void... params) {
    756             try {
    757                 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
    758                         mCertHolder.mProfileId);
    759                 IKeyChainService service = keyChainConnection.getService();
    760                 if (mCertHolder.mDeleted) {
    761                     byte[] bytes = mCertHolder.mX509Cert.getEncoded();
    762                     service.installCaCertificate(bytes);
    763                     return true;
    764                 } else {
    765                     return service.deleteCaCertificate(mCertHolder.mAlias);
    766                 }
    767             } catch (CertificateEncodingException | SecurityException | IllegalStateException
    768                     | RemoteException e) {
    769                 Log.w(TAG, "Error while toggling alias " + mCertHolder.mAlias,
    770                         e);
    771                 return false;
    772             }
    773         }
    774 
    775         @Override
    776         protected void onPostExecute(Boolean ok) {
    777             mCertHolder.mTab.postOperationUpdate(ok, mCertHolder);
    778             mAliasOperation = null;
    779         }
    780     }
    781 }
    782