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