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