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