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