blob: 12524b9bd00d2c017bf98a8af026509ca69eaec3 [file] [log] [blame]
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001/*
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
17package com.android.settings;
18
Robin Lee232f0572016-08-02 10:52:22 +010019import static android.widget.LinearLayout.LayoutParams.MATCH_PARENT;
20import static android.widget.LinearLayout.LayoutParams.WRAP_CONTENT;
21
22import android.animation.LayoutTransition;
Doris Ling03a3b512017-10-18 14:25:01 -070023import android.annotation.StringRes;
Victor Changcaa88772016-04-05 22:06:49 +010024import android.annotation.UiThread;
Victor Chang01f4dbc2016-05-09 16:13:10 +010025import android.app.Activity;
Ricky Wai78fb3d32016-03-11 07:09:24 +000026import android.app.KeyguardManager;
Victor Changcaa88772016-04-05 22:06:49 +010027import android.app.admin.DevicePolicyManager;
Ricky Wai78fb3d32016-03-11 07:09:24 +000028import android.content.BroadcastReceiver;
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -040029import android.content.Context;
Victor Changcaa88772016-04-05 22:06:49 +010030import android.content.DialogInterface;
Ricky Wai78fb3d32016-03-11 07:09:24 +000031import android.content.Intent;
32import android.content.IntentFilter;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010033import android.content.pm.UserInfo;
Victor Chang9583dce2016-04-14 22:51:38 +010034import android.content.res.TypedArray;
35import android.database.DataSetObserver;
36import android.graphics.drawable.Drawable;
Brian Carlstromf6f4e302011-06-26 16:05:21 -070037import android.net.http.SslCertificate;
38import android.os.AsyncTask;
39import android.os.Bundle;
40import android.os.RemoteException;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010041import android.os.UserHandle;
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -040042import android.os.UserManager;
Brian Carlstromf6f4e302011-06-26 16:05:21 -070043import android.security.IKeyChainService;
44import android.security.KeyChain;
45import android.security.KeyChain.KeyChainConnection;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010046import android.util.Log;
Jason Monk39b46742015-09-10 15:52:51 -040047import android.util.SparseArray;
Victor Chang9583dce2016-04-14 22:51:38 +010048import android.util.ArraySet;
Brian Carlstromf6f4e302011-06-26 16:05:21 -070049import android.view.LayoutInflater;
50import android.view.View;
51import android.view.ViewGroup;
52import android.widget.AdapterView;
53import android.widget.BaseAdapter;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010054import android.widget.BaseExpandableListAdapter;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010055import android.widget.ExpandableListView;
Victor Chang9583dce2016-04-14 22:51:38 +010056import android.widget.FrameLayout;
57import android.widget.ImageView;
58import android.widget.LinearLayout;
Brian Carlstromf6f4e302011-06-26 16:05:21 -070059import android.widget.ListView;
Brian Carlstrom729c6d92011-07-06 13:15:03 -070060import android.widget.ProgressBar;
Fabrice Di Meglio44db45a2014-10-17 14:06:22 -070061import android.widget.Switch;
Brian Carlstromf6f4e302011-06-26 16:05:21 -070062import android.widget.TabHost;
63import android.widget.TextView;
Julia Reynolds565653c2014-06-12 11:49:12 -040064
daqibe47df72017-09-05 09:53:05 +080065import com.android.internal.annotations.GuardedBy;
Ricky Wai78fb3d32016-03-11 07:09:24 +000066import com.android.internal.app.UnlaunchableAppActivity;
Tamas Berghammer265d3c22016-06-22 15:34:45 +010067import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Ricky Wai78fb3d32016-03-11 07:09:24 +000068import com.android.internal.widget.LockPatternUtils;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010069
Brian Carlstromf6f4e302011-06-26 16:05:21 -070070import java.security.cert.CertificateEncodingException;
71import java.security.cert.X509Certificate;
72import java.util.ArrayList;
73import java.util.Collections;
Jason Monk39b46742015-09-10 15:52:51 -040074import java.util.List;
Victor Chang9583dce2016-04-14 22:51:38 +010075import java.util.Set;
Victor Chang45ca9062016-05-23 19:47:38 +010076import java.util.function.IntConsumer;
Brian Carlstromf6f4e302011-06-26 16:05:21 -070077
Victor Changd7d0e1b2016-04-05 20:01:24 +010078public class TrustedCredentialsSettings extends OptionsMenuFragment
79 implements TrustedCredentialsDialogBuilder.DelegateInterface {
Brian Carlstromf6f4e302011-06-26 16:05:21 -070080
Victor Changcaa88772016-04-05 22:06:49 +010081 public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
82
Brian Carlstromac45fb42011-06-28 19:57:14 -070083 private static final String TAG = "TrustedCredentialsSettings";
Brian Carlstromf6f4e302011-06-26 16:05:21 -070084
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -040085 private UserManager mUserManager;
Ricky Wai78fb3d32016-03-11 07:09:24 +000086 private KeyguardManager mKeyguardManager;
Victor Changcaa88772016-04-05 22:06:49 +010087 private int mTrustAllCaUserId;
88
Victor Chang01f4dbc2016-05-09 16:13:10 +010089 private static final String SAVED_CONFIRMED_CREDENTIAL_USERS = "ConfirmedCredentialUsers";
90 private static final String SAVED_CONFIRMING_CREDENTIAL_USER = "ConfirmingCredentialUser";
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -040091 private static final String USER_ACTION = "com.android.settings.TRUSTED_CREDENTIALS_USER";
Victor Chang01f4dbc2016-05-09 16:13:10 +010092 private static final int REQUEST_CONFIRM_CREDENTIALS = 1;
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -040093
Chris Wren8a963ba2015-03-20 10:29:14 -040094 @Override
Doris Ling03a3b512017-10-18 14:25:01 -070095 @StringRes
96 protected int getTitle() {
97 return R.string.trusted_credentials;
98 }
99
100 @Override
Fan Zhang65076132016-08-08 10:25:13 -0700101 public int getMetricsCategory() {
Chris Wren9d1bfd12016-01-26 18:04:01 -0500102 return MetricsEvent.TRUSTED_CREDENTIALS;
Chris Wren8a963ba2015-03-20 10:29:14 -0400103 }
104
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700105 private enum Tab {
106 SYSTEM("system",
Victor Chang9583dce2016-04-14 22:51:38 +0100107 R.string.trusted_credentials_system_tab,
108 R.id.system_tab,
109 R.id.system_progress,
Victor Chang9583dce2016-04-14 22:51:38 +0100110 R.id.system_content,
Robin Lee232f0572016-08-02 10:52:22 +0100111 true),
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700112 USER("user",
Victor Chang9583dce2016-04-14 22:51:38 +0100113 R.string.trusted_credentials_user_tab,
114 R.id.user_tab,
115 R.id.user_progress,
Victor Chang9583dce2016-04-14 22:51:38 +0100116 R.id.user_content,
117 false);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700118
119 private final String mTag;
120 private final int mLabel;
121 private final int mView;
122 private final int mProgress;
Victor Chang9583dce2016-04-14 22:51:38 +0100123 private final int mContentView;
Fabrice Di Meglio44db45a2014-10-17 14:06:22 -0700124 private final boolean mSwitch;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100125
Robin Lee232f0572016-08-02 10:52:22 +0100126 private Tab(String tag, int label, int view, int progress, int contentView, boolean withSwitch) {
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700127 mTag = tag;
128 mLabel = label;
129 mView = view;
130 mProgress = progress;
Victor Chang9583dce2016-04-14 22:51:38 +0100131 mContentView = contentView;
Fabrice Di Meglio44db45a2014-10-17 14:06:22 -0700132 mSwitch = withSwitch;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700133 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100134
Robin Leeb50e6812017-02-20 21:02:45 +0000135 private List<String> getAliases(IKeyChainService service) throws RemoteException {
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700136 switch (this) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100137 case SYSTEM: {
138 return service.getSystemCaAliases().getList();
139 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700140 case USER:
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100141 return service.getUserCaAliases().getList();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700142 }
143 throw new AssertionError();
144 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100145 private boolean deleted(IKeyChainService service, String alias) throws RemoteException {
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700146 switch (this) {
147 case SYSTEM:
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100148 return !service.containsCaAlias(alias);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700149 case USER:
150 return false;
151 }
152 throw new AssertionError();
153 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700154 }
155
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700156 private TabHost mTabHost;
Victor Chang9583dce2016-04-14 22:51:38 +0100157 private ArrayList<GroupAdapter> mGroupAdapters = new ArrayList<>(2);
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100158 private AliasOperation mAliasOperation;
Victor Chang01f4dbc2016-05-09 16:13:10 +0100159 private ArraySet<Integer> mConfirmedCredentialUsers;
160 private int mConfirmingCredentialUser;
Victor Chang45ca9062016-05-23 19:47:38 +0100161 private IntConsumer mConfirmingCredentialListener;
Victor Chang9583dce2016-04-14 22:51:38 +0100162 private Set<AdapterData.AliasLoader> mAliasLoaders = new ArraySet<AdapterData.AliasLoader>(2);
daqibe47df72017-09-05 09:53:05 +0800163 @GuardedBy("mKeyChainConnectionByProfileId")
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100164 private final SparseArray<KeyChainConnection>
165 mKeyChainConnectionByProfileId = new SparseArray<KeyChainConnection>();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700166
Ricky Wai78fb3d32016-03-11 07:09:24 +0000167 private BroadcastReceiver mWorkProfileChangedReceiver = new BroadcastReceiver() {
168
169 @Override
170 public void onReceive(Context context, Intent intent) {
171 final String action = intent.getAction();
Rubin Xu819f78e2016-04-04 17:23:46 +0100172 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
173 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
Ricky Wai78fb3d32016-03-11 07:09:24 +0000174 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
Victor Chang9583dce2016-04-14 22:51:38 +0100175 for (GroupAdapter adapter : mGroupAdapters) {
176 adapter.load();
Ricky Wai78fb3d32016-03-11 07:09:24 +0000177 }
178 }
179 }
180
181 };
182
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -0400183 @Override
184 public void onCreate(Bundle savedInstanceState) {
185 super.onCreate(savedInstanceState);
186 mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
Ricky Wai78fb3d32016-03-11 07:09:24 +0000187 mKeyguardManager = (KeyguardManager) getActivity()
188 .getSystemService(Context.KEYGUARD_SERVICE);
Victor Changcaa88772016-04-05 22:06:49 +0100189 mTrustAllCaUserId = getActivity().getIntent().getIntExtra(ARG_SHOW_NEW_FOR_USER,
190 UserHandle.USER_NULL);
Victor Chang01f4dbc2016-05-09 16:13:10 +0100191 mConfirmedCredentialUsers = new ArraySet<>(2);
192 mConfirmingCredentialUser = UserHandle.USER_NULL;
193 if (savedInstanceState != null) {
194 mConfirmingCredentialUser = savedInstanceState.getInt(SAVED_CONFIRMING_CREDENTIAL_USER,
195 UserHandle.USER_NULL);
196 ArrayList<Integer> users = savedInstanceState.getIntegerArrayList(
197 SAVED_CONFIRMED_CREDENTIAL_USERS);
198 if (users != null) {
199 mConfirmedCredentialUsers.addAll(users);
200 }
201 }
Victor Changcaa88772016-04-05 22:06:49 +0100202
Victor Chang45ca9062016-05-23 19:47:38 +0100203 mConfirmingCredentialListener = null;
204
Ricky Wai78fb3d32016-03-11 07:09:24 +0000205 IntentFilter filter = new IntentFilter();
Rubin Xu819f78e2016-04-04 17:23:46 +0100206 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
207 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
Ricky Wai78fb3d32016-03-11 07:09:24 +0000208 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
209 getActivity().registerReceiver(mWorkProfileChangedReceiver, filter);
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -0400210 }
211
Victor Chang01f4dbc2016-05-09 16:13:10 +0100212 @Override
213 public void onSaveInstanceState(Bundle outState) {
214 super.onSaveInstanceState(outState);
215 outState.putIntegerArrayList(SAVED_CONFIRMED_CREDENTIAL_USERS, new ArrayList<>(
216 mConfirmedCredentialUsers));
217 outState.putInt(SAVED_CONFIRMING_CREDENTIAL_USER, mConfirmingCredentialUser);
218 }
219
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700220 @Override public View onCreateView(
221 LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
222 mTabHost = (TabHost) inflater.inflate(R.layout.trusted_credentials, parent, false);
223 mTabHost.setup();
224 addTab(Tab.SYSTEM);
225 // TODO add Install button on Tab.USER to go to CertInstaller like KeyChainActivity
226 addTab(Tab.USER);
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -0400227 if (getActivity().getIntent() != null &&
228 USER_ACTION.equals(getActivity().getIntent().getAction())) {
229 mTabHost.setCurrentTabByTag(Tab.USER.mTag);
230 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700231 return mTabHost;
232 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100233 @Override
234 public void onDestroy() {
Ricky Wai78fb3d32016-03-11 07:09:24 +0000235 getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
Victor Chang9583dce2016-04-14 22:51:38 +0100236 for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100237 aliasLoader.cancel(true);
238 }
Victor Chang9583dce2016-04-14 22:51:38 +0100239 mAliasLoaders.clear();
240 mGroupAdapters.clear();
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100241 if (mAliasOperation != null) {
242 mAliasOperation.cancel(true);
243 mAliasOperation = null;
244 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100245 closeKeyChainConnections();
246 super.onDestroy();
247 }
248
Victor Chang01f4dbc2016-05-09 16:13:10 +0100249 @Override
250 public void onActivityResult(int requestCode, int resultCode, Intent data) {
251 if (requestCode == REQUEST_CONFIRM_CREDENTIALS) {
Victor Chang45ca9062016-05-23 19:47:38 +0100252 int userId = mConfirmingCredentialUser;
253 IntConsumer listener = mConfirmingCredentialListener;
254 // reset them before calling the listener because the listener may call back to start
255 // activity again. (though it should never happen.)
Victor Chang01f4dbc2016-05-09 16:13:10 +0100256 mConfirmingCredentialUser = UserHandle.USER_NULL;
Victor Chang45ca9062016-05-23 19:47:38 +0100257 mConfirmingCredentialListener = null;
258 if (resultCode == Activity.RESULT_OK) {
259 mConfirmedCredentialUsers.add(userId);
260 if (listener != null) {
261 listener.accept(userId);
262 }
263 }
Victor Chang01f4dbc2016-05-09 16:13:10 +0100264 }
265 }
266
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100267 private void closeKeyChainConnections() {
daqibe47df72017-09-05 09:53:05 +0800268 synchronized (mKeyChainConnectionByProfileId) {
269 final int n = mKeyChainConnectionByProfileId.size();
270 for (int i = 0; i < n; ++i) {
271 mKeyChainConnectionByProfileId.valueAt(i).close();
272 }
273 mKeyChainConnectionByProfileId.clear();
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100274 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100275 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700276
277 private void addTab(Tab tab) {
278 TabHost.TabSpec systemSpec = mTabHost.newTabSpec(tab.mTag)
279 .setIndicator(getActivity().getString(tab.mLabel))
280 .setContent(tab.mView);
281 mTabHost.addTab(systemSpec);
282
Victor Chang9583dce2016-04-14 22:51:38 +0100283 final GroupAdapter groupAdapter = new GroupAdapter(tab);
284 mGroupAdapters.add(groupAdapter);
Robin Lee232f0572016-08-02 10:52:22 +0100285 final int profilesSize = groupAdapter.getGroupCount();
Ricky Wai78fb3d32016-03-11 07:09:24 +0000286
Robin Lee232f0572016-08-02 10:52:22 +0100287 // Add a transition for non-visibility events like resizing the pane.
288 final ViewGroup contentView = (ViewGroup) mTabHost.findViewById(tab.mContentView);
289 contentView.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
Victor Chang9583dce2016-04-14 22:51:38 +0100290
Robin Lee232f0572016-08-02 10:52:22 +0100291 final LayoutInflater inflater = LayoutInflater.from(getActivity());
292 for (int i = 0; i < groupAdapter.getGroupCount(); i++) {
293 final boolean isWork = groupAdapter.getUserInfoByGroup(i).isManagedProfile();
294 final ChildAdapter adapter = groupAdapter.getChildAdapter(i);
Victor Chang9583dce2016-04-14 22:51:38 +0100295
Robin Lee232f0572016-08-02 10:52:22 +0100296 final LinearLayout containerView = (LinearLayout) inflater
297 .inflate(R.layout.trusted_credential_list_container, contentView, false);
298 adapter.setContainerView(containerView);
299
300 adapter.showHeader(profilesSize > 1);
301 adapter.showDivider(isWork);
302 adapter.setExpandIfAvailable(profilesSize <= 2 ? true : !isWork);
303 if (isWork) {
304 contentView.addView(containerView);
305 } else {
306 contentView.addView(containerView, 0);
307 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100308 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700309 }
310
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100311 /**
Victor Chang01f4dbc2016-05-09 16:13:10 +0100312 * Start work challenge activity.
313 * @return true if screenlock exists
Ricky Wai78fb3d32016-03-11 07:09:24 +0000314 */
Victor Chang01f4dbc2016-05-09 16:13:10 +0100315 private boolean startConfirmCredential(int userId) {
Ricky Wai78fb3d32016-03-11 07:09:24 +0000316 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
317 userId);
Victor Chang01f4dbc2016-05-09 16:13:10 +0100318 if (newIntent == null) {
319 return false;
320 }
321 mConfirmingCredentialUser = userId;
322 startActivityForResult(newIntent, REQUEST_CONFIRM_CREDENTIALS);
323 return true;
Ricky Wai78fb3d32016-03-11 07:09:24 +0000324 }
325
326 /**
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100327 * Adapter for expandable list view of certificates. Groups in the view correspond to profiles
328 * whereas children correspond to certificates.
329 */
Victor Chang9583dce2016-04-14 22:51:38 +0100330 private class GroupAdapter extends BaseExpandableListAdapter implements
Robin Leebed85592016-09-01 18:35:00 +0100331 ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener,
332 View.OnClickListener {
Victor Chang9583dce2016-04-14 22:51:38 +0100333 private final AdapterData mData;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100334
Victor Chang9583dce2016-04-14 22:51:38 +0100335 private GroupAdapter(Tab tab) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100336 mData = new AdapterData(tab, this);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700337 load();
338 }
Victor Chang9583dce2016-04-14 22:51:38 +0100339
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100340 @Override
341 public int getGroupCount() {
342 return mData.mCertHoldersByUserId.size();
343 }
344 @Override
345 public int getChildrenCount(int groupPosition) {
Zoltan Szatmary-Ban860e1e12014-09-10 12:22:36 +0100346 List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(groupPosition);
347 if (certHolders != null) {
348 return certHolders.size();
349 }
350 return 0;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100351 }
352 @Override
353 public UserHandle getGroup(int groupPosition) {
354 return new UserHandle(mData.mCertHoldersByUserId.keyAt(groupPosition));
355 }
356 @Override
357 public CertHolder getChild(int groupPosition, int childPosition) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100358 return mData.mCertHoldersByUserId.get(getUserIdByGroup(groupPosition)).get(
359 childPosition);
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100360 }
361 @Override
362 public long getGroupId(int groupPosition) {
Victor Chang9583dce2016-04-14 22:51:38 +0100363 return getUserIdByGroup(groupPosition);
364 }
365 private int getUserIdByGroup(int groupPosition) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100366 return mData.mCertHoldersByUserId.keyAt(groupPosition);
367 }
Victor Chang9583dce2016-04-14 22:51:38 +0100368 public UserInfo getUserInfoByGroup(int groupPosition) {
369 return mUserManager.getUserInfo(getUserIdByGroup(groupPosition));
370 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100371 @Override
372 public long getChildId(int groupPosition, int childPosition) {
373 return childPosition;
374 }
375 @Override
376 public boolean hasStableIds() {
377 return false;
378 }
379 @Override
380 public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
381 ViewGroup parent) {
382 if (convertView == null) {
383 LayoutInflater inflater = (LayoutInflater) getActivity()
384 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Zoltan Szatmary-Ban3af2e4c2014-12-19 17:17:23 +0000385 convertView = Utils.inflateCategoryHeader(inflater, parent);
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100386 }
387
388 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
Victor Chang9583dce2016-04-14 22:51:38 +0100389 if (getUserInfoByGroup(groupPosition).isManagedProfile()) {
Zoltan Szatmary-Banaf0f89f2014-09-30 16:30:07 +0100390 title.setText(R.string.category_work);
391 } else {
392 title.setText(R.string.category_personal);
393 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100394 title.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
395
396 return convertView;
397 }
398 @Override
399 public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
400 View convertView, ViewGroup parent) {
401 return getViewForCertificate(getChild(groupPosition, childPosition), mData.mTab,
402 convertView, parent);
403 }
404 @Override
405 public boolean isChildSelectable(int groupPosition, int childPosition) {
406 return true;
407 }
Victor Chang9583dce2016-04-14 22:51:38 +0100408
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100409 @Override
Victor Chang9583dce2016-04-14 22:51:38 +0100410 public boolean onChildClick(ExpandableListView expandableListView, View view,
411 int groupPosition, int childPosition, long id) {
412 showCertDialog(getChild(groupPosition, childPosition));
413 return true;
414 }
415
Robin Leebed85592016-09-01 18:35:00 +0100416 /**
417 * Called when the switch on a system certificate is clicked. This will toggle whether it
418 * is trusted as a credential.
419 */
420 @Override
421 public void onClick(View view) {
422 CertHolder holder = (CertHolder) view.getTag();
423 removeOrInstallCert(holder);
424 }
425
Victor Chang9583dce2016-04-14 22:51:38 +0100426 @Override
427 public boolean onGroupClick(ExpandableListView expandableListView, View view,
428 int groupPosition, long id) {
429 return !checkGroupExpandableAndStartWarningActivity(groupPosition);
430 }
431
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100432 public void load() {
433 mData.new AliasLoader().execute();
434 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100435
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100436 public void remove(CertHolder certHolder) {
437 mData.remove(certHolder);
438 }
Victor Chang9583dce2016-04-14 22:51:38 +0100439
440 public void setExpandableListView(ExpandableListView lv) {
441 lv.setAdapter(this);
442 lv.setOnGroupClickListener(this);
443 lv.setOnChildClickListener(this);
444 lv.setVisibility(View.VISIBLE);
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100445 }
Victor Chang9583dce2016-04-14 22:51:38 +0100446
447 public ChildAdapter getChildAdapter(int groupPosition) {
448 return new ChildAdapter(this, groupPosition);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700449 }
Victor Chang9583dce2016-04-14 22:51:38 +0100450
451 public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100452 return checkGroupExpandableAndStartWarningActivity(groupPosition, true);
453 }
454
455 public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition,
456 boolean startActivity) {
Victor Chang9583dce2016-04-14 22:51:38 +0100457 final UserHandle groupUser = getGroup(groupPosition);
458 final int groupUserId = groupUser.getIdentifier();
459 if (mUserManager.isQuietModeEnabled(groupUser)) {
460 final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
461 groupUserId);
Victor Chang01f4dbc2016-05-09 16:13:10 +0100462 if (startActivity) {
463 getActivity().startActivity(intent);
464 }
Victor Chang9583dce2016-04-14 22:51:38 +0100465 return false;
466 } else if (!mUserManager.isUserUnlocked(groupUser)) {
467 final LockPatternUtils lockPatternUtils = new LockPatternUtils(
468 getActivity());
469 if (lockPatternUtils.isSeparateProfileChallengeEnabled(groupUserId)) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100470 if (startActivity) {
471 startConfirmCredential(groupUserId);
472 }
Victor Chang9583dce2016-04-14 22:51:38 +0100473 return false;
474 }
Zoltan Szatmary-Ban860e1e12014-09-10 12:22:36 +0100475 }
Victor Chang9583dce2016-04-14 22:51:38 +0100476 return true;
477 }
478
479 private View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView,
480 ViewGroup parent) {
481 ViewHolder holder;
482 if (convertView == null) {
Robin Leebed85592016-09-01 18:35:00 +0100483 holder = new ViewHolder();
Victor Chang9583dce2016-04-14 22:51:38 +0100484 LayoutInflater inflater = LayoutInflater.from(getActivity());
485 convertView = inflater.inflate(R.layout.trusted_credential, parent, false);
Robin Leebed85592016-09-01 18:35:00 +0100486 convertView.setTag(holder);
Victor Chang9583dce2016-04-14 22:51:38 +0100487 holder.mSubjectPrimaryView = (TextView)
488 convertView.findViewById(R.id.trusted_credential_subject_primary);
489 holder.mSubjectSecondaryView = (TextView)
490 convertView.findViewById(R.id.trusted_credential_subject_secondary);
491 holder.mSwitch = (Switch) convertView.findViewById(
492 R.id.trusted_credential_status);
Robin Leebed85592016-09-01 18:35:00 +0100493 holder.mSwitch.setOnClickListener(this);
Victor Chang9583dce2016-04-14 22:51:38 +0100494 } else {
495 holder = (ViewHolder) convertView.getTag();
496 }
497 holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary);
498 holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary);
499 if (mTab.mSwitch) {
500 holder.mSwitch.setChecked(!certHolder.mDeleted);
501 holder.mSwitch.setEnabled(!mUserManager.hasUserRestriction(
502 UserManager.DISALLOW_CONFIG_CREDENTIALS,
503 new UserHandle(certHolder.mProfileId)));
504 holder.mSwitch.setVisibility(View.VISIBLE);
Robin Leebed85592016-09-01 18:35:00 +0100505 holder.mSwitch.setTag(certHolder);
Victor Chang9583dce2016-04-14 22:51:38 +0100506 }
507 return convertView;
508 }
509
510 private class ViewHolder {
511 private TextView mSubjectPrimaryView;
512 private TextView mSubjectSecondaryView;
513 private Switch mSwitch;
514 }
515 }
516
517 private class ChildAdapter extends BaseAdapter implements View.OnClickListener,
518 AdapterView.OnItemClickListener {
519 private final int[] GROUP_EXPANDED_STATE_SET = {com.android.internal.R.attr.state_expanded};
520 private final int[] EMPTY_STATE_SET = {};
Robin Lee232f0572016-08-02 10:52:22 +0100521 private final LinearLayout.LayoutParams HIDE_CONTAINER_LAYOUT_PARAMS =
522 new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0f);
523 private final LinearLayout.LayoutParams HIDE_LIST_LAYOUT_PARAMS =
524 new LinearLayout.LayoutParams(MATCH_PARENT, 0);
Victor Chang9583dce2016-04-14 22:51:38 +0100525 private final LinearLayout.LayoutParams SHOW_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
Robin Lee232f0572016-08-02 10:52:22 +0100526 LinearLayout.LayoutParams.MATCH_PARENT, MATCH_PARENT, 1f);
Victor Chang9583dce2016-04-14 22:51:38 +0100527 private final GroupAdapter mParent;
528 private final int mGroupPosition;
529 /*
530 * This class doesn't hold the actual data. Events should notify parent.
531 * When notifying DataSet events in this class, events should be forwarded to mParent.
532 * i.e. this.notifyDataSetChanged -> mParent.notifyDataSetChanged -> mObserver.onChanged
533 * -> outsideObservers.onChanged() (e.g. ListView)
534 */
535 private final DataSetObserver mObserver = new DataSetObserver() {
536 @Override
537 public void onChanged() {
538 super.onChanged();
539 ChildAdapter.super.notifyDataSetChanged();
540 }
541 @Override
542 public void onInvalidated() {
543 super.onInvalidated();
544 ChildAdapter.super.notifyDataSetInvalidated();
545 }
546 };
547
548 private boolean mIsListExpanded = true;
549 private LinearLayout mContainerView;
550 private ViewGroup mHeaderView;
551 private ListView mListView;
552 private ImageView mIndicatorView;
553
554 private ChildAdapter(GroupAdapter parent, int groupPosition) {
555 mParent = parent;
556 mGroupPosition = groupPosition;
557 mParent.registerDataSetObserver(mObserver);
558 }
559
560 @Override public int getCount() {
561 return mParent.getChildrenCount(mGroupPosition);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700562 }
563 @Override public CertHolder getItem(int position) {
Victor Chang9583dce2016-04-14 22:51:38 +0100564 return mParent.getChild(mGroupPosition, position);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700565 }
566 @Override public long getItemId(int position) {
Victor Chang9583dce2016-04-14 22:51:38 +0100567 return mParent.getChildId(mGroupPosition, position);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700568 }
Victor Chang9583dce2016-04-14 22:51:38 +0100569 @Override public View getView(int position, View convertView, ViewGroup parent) {
570 return mParent.getChildView(mGroupPosition, position, false, convertView, parent);
571 }
572 // DataSet events
573 @Override
574 public void notifyDataSetChanged() {
575 // Don't call super as the parent will propagate this event back later in mObserver
576 mParent.notifyDataSetChanged();
577 }
578 @Override
579 public void notifyDataSetInvalidated() {
580 // Don't call super as the parent will propagate this event back later in mObserver
581 mParent.notifyDataSetInvalidated();
582 }
583
584 // View related codes
585 @Override
586 public void onClick(View view) {
587 mIsListExpanded = checkGroupExpandableAndStartWarningActivity() && !mIsListExpanded;
588 refreshViews();
589 }
590
591 @Override
592 public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
593 showCertDialog(getItem(pos));
594 }
595
Robin Lee232f0572016-08-02 10:52:22 +0100596 public void setContainerView(LinearLayout containerView) {
597 mContainerView = containerView;
Victor Chang9583dce2016-04-14 22:51:38 +0100598
599 mListView = (ListView) mContainerView.findViewById(R.id.cert_list);
600 mListView.setAdapter(this);
601 mListView.setOnItemClickListener(this);
Robin Leebed85592016-09-01 18:35:00 +0100602 mListView.setItemsCanFocus(true);
Victor Chang9583dce2016-04-14 22:51:38 +0100603
604 mHeaderView = (ViewGroup) mContainerView.findViewById(R.id.header_view);
605 mHeaderView.setOnClickListener(this);
606
607 mIndicatorView = (ImageView) mHeaderView.findViewById(R.id.group_indicator);
608 mIndicatorView.setImageDrawable(getGroupIndicator());
609
610 FrameLayout headerContentContainer = (FrameLayout)
611 mHeaderView.findViewById(R.id.header_content_container);
612 headerContentContainer.addView(
613 mParent.getGroupView(mGroupPosition, true /* parent ignores it */, null,
614 headerContentContainer));
615 }
616
617 public void showHeader(boolean showHeader) {
618 mHeaderView.setVisibility(showHeader ? View.VISIBLE : View.GONE);
619 }
620
621 public void showDivider(boolean showDivider) {
622 View dividerView = mHeaderView.findViewById(R.id.header_divider);
623 dividerView.setVisibility(showDivider ? View.VISIBLE : View.GONE );
624 }
625
Robin Lee232f0572016-08-02 10:52:22 +0100626 public void setExpandIfAvailable(boolean expanded) {
627 mIsListExpanded = expanded && mParent.checkGroupExpandableAndStartWarningActivity(
628 mGroupPosition, false /* startActivity */);
Victor Chang9583dce2016-04-14 22:51:38 +0100629 refreshViews();
630 }
631
632 private boolean checkGroupExpandableAndStartWarningActivity() {
633 return mParent.checkGroupExpandableAndStartWarningActivity(mGroupPosition);
634 }
635
636 private void refreshViews() {
637 mIndicatorView.setImageState(mIsListExpanded ? GROUP_EXPANDED_STATE_SET
638 : EMPTY_STATE_SET, false);
Robin Lee232f0572016-08-02 10:52:22 +0100639 mListView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
640 : HIDE_LIST_LAYOUT_PARAMS);
Victor Chang9583dce2016-04-14 22:51:38 +0100641 mContainerView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
Robin Lee232f0572016-08-02 10:52:22 +0100642 : HIDE_CONTAINER_LAYOUT_PARAMS);
Victor Chang9583dce2016-04-14 22:51:38 +0100643 }
644
645 // Get group indicator from styles of ExpandableListView
646 private Drawable getGroupIndicator() {
647 final TypedArray a = getActivity().obtainStyledAttributes(null,
648 com.android.internal.R.styleable.ExpandableListView,
649 com.android.internal.R.attr.expandableListViewStyle, 0);
650 Drawable groupIndicator = a.getDrawable(
651 com.android.internal.R.styleable.ExpandableListView_groupIndicator);
652 a.recycle();
653 return groupIndicator;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100654 }
655 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700656
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100657 private class AdapterData {
658 private final SparseArray<List<CertHolder>> mCertHoldersByUserId =
659 new SparseArray<List<CertHolder>>();
660 private final Tab mTab;
Victor Chang9583dce2016-04-14 22:51:38 +0100661 private final GroupAdapter mAdapter;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100662
Victor Chang9583dce2016-04-14 22:51:38 +0100663 private AdapterData(Tab tab, GroupAdapter adapter) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100664 mAdapter = adapter;
665 mTab = tab;
666 }
667
668 private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> {
669 private ProgressBar mProgressBar;
Victor Chang9583dce2016-04-14 22:51:38 +0100670 private View mContentView;
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100671 private Context mContext;
672
673 public AliasLoader() {
674 mContext = getActivity();
Victor Chang9583dce2016-04-14 22:51:38 +0100675 mAliasLoaders.add(this);
676 List<UserHandle> profiles = mUserManager.getUserProfiles();
677 for (UserHandle profile : profiles) {
678 mCertHoldersByUserId.put(profile.getIdentifier(), new ArrayList<CertHolder>());
679 }
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100680 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100681
Ricky Wai78fb3d32016-03-11 07:09:24 +0000682 private boolean shouldSkipProfile(UserHandle userHandle) {
683 return mUserManager.isQuietModeEnabled(userHandle)
684 || !mUserManager.isUserUnlocked(userHandle.getIdentifier());
685 }
686
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700687 @Override protected void onPreExecute() {
688 View content = mTabHost.getTabContentView();
Brian Carlstrom729c6d92011-07-06 13:15:03 -0700689 mProgressBar = (ProgressBar) content.findViewById(mTab.mProgress);
Victor Chang9583dce2016-04-14 22:51:38 +0100690 mContentView = content.findViewById(mTab.mContentView);
Brian Carlstrom729c6d92011-07-06 13:15:03 -0700691 mProgressBar.setVisibility(View.VISIBLE);
Victor Chang9583dce2016-04-14 22:51:38 +0100692 mContentView.setVisibility(View.GONE);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700693 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100694 @Override protected SparseArray<List<CertHolder>> doInBackground(Void... params) {
695 SparseArray<List<CertHolder>> certHoldersByProfile =
696 new SparseArray<List<CertHolder>>();
697 try {
daqibe47df72017-09-05 09:53:05 +0800698 synchronized(mKeyChainConnectionByProfileId) {
699 List<UserHandle> profiles = mUserManager.getUserProfiles();
700 final int n = profiles.size();
701 // First we get all aliases for all profiles in order to show progress
702 // correctly. Otherwise this could all be in a single loop.
703 SparseArray<List<String>> aliasesByProfileId = new SparseArray<
704 List<String>>(n);
705 int max = 0;
706 int progress = 0;
707 for (int i = 0; i < n; ++i) {
708 UserHandle profile = profiles.get(i);
709 int profileId = profile.getIdentifier();
710 if (shouldSkipProfile(profile)) {
711 continue;
712 }
713 KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
714 profile);
715 // Saving the connection for later use on the certificate dialog.
716 mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
717 IKeyChainService service = keyChainConnection.getService();
718 List<String> aliases = mTab.getAliases(service);
719 if (isCancelled()) {
720 return new SparseArray<List<CertHolder>>();
721 }
722 max += aliases.size();
723 aliasesByProfileId.put(profileId, aliases);
Ricky Wai78fb3d32016-03-11 07:09:24 +0000724 }
daqibe47df72017-09-05 09:53:05 +0800725 for (int i = 0; i < n; ++i) {
726 UserHandle profile = profiles.get(i);
727 int profileId = profile.getIdentifier();
728 List<String> aliases = aliasesByProfileId.get(profileId);
729 if (isCancelled()) {
730 return new SparseArray<List<CertHolder>>();
731 }
732 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
733 profileId);
734 if (shouldSkipProfile(profile) || aliases == null
735 || keyChainConnection == null) {
736 certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0));
737 continue;
738 }
739 IKeyChainService service = keyChainConnection.getService();
740 List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
741 final int aliasMax = aliases.size();
742 for (int j = 0; j < aliasMax; ++j) {
743 String alias = aliases.get(j);
744 byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
745 true);
746 X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
747 certHolders.add(new CertHolder(service, mAdapter,
748 mTab, alias, cert, profileId));
749 publishProgress(++progress, max);
750 }
751 Collections.sort(certHolders);
752 certHoldersByProfile.put(profileId, certHolders);
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100753 }
daqibe47df72017-09-05 09:53:05 +0800754 return certHoldersByProfile;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100755 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100756 } catch (RemoteException e) {
757 Log.e(TAG, "Remote exception while loading aliases.", e);
758 return new SparseArray<List<CertHolder>>();
759 } catch (InterruptedException e) {
760 Log.e(TAG, "InterruptedException while loading aliases.", e);
761 return new SparseArray<List<CertHolder>>();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700762 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700763 }
Brian Carlstrom729c6d92011-07-06 13:15:03 -0700764 @Override protected void onProgressUpdate(Integer... progressAndMax) {
765 int progress = progressAndMax[0];
766 int max = progressAndMax[1];
767 if (max != mProgressBar.getMax()) {
768 mProgressBar.setMax(max);
769 }
770 mProgressBar.setProgress(progress);
771 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100772 @Override protected void onPostExecute(SparseArray<List<CertHolder>> certHolders) {
773 mCertHoldersByUserId.clear();
774 final int n = certHolders.size();
775 for (int i = 0; i < n; ++i) {
776 mCertHoldersByUserId.put(certHolders.keyAt(i), certHolders.valueAt(i));
777 }
778 mAdapter.notifyDataSetChanged();
Brian Carlstrom945e3832011-07-06 15:13:03 -0700779 mProgressBar.setVisibility(View.GONE);
Victor Chang9583dce2016-04-14 22:51:38 +0100780 mContentView.setVisibility(View.VISIBLE);
Brian Carlstrom945e3832011-07-06 15:13:03 -0700781 mProgressBar.setProgress(0);
Victor Chang9583dce2016-04-14 22:51:38 +0100782 mAliasLoaders.remove(this);
Victor Changcaa88772016-04-05 22:06:49 +0100783 showTrustAllCaDialogIfNeeded();
784 }
785
786 private boolean isUserTabAndTrustAllCertMode() {
787 return isTrustAllCaCertModeInProgress() && mTab == Tab.USER;
788 }
789
790 @UiThread
791 private void showTrustAllCaDialogIfNeeded() {
792 if (!isUserTabAndTrustAllCertMode()) {
793 return;
794 }
795 List<CertHolder> certHolders = mCertHoldersByUserId.get(mTrustAllCaUserId);
796 if (certHolders == null) {
797 return;
798 }
799
800 List<CertHolder> unapprovedUserCertHolders = new ArrayList<>();
801 final DevicePolicyManager dpm = mContext.getSystemService(
802 DevicePolicyManager.class);
803 for (CertHolder cert : certHolders) {
804 if (cert != null && !dpm.isCaCertApproved(cert.mAlias, mTrustAllCaUserId)) {
805 unapprovedUserCertHolders.add(cert);
806 }
807 }
808
809 if (unapprovedUserCertHolders.size() == 0) {
810 Log.w(TAG, "no cert is pending approval for user " + mTrustAllCaUserId);
811 return;
812 }
813 showTrustAllCaDialog(unapprovedUserCertHolders);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700814 }
815 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100816
817 public void remove(CertHolder certHolder) {
Zoltan Szatmary-Ban50263ef2014-10-09 13:00:32 +0100818 if (mCertHoldersByUserId != null) {
819 final List<CertHolder> certs = mCertHoldersByUserId.get(certHolder.mProfileId);
820 if (certs != null) {
821 certs.remove(certHolder);
822 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100823 }
824 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700825 }
826
Victor Changd7d0e1b2016-04-05 20:01:24 +0100827 /* package */ static class CertHolder implements Comparable<CertHolder> {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100828 public int mProfileId;
829 private final IKeyChainService mService;
Victor Chang9583dce2016-04-14 22:51:38 +0100830 private final GroupAdapter mAdapter;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700831 private final Tab mTab;
832 private final String mAlias;
833 private final X509Certificate mX509Cert;
834
835 private final SslCertificate mSslCert;
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700836 private final String mSubjectPrimary;
837 private final String mSubjectSecondary;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700838 private boolean mDeleted;
839
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100840 private CertHolder(IKeyChainService service,
Victor Chang9583dce2016-04-14 22:51:38 +0100841 GroupAdapter adapter,
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700842 Tab tab,
843 String alias,
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100844 X509Certificate x509Cert,
845 int profileId) {
846 mProfileId = profileId;
847 mService = service;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700848 mAdapter = adapter;
849 mTab = tab;
850 mAlias = alias;
851 mX509Cert = x509Cert;
852
853 mSslCert = new SslCertificate(x509Cert);
854
855 String cn = mSslCert.getIssuedTo().getCName();
856 String o = mSslCert.getIssuedTo().getOName();
857 String ou = mSslCert.getIssuedTo().getUName();
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700858 // if we have a O, use O as primary subject, secondary prefer CN over OU
859 // if we don't have an O, use CN as primary, empty secondary
860 // if we don't have O or CN, use DName as primary, empty secondary
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700861 if (!o.isEmpty()) {
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700862 if (!cn.isEmpty()) {
863 mSubjectPrimary = o;
864 mSubjectSecondary = cn;
865 } else {
866 mSubjectPrimary = o;
867 mSubjectSecondary = ou;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700868 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700869 } else {
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700870 if (!cn.isEmpty()) {
871 mSubjectPrimary = cn;
872 mSubjectSecondary = "";
873 } else {
874 mSubjectPrimary = mSslCert.getIssuedTo().getDName();
875 mSubjectSecondary = "";
876 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700877 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100878 try {
879 mDeleted = mTab.deleted(mService, mAlias);
880 } catch (RemoteException e) {
881 Log.e(TAG, "Remote exception while checking if alias " + mAlias + " is deleted.",
882 e);
883 mDeleted = false;
884 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700885 }
886 @Override public int compareTo(CertHolder o) {
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700887 int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary);
888 if (primary != 0) {
889 return primary;
890 }
891 return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700892 }
893 @Override public boolean equals(Object o) {
894 if (!(o instanceof CertHolder)) {
895 return false;
896 }
897 CertHolder other = (CertHolder) o;
898 return mAlias.equals(other.mAlias);
899 }
900 @Override public int hashCode() {
901 return mAlias.hashCode();
902 }
Victor Changd7d0e1b2016-04-05 20:01:24 +0100903
904 public int getUserId() {
905 return mProfileId;
906 }
907
908 public String getAlias() {
909 return mAlias;
910 }
911
912 public boolean isSystemCert() {
913 return mTab == Tab.SYSTEM;
914 }
915
916 public boolean isDeleted() {
917 return mDeleted;
918 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700919 }
920
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700921
Victor Changcaa88772016-04-05 22:06:49 +0100922 private boolean isTrustAllCaCertModeInProgress() {
923 return mTrustAllCaUserId != UserHandle.USER_NULL;
924 }
925
926 private void showTrustAllCaDialog(List<CertHolder> unapprovedCertHolders) {
927 final CertHolder[] arr = unapprovedCertHolders.toArray(
928 new CertHolder[unapprovedCertHolders.size()]);
929 new TrustedCredentialsDialogBuilder(getActivity(), this)
930 .setCertHolders(arr)
931 .setOnDismissListener(new DialogInterface.OnDismissListener() {
932 @Override
933 public void onDismiss(DialogInterface dialogInterface) {
934 // Avoid starting dialog again after Activity restart.
935 getActivity().getIntent().removeExtra(ARG_SHOW_NEW_FOR_USER);
936 mTrustAllCaUserId = UserHandle.USER_NULL;
937 }
938 })
939 .show();
940 }
941
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700942 private void showCertDialog(final CertHolder certHolder) {
Victor Changd7d0e1b2016-04-05 20:01:24 +0100943 new TrustedCredentialsDialogBuilder(getActivity(), this)
944 .setCertHolder(certHolder)
945 .show();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700946 }
947
Victor Changd7d0e1b2016-04-05 20:01:24 +0100948 @Override
949 public List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder) {
Selim Gurunf64facc2013-12-27 18:05:26 -0800950 List<X509Certificate> certificates = null;
951 try {
daqibe47df72017-09-05 09:53:05 +0800952 synchronized (mKeyChainConnectionByProfileId) {
953 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
954 certHolder.mProfileId);
955 IKeyChainService service = keyChainConnection.getService();
956 List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
957 final int n = chain.size();
958 certificates = new ArrayList<X509Certificate>(n);
959 for (int i = 0; i < n; ++i) {
960 byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
961 X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
962 certificates.add(certificate);
963 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100964 }
965 } catch (RemoteException ex) {
966 Log.e(TAG, "RemoteException while retrieving certificate chain for root "
967 + certHolder.mAlias, ex);
Selim Gurunf64facc2013-12-27 18:05:26 -0800968 }
Victor Changd7d0e1b2016-04-05 20:01:24 +0100969 return certificates;
Selim Gurunf64facc2013-12-27 18:05:26 -0800970 }
971
Victor Changd7d0e1b2016-04-05 20:01:24 +0100972 @Override
973 public void removeOrInstallCert(CertHolder certHolder) {
974 new AliasOperation(certHolder).execute();
Selim Gurunf64facc2013-12-27 18:05:26 -0800975 }
976
Victor Chang01f4dbc2016-05-09 16:13:10 +0100977 @Override
Victor Chang45ca9062016-05-23 19:47:38 +0100978 public boolean startConfirmCredentialIfNotConfirmed(int userId,
979 IntConsumer onCredentialConfirmedListener) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100980 if (mConfirmedCredentialUsers.contains(userId)) {
981 // Credential has been confirmed. Don't start activity.
982 return false;
983 }
Victor Chang45ca9062016-05-23 19:47:38 +0100984
985 boolean result = startConfirmCredential(userId);
986 if (result) {
987 mConfirmingCredentialListener = onCredentialConfirmedListener;
988 }
989 return result;
Victor Chang01f4dbc2016-05-09 16:13:10 +0100990 }
991
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700992 private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
993 private final CertHolder mCertHolder;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100994
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700995 private AliasOperation(CertHolder certHolder) {
996 mCertHolder = certHolder;
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100997 mAliasOperation = this;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700998 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100999
1000 @Override
1001 protected Boolean doInBackground(Void... params) {
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001002 try {
daqibe47df72017-09-05 09:53:05 +08001003 synchronized (mKeyChainConnectionByProfileId) {
1004 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
1005 mCertHolder.mProfileId);
1006 IKeyChainService service = keyChainConnection.getService();
1007 if (mCertHolder.mDeleted) {
1008 byte[] bytes = mCertHolder.mX509Cert.getEncoded();
1009 service.installCaCertificate(bytes);
1010 return true;
1011 } else {
1012 return service.deleteCaCertificate(mCertHolder.mAlias);
1013 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001014 }
Zoltan Szatmary-Banfb1bdf02015-01-05 16:41:35 +00001015 } catch (CertificateEncodingException | SecurityException | IllegalStateException
1016 | RemoteException e) {
Victor Changd7d0e1b2016-04-05 20:01:24 +01001017 Log.w(TAG, "Error while toggling alias " + mCertHolder.mAlias, e);
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001018 return false;
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001019 }
1020 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +01001021
1022 @Override
1023 protected void onPostExecute(Boolean ok) {
Victor Chang9583dce2016-04-14 22:51:38 +01001024 if (ok) {
1025 if (mCertHolder.mTab.mSwitch) {
1026 mCertHolder.mDeleted = !mCertHolder.mDeleted;
1027 } else {
1028 mCertHolder.mAdapter.remove(mCertHolder);
1029 }
1030 mCertHolder.mAdapter.notifyDataSetChanged();
1031 } else {
1032 // bail, reload to reset to known state
1033 mCertHolder.mAdapter.load();
1034 }
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +01001035 mAliasOperation = null;
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001036 }
1037 }
1038}