Home | History | Annotate | Download | only in settings
      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.annotation.UiThread;
     20 import android.app.Activity;
     21 import android.app.KeyguardManager;
     22 import android.app.admin.DevicePolicyManager;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.UserInfo;
     29 import android.content.res.TypedArray;
     30 import android.database.DataSetObserver;
     31 import android.graphics.drawable.Drawable;
     32 import android.net.http.SslCertificate;
     33 import android.os.AsyncTask;
     34 import android.os.Bundle;
     35 import android.os.RemoteException;
     36 import android.os.UserHandle;
     37 import android.os.UserManager;
     38 import android.security.IKeyChainService;
     39 import android.security.KeyChain;
     40 import android.security.KeyChain.KeyChainConnection;
     41 import android.util.Log;
     42 import android.util.SparseArray;
     43 import android.util.ArraySet;
     44 import android.view.LayoutInflater;
     45 import android.view.View;
     46 import android.view.ViewGroup;
     47 import android.widget.AdapterView;
     48 import android.widget.BaseAdapter;
     49 import android.widget.BaseExpandableListAdapter;
     50 import android.widget.ExpandableListView;
     51 import android.widget.FrameLayout;
     52 import android.widget.ImageView;
     53 import android.widget.LinearLayout;
     54 import android.widget.ListView;
     55 import android.widget.ProgressBar;
     56 import android.widget.Switch;
     57 import android.widget.TabHost;
     58 import android.widget.TextView;
     59 
     60 import com.android.internal.app.UnlaunchableAppActivity;
     61 import com.android.internal.logging.MetricsProto.MetricsEvent;
     62 import com.android.internal.util.ParcelableString;
     63 import com.android.internal.widget.LockPatternUtils;
     64 
     65 import java.security.cert.CertificateEncodingException;
     66 import java.security.cert.X509Certificate;
     67 import java.util.ArrayList;
     68 import java.util.Collections;
     69 import java.util.List;
     70 import java.util.Set;
     71 import java.util.function.IntConsumer;
     72 
     73 public class TrustedCredentialsSettings extends OptionsMenuFragment
     74         implements TrustedCredentialsDialogBuilder.DelegateInterface {
     75 
     76     public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
     77 
     78     private static final String TAG = "TrustedCredentialsSettings";
     79 
     80     private UserManager mUserManager;
     81     private KeyguardManager mKeyguardManager;
     82     private int mTrustAllCaUserId;
     83 
     84     private static final String SAVED_CONFIRMED_CREDENTIAL_USERS = "ConfirmedCredentialUsers";
     85     private static final String SAVED_CONFIRMING_CREDENTIAL_USER = "ConfirmingCredentialUser";
     86     private static final String USER_ACTION = "com.android.settings.TRUSTED_CREDENTIALS_USER";
     87     private static final int REQUEST_CONFIRM_CREDENTIALS = 1;
     88 
     89     @Override
     90     protected int getMetricsCategory() {
     91         return MetricsEvent.TRUSTED_CREDENTIALS;
     92     }
     93 
     94     private enum Tab {
     95         SYSTEM("system",
     96                 R.string.trusted_credentials_system_tab,
     97                 R.id.system_tab,
     98                 R.id.system_progress,
     99                 R.id.system_personal_container,
    100                 R.id.system_work_container,
    101                 R.id.system_expandable_list,
    102                 R.id.system_content,
    103                true),
    104         USER("user",
    105                 R.string.trusted_credentials_user_tab,
    106                 R.id.user_tab,
    107                 R.id.user_progress,
    108                 R.id.user_personal_container,
    109                 R.id.user_work_container,
    110                 R.id.user_expandable_list,
    111                 R.id.user_content,
    112                 false);
    113 
    114         private final String mTag;
    115         private final int mLabel;
    116         private final int mView;
    117         private final int mProgress;
    118         private final int mPersonalList;
    119         private final int mWorkList;
    120         private final int mExpandableList;
    121         private final int mContentView;
    122         private final boolean mSwitch;
    123 
    124         private Tab(String tag, int label, int view, int progress, int personalList, int workList,
    125                 int expandableList, int contentView, boolean withSwitch) {
    126             mTag = tag;
    127             mLabel = label;
    128             mView = view;
    129             mProgress = progress;
    130             mPersonalList = personalList;
    131             mWorkList = workList;
    132             mExpandableList = expandableList;
    133             mContentView = contentView;
    134             mSwitch = withSwitch;
    135         }
    136 
    137         private List<ParcelableString> getAliases(IKeyChainService service) throws RemoteException {
    138             switch (this) {
    139                 case SYSTEM: {
    140                     return service.getSystemCaAliases().getList();
    141                 }
    142                 case USER:
    143                     return service.getUserCaAliases().getList();
    144             }
    145             throw new AssertionError();
    146         }
    147         private boolean deleted(IKeyChainService service, String alias) throws RemoteException {
    148             switch (this) {
    149                 case SYSTEM:
    150                     return !service.containsCaAlias(alias);
    151                 case USER:
    152                     return false;
    153             }
    154             throw new AssertionError();
    155         }
    156     }
    157 
    158     private TabHost mTabHost;
    159     private ArrayList<GroupAdapter> mGroupAdapters = new ArrayList<>(2);
    160     private AliasOperation mAliasOperation;
    161     private ArraySet<Integer> mConfirmedCredentialUsers;
    162     private int mConfirmingCredentialUser;
    163     private IntConsumer mConfirmingCredentialListener;
    164     private Set<AdapterData.AliasLoader> mAliasLoaders = new ArraySet<AdapterData.AliasLoader>(2);
    165     private final SparseArray<KeyChainConnection>
    166             mKeyChainConnectionByProfileId = new SparseArray<KeyChainConnection>();
    167 
    168     private BroadcastReceiver mWorkProfileChangedReceiver = new BroadcastReceiver() {
    169 
    170         @Override
    171         public void onReceive(Context context, Intent intent) {
    172             final String action = intent.getAction();
    173             if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
    174                     Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
    175                     Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
    176                 for (GroupAdapter adapter : mGroupAdapters) {
    177                     adapter.load();
    178                 }
    179             }
    180         }
    181 
    182     };
    183 
    184     @Override
    185     public void onCreate(Bundle savedInstanceState) {
    186         super.onCreate(savedInstanceState);
    187         mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
    188         mKeyguardManager = (KeyguardManager) getActivity()
    189                 .getSystemService(Context.KEYGUARD_SERVICE);
    190         mTrustAllCaUserId = getActivity().getIntent().getIntExtra(ARG_SHOW_NEW_FOR_USER,
    191                 UserHandle.USER_NULL);
    192         mConfirmedCredentialUsers = new ArraySet<>(2);
    193         mConfirmingCredentialUser = UserHandle.USER_NULL;
    194         if (savedInstanceState != null) {
    195             mConfirmingCredentialUser = savedInstanceState.getInt(SAVED_CONFIRMING_CREDENTIAL_USER,
    196                     UserHandle.USER_NULL);
    197             ArrayList<Integer> users = savedInstanceState.getIntegerArrayList(
    198                     SAVED_CONFIRMED_CREDENTIAL_USERS);
    199             if (users != null) {
    200                 mConfirmedCredentialUsers.addAll(users);
    201             }
    202         }
    203 
    204         mConfirmingCredentialListener = null;
    205 
    206         IntentFilter filter = new IntentFilter();
    207         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
    208         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
    209         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
    210         getActivity().registerReceiver(mWorkProfileChangedReceiver, filter);
    211     }
    212 
    213     @Override
    214     public void onSaveInstanceState(Bundle outState) {
    215         super.onSaveInstanceState(outState);
    216         outState.putIntegerArrayList(SAVED_CONFIRMED_CREDENTIAL_USERS, new ArrayList<>(
    217                 mConfirmedCredentialUsers));
    218         outState.putInt(SAVED_CONFIRMING_CREDENTIAL_USER, mConfirmingCredentialUser);
    219     }
    220 
    221     @Override public View onCreateView(
    222             LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
    223         mTabHost = (TabHost) inflater.inflate(R.layout.trusted_credentials, parent, false);
    224         mTabHost.setup();
    225         addTab(Tab.SYSTEM);
    226         // TODO add Install button on Tab.USER to go to CertInstaller like KeyChainActivity
    227         addTab(Tab.USER);
    228         if (getActivity().getIntent() != null &&
    229                 USER_ACTION.equals(getActivity().getIntent().getAction())) {
    230             mTabHost.setCurrentTabByTag(Tab.USER.mTag);
    231         }
    232         return mTabHost;
    233     }
    234     @Override
    235     public void onDestroy() {
    236         getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
    237         for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
    238             aliasLoader.cancel(true);
    239         }
    240         mAliasLoaders.clear();
    241         mGroupAdapters.clear();
    242         if (mAliasOperation != null) {
    243             mAliasOperation.cancel(true);
    244             mAliasOperation = null;
    245         }
    246         closeKeyChainConnections();
    247         super.onDestroy();
    248     }
    249 
    250     @Override
    251     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    252         if (requestCode == REQUEST_CONFIRM_CREDENTIALS) {
    253             int userId = mConfirmingCredentialUser;
    254             IntConsumer listener = mConfirmingCredentialListener;
    255             // reset them before calling the listener because the listener may call back to start
    256             // activity again. (though it should never happen.)
    257             mConfirmingCredentialUser = UserHandle.USER_NULL;
    258             mConfirmingCredentialListener = null;
    259             if (resultCode == Activity.RESULT_OK) {
    260                 mConfirmedCredentialUsers.add(userId);
    261                 if (listener != null) {
    262                     listener.accept(userId);
    263                 }
    264             }
    265         }
    266     }
    267 
    268     private void closeKeyChainConnections() {
    269         final int n = mKeyChainConnectionByProfileId.size();
    270         for (int i = 0; i < n; ++i) {
    271             mKeyChainConnectionByProfileId.valueAt(i).close();
    272         }
    273         mKeyChainConnectionByProfileId.clear();
    274     }
    275 
    276     private void addTab(Tab tab) {
    277         TabHost.TabSpec systemSpec = mTabHost.newTabSpec(tab.mTag)
    278                 .setIndicator(getActivity().getString(tab.mLabel))
    279                 .setContent(tab.mView);
    280         mTabHost.addTab(systemSpec);
    281 
    282         final int profilesSize = mUserManager.getUserProfiles().size();
    283         final GroupAdapter groupAdapter = new GroupAdapter(tab);
    284         mGroupAdapters.add(groupAdapter);
    285 
    286         if (profilesSize == 1) {
    287             final ChildAdapter adapter = groupAdapter.getChildAdapter(0);
    288             adapter.setContainerViewId(tab.mPersonalList);
    289             adapter.prepare();
    290         } else if (profilesSize == 2) {
    291             final int workIndex = groupAdapter.getUserInfoByGroup(1).isManagedProfile() ? 1 : 0;
    292             final int personalIndex = workIndex == 1 ? 0 : 1;
    293 
    294             final ChildAdapter personalAdapter = groupAdapter.getChildAdapter(personalIndex);
    295             personalAdapter.setContainerViewId(tab.mPersonalList);
    296             personalAdapter.showHeader(true);
    297             personalAdapter.prepare();
    298 
    299             final ChildAdapter workAdapter = groupAdapter.getChildAdapter(workIndex);
    300             workAdapter.setContainerViewId(tab.mWorkList);
    301             workAdapter.showHeader(true);
    302             workAdapter.showDivider(true);
    303             workAdapter.prepare();
    304         } else if (profilesSize >= 3) {
    305             groupAdapter.setExpandableListView(
    306                     (ExpandableListView) mTabHost.findViewById(tab.mExpandableList));
    307         }
    308     }
    309 
    310     /**
    311      * Start work challenge activity.
    312      * @return true if screenlock exists
    313      */
    314     private boolean startConfirmCredential(int userId) {
    315         final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
    316                 userId);
    317         if (newIntent == null) {
    318             return false;
    319         }
    320         mConfirmingCredentialUser = userId;
    321         startActivityForResult(newIntent, REQUEST_CONFIRM_CREDENTIALS);
    322         return true;
    323     }
    324 
    325     /**
    326      * Adapter for expandable list view of certificates. Groups in the view correspond to profiles
    327      * whereas children correspond to certificates.
    328      */
    329     private class GroupAdapter extends BaseExpandableListAdapter implements
    330             ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener {
    331         private final AdapterData mData;
    332 
    333         private GroupAdapter(Tab tab) {
    334             mData = new AdapterData(tab, this);
    335             load();
    336         }
    337 
    338         @Override
    339         public int getGroupCount() {
    340             return mData.mCertHoldersByUserId.size();
    341         }
    342         @Override
    343         public int getChildrenCount(int groupPosition) {
    344             List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(groupPosition);
    345             if (certHolders != null) {
    346                 return certHolders.size();
    347             }
    348             return 0;
    349         }
    350         @Override
    351         public UserHandle getGroup(int groupPosition) {
    352             return new UserHandle(mData.mCertHoldersByUserId.keyAt(groupPosition));
    353         }
    354         @Override
    355         public CertHolder getChild(int groupPosition, int childPosition) {
    356             return mData.mCertHoldersByUserId.get(getUserIdByGroup(groupPosition)).get(
    357                     childPosition);
    358         }
    359         @Override
    360         public long getGroupId(int groupPosition) {
    361             return getUserIdByGroup(groupPosition);
    362         }
    363         private int getUserIdByGroup(int groupPosition) {
    364             return mData.mCertHoldersByUserId.keyAt(groupPosition);
    365         }
    366         public UserInfo getUserInfoByGroup(int groupPosition) {
    367             return mUserManager.getUserInfo(getUserIdByGroup(groupPosition));
    368         }
    369         @Override
    370         public long getChildId(int groupPosition, int childPosition) {
    371             return childPosition;
    372         }
    373         @Override
    374         public boolean hasStableIds() {
    375             return false;
    376         }
    377         @Override
    378         public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
    379                 ViewGroup parent) {
    380             if (convertView == null) {
    381                 LayoutInflater inflater = (LayoutInflater) getActivity()
    382                         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    383                 convertView = Utils.inflateCategoryHeader(inflater, parent);
    384             }
    385 
    386             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
    387             if (getUserInfoByGroup(groupPosition).isManagedProfile()) {
    388                 title.setText(R.string.category_work);
    389             } else {
    390                 title.setText(R.string.category_personal);
    391             }
    392             title.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
    393 
    394             return convertView;
    395         }
    396         @Override
    397         public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
    398                 View convertView, ViewGroup parent) {
    399             return getViewForCertificate(getChild(groupPosition, childPosition), mData.mTab,
    400                     convertView, parent);
    401         }
    402         @Override
    403         public boolean isChildSelectable(int groupPosition, int childPosition) {
    404             return true;
    405         }
    406 
    407         @Override
    408         public boolean onChildClick(ExpandableListView expandableListView, View view,
    409                 int groupPosition, int childPosition, long id) {
    410             showCertDialog(getChild(groupPosition, childPosition));
    411             return true;
    412         }
    413 
    414         @Override
    415         public boolean onGroupClick(ExpandableListView expandableListView, View view,
    416                 int groupPosition, long id) {
    417             return !checkGroupExpandableAndStartWarningActivity(groupPosition);
    418         }
    419 
    420         public void load() {
    421             mData.new AliasLoader().execute();
    422         }
    423 
    424         public void remove(CertHolder certHolder) {
    425             mData.remove(certHolder);
    426         }
    427 
    428         public void setExpandableListView(ExpandableListView lv) {
    429             lv.setAdapter(this);
    430             lv.setOnGroupClickListener(this);
    431             lv.setOnChildClickListener(this);
    432             lv.setVisibility(View.VISIBLE);
    433         }
    434 
    435         public ChildAdapter getChildAdapter(int groupPosition) {
    436             return new ChildAdapter(this, groupPosition);
    437         }
    438 
    439         public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition) {
    440             return checkGroupExpandableAndStartWarningActivity(groupPosition, true);
    441         }
    442 
    443         public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition,
    444                 boolean startActivity) {
    445             final UserHandle groupUser = getGroup(groupPosition);
    446             final int groupUserId = groupUser.getIdentifier();
    447             if (mUserManager.isQuietModeEnabled(groupUser)) {
    448                 final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
    449                         groupUserId);
    450                 if (startActivity) {
    451                     getActivity().startActivity(intent);
    452                 }
    453                 return false;
    454             } else if (!mUserManager.isUserUnlocked(groupUser)) {
    455                 final LockPatternUtils lockPatternUtils = new LockPatternUtils(
    456                         getActivity());
    457                 if (lockPatternUtils.isSeparateProfileChallengeEnabled(groupUserId)) {
    458                     if (startActivity) {
    459                         startConfirmCredential(groupUserId);
    460                     }
    461                     return false;
    462                 }
    463             }
    464             return true;
    465         }
    466 
    467         private View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView,
    468                 ViewGroup parent) {
    469             ViewHolder holder;
    470             if (convertView == null) {
    471                 LayoutInflater inflater = LayoutInflater.from(getActivity());
    472                 convertView = inflater.inflate(R.layout.trusted_credential, parent, false);
    473                 holder = new ViewHolder();
    474                 holder.mSubjectPrimaryView = (TextView)
    475                         convertView.findViewById(R.id.trusted_credential_subject_primary);
    476                 holder.mSubjectSecondaryView = (TextView)
    477                         convertView.findViewById(R.id.trusted_credential_subject_secondary);
    478                 holder.mSwitch = (Switch) convertView.findViewById(
    479                         R.id.trusted_credential_status);
    480                 convertView.setTag(holder);
    481             } else {
    482                 holder = (ViewHolder) convertView.getTag();
    483             }
    484             holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary);
    485             holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary);
    486             if (mTab.mSwitch) {
    487                 holder.mSwitch.setChecked(!certHolder.mDeleted);
    488                 holder.mSwitch.setEnabled(!mUserManager.hasUserRestriction(
    489                         UserManager.DISALLOW_CONFIG_CREDENTIALS,
    490                         new UserHandle(certHolder.mProfileId)));
    491                 holder.mSwitch.setVisibility(View.VISIBLE);
    492             }
    493             return convertView;
    494         }
    495 
    496         private class ViewHolder {
    497             private TextView mSubjectPrimaryView;
    498             private TextView mSubjectSecondaryView;
    499             private Switch mSwitch;
    500         }
    501     }
    502 
    503     private class ChildAdapter extends BaseAdapter implements View.OnClickListener,
    504             AdapterView.OnItemClickListener {
    505         private final int[] GROUP_EXPANDED_STATE_SET = {com.android.internal.R.attr.state_expanded};
    506         private final int[] EMPTY_STATE_SET = {};
    507         private final LinearLayout.LayoutParams HIDE_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
    508                 LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    509         private final LinearLayout.LayoutParams SHOW_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
    510                 LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1f);
    511         private final GroupAdapter mParent;
    512         private final int mGroupPosition;
    513         /*
    514          * This class doesn't hold the actual data. Events should notify parent.
    515          * When notifying DataSet events in this class, events should be forwarded to mParent.
    516          * i.e. this.notifyDataSetChanged -> mParent.notifyDataSetChanged -> mObserver.onChanged
    517          * -> outsideObservers.onChanged() (e.g. ListView)
    518          */
    519         private final DataSetObserver mObserver = new DataSetObserver() {
    520             @Override
    521             public void onChanged() {
    522                 super.onChanged();
    523                 ChildAdapter.super.notifyDataSetChanged();
    524             }
    525             @Override
    526             public void onInvalidated() {
    527                 super.onInvalidated();
    528                 ChildAdapter.super.notifyDataSetInvalidated();
    529             }
    530         };
    531 
    532         private boolean mIsListExpanded = true;
    533         private LinearLayout mContainerView;
    534         private ViewGroup mHeaderView;
    535         private ListView mListView;
    536         private ImageView mIndicatorView;
    537 
    538         private ChildAdapter(GroupAdapter parent, int groupPosition) {
    539             mParent = parent;
    540             mGroupPosition = groupPosition;
    541             mParent.registerDataSetObserver(mObserver);
    542         }
    543 
    544         @Override public int getCount() {
    545             return mParent.getChildrenCount(mGroupPosition);
    546         }
    547         @Override public CertHolder getItem(int position) {
    548             return mParent.getChild(mGroupPosition, position);
    549         }
    550         @Override public long getItemId(int position) {
    551             return mParent.getChildId(mGroupPosition, position);
    552         }
    553         @Override public View getView(int position, View convertView, ViewGroup parent) {
    554             return mParent.getChildView(mGroupPosition, position, false, convertView, parent);
    555         }
    556         // DataSet events
    557         @Override
    558         public void notifyDataSetChanged() {
    559             // Don't call super as the parent will propagate this event back later in mObserver
    560             mParent.notifyDataSetChanged();
    561         }
    562         @Override
    563         public void notifyDataSetInvalidated() {
    564             // Don't call super as the parent will propagate this event back later in mObserver
    565             mParent.notifyDataSetInvalidated();
    566         }
    567 
    568         // View related codes
    569         @Override
    570         public void onClick(View view) {
    571             mIsListExpanded = checkGroupExpandableAndStartWarningActivity() && !mIsListExpanded;
    572             refreshViews();
    573         }
    574 
    575         @Override
    576         public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
    577             showCertDialog(getItem(pos));
    578         }
    579 
    580         public void setContainerViewId(int viewId) {
    581             mContainerView = (LinearLayout) mTabHost.findViewById(viewId);
    582             mContainerView.setVisibility(View.VISIBLE);
    583 
    584             mListView = (ListView) mContainerView.findViewById(R.id.cert_list);
    585             mListView.setAdapter(this);
    586             mListView.setOnItemClickListener(this);
    587 
    588             mHeaderView = (ViewGroup) mContainerView.findViewById(R.id.header_view);
    589             mHeaderView.setOnClickListener(this);
    590 
    591             mIndicatorView = (ImageView) mHeaderView.findViewById(R.id.group_indicator);
    592             mIndicatorView.setImageDrawable(getGroupIndicator());
    593 
    594             FrameLayout headerContentContainer = (FrameLayout)
    595                     mHeaderView.findViewById(R.id.header_content_container);
    596             headerContentContainer.addView(
    597                     mParent.getGroupView(mGroupPosition, true /* parent ignores it */, null,
    598                             headerContentContainer));
    599         }
    600 
    601         public void showHeader(boolean showHeader) {
    602             mHeaderView.setVisibility(showHeader ? View.VISIBLE : View.GONE);
    603         }
    604 
    605         public void showDivider(boolean showDivider) {
    606             View dividerView = mHeaderView.findViewById(R.id.header_divider);
    607             dividerView.setVisibility(showDivider ? View.VISIBLE : View.GONE );
    608         }
    609 
    610         public void prepare() {
    611             mIsListExpanded = mParent.checkGroupExpandableAndStartWarningActivity(mGroupPosition,
    612                     false /* startActivity */);
    613             refreshViews();
    614         }
    615 
    616         private boolean checkGroupExpandableAndStartWarningActivity() {
    617             return mParent.checkGroupExpandableAndStartWarningActivity(mGroupPosition);
    618         }
    619 
    620         private void refreshViews() {
    621             mIndicatorView.setImageState(mIsListExpanded ? GROUP_EXPANDED_STATE_SET
    622                     : EMPTY_STATE_SET, false);
    623             mListView.setVisibility(mIsListExpanded ? View.VISIBLE : View.GONE);
    624             mContainerView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
    625                     : HIDE_LAYOUT_PARAMS);
    626         }
    627 
    628         // Get group indicator from styles of ExpandableListView
    629         private Drawable getGroupIndicator() {
    630             final TypedArray a = getActivity().obtainStyledAttributes(null,
    631                     com.android.internal.R.styleable.ExpandableListView,
    632                     com.android.internal.R.attr.expandableListViewStyle, 0);
    633             Drawable groupIndicator = a.getDrawable(
    634                     com.android.internal.R.styleable.ExpandableListView_groupIndicator);
    635             a.recycle();
    636             return groupIndicator;
    637         }
    638     }
    639 
    640     private class AdapterData {
    641         private final SparseArray<List<CertHolder>> mCertHoldersByUserId =
    642                 new SparseArray<List<CertHolder>>();
    643         private final Tab mTab;
    644         private final GroupAdapter mAdapter;
    645 
    646         private AdapterData(Tab tab, GroupAdapter adapter) {
    647             mAdapter = adapter;
    648             mTab = tab;
    649         }
    650 
    651         private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> {
    652             private ProgressBar mProgressBar;
    653             private View mContentView;
    654             private Context mContext;
    655 
    656             public AliasLoader() {
    657                 mContext = getActivity();
    658                 mAliasLoaders.add(this);
    659                 List<UserHandle> profiles = mUserManager.getUserProfiles();
    660                 for (UserHandle profile : profiles) {
    661                     mCertHoldersByUserId.put(profile.getIdentifier(), new ArrayList<CertHolder>());
    662                 }
    663             }
    664 
    665             private boolean shouldSkipProfile(UserHandle userHandle) {
    666                 return mUserManager.isQuietModeEnabled(userHandle)
    667                         || !mUserManager.isUserUnlocked(userHandle.getIdentifier());
    668             }
    669 
    670             @Override protected void onPreExecute() {
    671                 View content = mTabHost.getTabContentView();
    672                 mProgressBar = (ProgressBar) content.findViewById(mTab.mProgress);
    673                 mContentView = content.findViewById(mTab.mContentView);
    674                 mProgressBar.setVisibility(View.VISIBLE);
    675                 mContentView.setVisibility(View.GONE);
    676             }
    677             @Override protected SparseArray<List<CertHolder>> doInBackground(Void... params) {
    678                 SparseArray<List<CertHolder>> certHoldersByProfile =
    679                         new SparseArray<List<CertHolder>>();
    680                 try {
    681                     List<UserHandle> profiles = mUserManager.getUserProfiles();
    682                     final int n = profiles.size();
    683                     // First we get all aliases for all profiles in order to show progress
    684                     // correctly. Otherwise this could all be in a single loop.
    685                     SparseArray<List<ParcelableString>> aliasesByProfileId = new SparseArray<
    686                             List<ParcelableString>>(n);
    687                     int max = 0;
    688                     int progress = 0;
    689                     for (int i = 0; i < n; ++i) {
    690                         UserHandle profile = profiles.get(i);
    691                         int profileId = profile.getIdentifier();
    692                         if (shouldSkipProfile(profile)) {
    693                             continue;
    694                         }
    695                         KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
    696                                 profile);
    697                         // Saving the connection for later use on the certificate dialog.
    698                         mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
    699                         IKeyChainService service = keyChainConnection.getService();
    700                         List<ParcelableString> aliases = mTab.getAliases(service);
    701                         if (isCancelled()) {
    702                             return new SparseArray<List<CertHolder>>();
    703                         }
    704                         max += aliases.size();
    705                         aliasesByProfileId.put(profileId, aliases);
    706                     }
    707                     for (int i = 0; i < n; ++i) {
    708                         UserHandle profile = profiles.get(i);
    709                         int profileId = profile.getIdentifier();
    710                         List<ParcelableString> aliases = aliasesByProfileId.get(profileId);
    711                         if (isCancelled()) {
    712                             return new SparseArray<List<CertHolder>>();
    713                         }
    714                         KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
    715                                 profileId);
    716                         if (shouldSkipProfile(profile) || aliases == null
    717                                 || keyChainConnection == null) {
    718                             certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0));
    719                             continue;
    720                         }
    721                         IKeyChainService service = keyChainConnection.getService();
    722                         List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
    723                         final int aliasMax = aliases.size();
    724                         for (int j = 0; j < aliasMax; ++j) {
    725                             String alias = aliases.get(j).string;
    726                             byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
    727                                     true);
    728                             X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
    729                             certHolders.add(new CertHolder(service, mAdapter,
    730                                     mTab, alias, cert, profileId));
    731                             publishProgress(++progress, max);
    732                         }
    733                         Collections.sort(certHolders);
    734                         certHoldersByProfile.put(profileId, certHolders);
    735                     }
    736                     return certHoldersByProfile;
    737                 } catch (RemoteException e) {
    738                     Log.e(TAG, "Remote exception while loading aliases.", e);
    739                     return new SparseArray<List<CertHolder>>();
    740                 } catch (InterruptedException e) {
    741                     Log.e(TAG, "InterruptedException while loading aliases.", e);
    742                     return new SparseArray<List<CertHolder>>();
    743                 }
    744             }
    745             @Override protected void onProgressUpdate(Integer... progressAndMax) {
    746                 int progress = progressAndMax[0];
    747                 int max = progressAndMax[1];
    748                 if (max != mProgressBar.getMax()) {
    749                     mProgressBar.setMax(max);
    750                 }
    751                 mProgressBar.setProgress(progress);
    752             }
    753             @Override protected void onPostExecute(SparseArray<List<CertHolder>> certHolders) {
    754                 mCertHoldersByUserId.clear();
    755                 final int n = certHolders.size();
    756                 for (int i = 0; i < n; ++i) {
    757                     mCertHoldersByUserId.put(certHolders.keyAt(i), certHolders.valueAt(i));
    758                 }
    759                 mAdapter.notifyDataSetChanged();
    760                 mProgressBar.setVisibility(View.GONE);
    761                 mContentView.setVisibility(View.VISIBLE);
    762                 mProgressBar.setProgress(0);
    763                 mAliasLoaders.remove(this);
    764                 showTrustAllCaDialogIfNeeded();
    765             }
    766 
    767             private boolean isUserTabAndTrustAllCertMode() {
    768                 return isTrustAllCaCertModeInProgress() && mTab == Tab.USER;
    769             }
    770 
    771             @UiThread
    772             private void showTrustAllCaDialogIfNeeded() {
    773                 if (!isUserTabAndTrustAllCertMode()) {
    774                     return;
    775                 }
    776                 List<CertHolder> certHolders = mCertHoldersByUserId.get(mTrustAllCaUserId);
    777                 if (certHolders == null) {
    778                     return;
    779                 }
    780 
    781                 List<CertHolder> unapprovedUserCertHolders = new ArrayList<>();
    782                 final DevicePolicyManager dpm = mContext.getSystemService(
    783                         DevicePolicyManager.class);
    784                 for (CertHolder cert : certHolders) {
    785                     if (cert != null && !dpm.isCaCertApproved(cert.mAlias, mTrustAllCaUserId)) {
    786                         unapprovedUserCertHolders.add(cert);
    787                     }
    788                 }
    789 
    790                 if (unapprovedUserCertHolders.size() == 0) {
    791                     Log.w(TAG, "no cert is pending approval for user " + mTrustAllCaUserId);
    792                     return;
    793                 }
    794                 showTrustAllCaDialog(unapprovedUserCertHolders);
    795             }
    796         }
    797 
    798         public void remove(CertHolder certHolder) {
    799             if (mCertHoldersByUserId != null) {
    800                 final List<CertHolder> certs = mCertHoldersByUserId.get(certHolder.mProfileId);
    801                 if (certs != null) {
    802                     certs.remove(certHolder);
    803                 }
    804             }
    805         }
    806     }
    807 
    808     /* package */ static class CertHolder implements Comparable<CertHolder> {
    809         public int mProfileId;
    810         private final IKeyChainService mService;
    811         private final GroupAdapter mAdapter;
    812         private final Tab mTab;
    813         private final String mAlias;
    814         private final X509Certificate mX509Cert;
    815 
    816         private final SslCertificate mSslCert;
    817         private final String mSubjectPrimary;
    818         private final String mSubjectSecondary;
    819         private boolean mDeleted;
    820 
    821         private CertHolder(IKeyChainService service,
    822                            GroupAdapter adapter,
    823                            Tab tab,
    824                            String alias,
    825                            X509Certificate x509Cert,
    826                            int profileId) {
    827             mProfileId = profileId;
    828             mService = service;
    829             mAdapter = adapter;
    830             mTab = tab;
    831             mAlias = alias;
    832             mX509Cert = x509Cert;
    833 
    834             mSslCert = new SslCertificate(x509Cert);
    835 
    836             String cn = mSslCert.getIssuedTo().getCName();
    837             String o = mSslCert.getIssuedTo().getOName();
    838             String ou = mSslCert.getIssuedTo().getUName();
    839             // if we have a O, use O as primary subject, secondary prefer CN over OU
    840             // if we don't have an O, use CN as primary, empty secondary
    841             // if we don't have O or CN, use DName as primary, empty secondary
    842             if (!o.isEmpty()) {
    843                 if (!cn.isEmpty()) {
    844                     mSubjectPrimary = o;
    845                     mSubjectSecondary = cn;
    846                 } else {
    847                     mSubjectPrimary = o;
    848                     mSubjectSecondary = ou;
    849                 }
    850             } else {
    851                 if (!cn.isEmpty()) {
    852                     mSubjectPrimary = cn;
    853                     mSubjectSecondary = "";
    854                 } else {
    855                     mSubjectPrimary = mSslCert.getIssuedTo().getDName();
    856                     mSubjectSecondary = "";
    857                 }
    858             }
    859             try {
    860                 mDeleted = mTab.deleted(mService, mAlias);
    861             } catch (RemoteException e) {
    862                 Log.e(TAG, "Remote exception while checking if alias " + mAlias + " is deleted.",
    863                         e);
    864                 mDeleted = false;
    865             }
    866         }
    867         @Override public int compareTo(CertHolder o) {
    868             int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary);
    869             if (primary != 0) {
    870                 return primary;
    871             }
    872             return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary);
    873         }
    874         @Override public boolean equals(Object o) {
    875             if (!(o instanceof CertHolder)) {
    876                 return false;
    877             }
    878             CertHolder other = (CertHolder) o;
    879             return mAlias.equals(other.mAlias);
    880         }
    881         @Override public int hashCode() {
    882             return mAlias.hashCode();
    883         }
    884 
    885         public int getUserId() {
    886             return mProfileId;
    887         }
    888 
    889         public String getAlias() {
    890             return mAlias;
    891         }
    892 
    893         public boolean isSystemCert() {
    894             return mTab == Tab.SYSTEM;
    895         }
    896 
    897         public boolean isDeleted() {
    898             return mDeleted;
    899         }
    900     }
    901 
    902 
    903     private boolean isTrustAllCaCertModeInProgress() {
    904         return mTrustAllCaUserId != UserHandle.USER_NULL;
    905     }
    906 
    907     private void showTrustAllCaDialog(List<CertHolder> unapprovedCertHolders) {
    908         final CertHolder[] arr = unapprovedCertHolders.toArray(
    909                 new CertHolder[unapprovedCertHolders.size()]);
    910         new TrustedCredentialsDialogBuilder(getActivity(), this)
    911                 .setCertHolders(arr)
    912                 .setOnDismissListener(new DialogInterface.OnDismissListener() {
    913                     @Override
    914                     public void onDismiss(DialogInterface dialogInterface) {
    915                         // Avoid starting dialog again after Activity restart.
    916                         getActivity().getIntent().removeExtra(ARG_SHOW_NEW_FOR_USER);
    917                         mTrustAllCaUserId = UserHandle.USER_NULL;
    918                     }
    919                 })
    920                 .show();
    921     }
    922 
    923     private void showCertDialog(final CertHolder certHolder) {
    924         new TrustedCredentialsDialogBuilder(getActivity(), this)
    925                 .setCertHolder(certHolder)
    926                 .show();
    927     }
    928 
    929     @Override
    930     public List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder) {
    931         List<X509Certificate> certificates = null;
    932         try {
    933             KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
    934                     certHolder.mProfileId);
    935             IKeyChainService service = keyChainConnection.getService();
    936             List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
    937             final int n = chain.size();
    938             certificates = new ArrayList<X509Certificate>(n);
    939             for (int i = 0; i < n; ++i) {
    940                 byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
    941                 X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
    942                 certificates.add(certificate);
    943             }
    944         } catch (RemoteException ex) {
    945             Log.e(TAG, "RemoteException while retrieving certificate chain for root "
    946                     + certHolder.mAlias, ex);
    947         }
    948         return certificates;
    949     }
    950 
    951     @Override
    952     public void removeOrInstallCert(CertHolder certHolder) {
    953         new AliasOperation(certHolder).execute();
    954     }
    955 
    956     @Override
    957     public boolean startConfirmCredentialIfNotConfirmed(int userId,
    958             IntConsumer onCredentialConfirmedListener) {
    959         if (mConfirmedCredentialUsers.contains(userId)) {
    960             // Credential has been confirmed. Don't start activity.
    961             return false;
    962         }
    963 
    964         boolean result = startConfirmCredential(userId);
    965         if (result) {
    966             mConfirmingCredentialListener = onCredentialConfirmedListener;
    967         }
    968         return result;
    969     }
    970 
    971     private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
    972         private final CertHolder mCertHolder;
    973 
    974         private AliasOperation(CertHolder certHolder) {
    975             mCertHolder = certHolder;
    976             mAliasOperation = this;
    977         }
    978 
    979         @Override
    980         protected Boolean doInBackground(Void... params) {
    981             try {
    982                 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
    983                         mCertHolder.mProfileId);
    984                 IKeyChainService service = keyChainConnection.getService();
    985                 if (mCertHolder.mDeleted) {
    986                     byte[] bytes = mCertHolder.mX509Cert.getEncoded();
    987                     service.installCaCertificate(bytes);
    988                     return true;
    989                 } else {
    990                     return service.deleteCaCertificate(mCertHolder.mAlias);
    991                 }
    992             } catch (CertificateEncodingException | SecurityException | IllegalStateException
    993                     | RemoteException e) {
    994                 Log.w(TAG, "Error while toggling alias " + mCertHolder.mAlias, e);
    995                 return false;
    996             }
    997         }
    998 
    999         @Override
   1000         protected void onPostExecute(Boolean ok) {
   1001             if (ok) {
   1002                 if (mCertHolder.mTab.mSwitch) {
   1003                     mCertHolder.mDeleted = !mCertHolder.mDeleted;
   1004                 } else {
   1005                     mCertHolder.mAdapter.remove(mCertHolder);
   1006                 }
   1007                 mCertHolder.mAdapter.notifyDataSetChanged();
   1008             } else {
   1009                 // bail, reload to reset to known state
   1010                 mCertHolder.mAdapter.load();
   1011             }
   1012             mAliasOperation = null;
   1013         }
   1014     }
   1015 }
   1016