• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Tab(String tag, int label, int view, int progress, int list, int expandableList, boolean withSwitch)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 
getAliases(IKeyChainService service)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         }
deleted(IKeyChainService service, String alias)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         }
getButtonLabel(CertHolder certHolder)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         }
getButtonConfirmation(CertHolder certHolder)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         }
postOperationUpdate(boolean ok, CertHolder certHolder)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
onCreate(Bundle savedInstanceState)173     public void onCreate(Bundle savedInstanceState) {
174         super.onCreate(savedInstanceState);
175         mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
176     }
177 
178 
onCreateView( LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)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
onDestroy()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 
closeKeyChainConnections()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 
addTab(Tab tab)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          */
remove(CertHolder certHolder)253         void remove(CertHolder certHolder);
254         /**
255          * Notify the adapter that the underlying data set has changed.
256          */
notifyDataSetChanged()257         void notifyDataSetChanged();
258         /**
259          * Load the certificates.
260          */
load()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          */
getListViewId(Tab tab)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 
TrustedCertificateExpandableAdapter(Tab tab)278         private TrustedCertificateExpandableAdapter(Tab tab) {
279             mData = new AdapterData(tab, this);
280             load();
281         }
282         @Override
remove(CertHolder certHolder)283         public void remove(CertHolder certHolder) {
284             mData.remove(certHolder);
285         }
286         @Override
getGroupCount()287         public int getGroupCount() {
288             return mData.mCertHoldersByUserId.size();
289         }
290         @Override
getChildrenCount(int groupPosition)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
getGroup(int groupPosition)299         public UserHandle getGroup(int groupPosition) {
300             return new UserHandle(mData.mCertHoldersByUserId.keyAt(groupPosition));
301         }
302         @Override
getChild(int groupPosition, int childPosition)303         public CertHolder getChild(int groupPosition, int childPosition) {
304             return mData.mCertHoldersByUserId.valueAt(groupPosition).get(childPosition);
305         }
306         @Override
getGroupId(int groupPosition)307         public long getGroupId(int groupPosition) {
308             return mData.mCertHoldersByUserId.keyAt(groupPosition);
309         }
310         @Override
getChildId(int groupPosition, int childPosition)311         public long getChildId(int groupPosition, int childPosition) {
312             return childPosition;
313         }
314         @Override
hasStableIds()315         public boolean hasStableIds() {
316             return false;
317         }
318         @Override
getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)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
getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)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
isChildSelectable(int groupPosition, int childPosition)346         public boolean isChildSelectable(int groupPosition, int childPosition) {
347             return true;
348         }
349         @Override
load()350         public void load() {
351             mData.new AliasLoader().execute();
352         }
353         @Override
getListViewId(Tab tab)354         public int getListViewId(Tab tab) {
355             return tab.mExpandableList;
356         }
inflateCategoryHeader(LayoutInflater inflater, ViewGroup parent)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;
TrustedCertificateAdapter(Tab tab)371         private TrustedCertificateAdapter(Tab tab) {
372             mData = new AdapterData(tab, this);
373             load();
374         }
375         @Override
remove(CertHolder certHolder)376         public void remove(CertHolder certHolder) {
377             mData.remove(certHolder);
378         }
379         @Override
getListViewId(Tab tab)380         public int getListViewId(Tab tab) {
381             return tab.mList;
382         }
383         @Override
load()384         public void load() {
385             mData.new AliasLoader().execute();
386         }
getCount()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         }
getItem(int position)394         @Override public CertHolder getItem(int position) {
395             return mData.mCertHoldersByUserId.valueAt(0).get(position);
396         }
getItemId(int position)397         @Override public long getItemId(int position) {
398             return position;
399         }
getView(int position, View view, ViewGroup parent)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 
AdapterData(Tab tab, TrustedCertificateAdapterCommons adapter)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 
AliasLoader()421             public AliasLoader() {
422                 mContext = getActivity();
423                 mAliasLoaders.put(mTab, this);
424             }
425 
onPreExecute()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             }
doInBackground(Void... params)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             }
onProgressUpdate(Integer... progressAndMax)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             }
onPostExecute(SparseArray<List<CertHolder>> certHolders)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 
remove(CertHolder certHolder)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 
CertHolder(IKeyChainService service, TrustedCertificateAdapterCommons adapter, Tab tab, String alias, X509Certificate x509Cert, int profileId)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         }
compareTo(CertHolder o)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         }
equals(Object o)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         }
hashCode()597         @Override public int hashCode() {
598             return mAlias.hashCode();
599         }
600     }
601 
getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView, ViewGroup parent)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 
showCertDialog(final CertHolder certHolder)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 
addCertChain(final CertHolder certHolder, final ArrayList<View> views, final ArrayList<String> titles)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 
addCertDetails(X509Certificate certificate, final ArrayList<View> views, final ArrayList<String> titles)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 
AliasOperation(CertHolder certHolder)753         private AliasOperation(CertHolder certHolder) {
754             mCertHolder = certHolder;
755             mAliasOperation = this;
756         }
757 
758         @Override
doInBackground(Void... params)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
onPostExecute(Boolean ok)780         protected void onPostExecute(Boolean ok) {
781             mCertHolder.mTab.postOperationUpdate(ok, mCertHolder);
782             mAliasOperation = null;
783         }
784     }
785 }
786