OSDN Git Service

am 4fedc0fd: (-s ours) Import translations. DO NOT MERGE
[android-x86/packages-apps-Settings.git] / src / com / android / settings / TrustedCredentialsSettings.java
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 }