blob: ae25ba939f6a668e1bd825077d01707fe78ac2e4 [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;
Victor Changcaa88772016-04-05 22:06:49 +010023import android.annotation.UiThread;
Victor Chang01f4dbc2016-05-09 16:13:10 +010024import android.app.Activity;
Ricky Wai78fb3d32016-03-11 07:09:24 +000025import android.app.KeyguardManager;
Victor Changcaa88772016-04-05 22:06:49 +010026import android.app.admin.DevicePolicyManager;
Fan Zhang31b21002019-01-16 13:49:47 -080027import android.app.settings.SettingsEnums;
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;
Fan Zhangc7162cd2018-06-18 15:21:41 -070046import android.util.ArraySet;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +010047import android.util.Log;
Jason Monk39b46742015-09-10 15:52:51 -040048import android.util.SparseArray;
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;
Ricky Wai78fb3d32016-03-11 07:09:24 +000067import com.android.internal.widget.LockPatternUtils;
Doris Ling72489722017-11-16 11:03:40 -080068import com.android.settings.core.InstrumentedFragment;
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
Doris Ling72489722017-11-16 11:03:40 -080078public class TrustedCredentialsSettings extends InstrumentedFragment
Victor Changd7d0e1b2016-04-05 20:01:24 +010079 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
Fan Zhang65076132016-08-08 10:25:13 -070095 public int getMetricsCategory() {
Fan Zhang31b21002019-01-16 13:49:47 -080096 return SettingsEnums.TRUSTED_CREDENTIALS;
Chris Wren8a963ba2015-03-20 10:29:14 -040097 }
98
Brian Carlstromf6f4e302011-06-26 16:05:21 -070099 private enum Tab {
100 SYSTEM("system",
Victor Chang9583dce2016-04-14 22:51:38 +0100101 R.string.trusted_credentials_system_tab,
102 R.id.system_tab,
103 R.id.system_progress,
Victor Chang9583dce2016-04-14 22:51:38 +0100104 R.id.system_content,
Robin Lee232f0572016-08-02 10:52:22 +0100105 true),
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700106 USER("user",
Victor Chang9583dce2016-04-14 22:51:38 +0100107 R.string.trusted_credentials_user_tab,
108 R.id.user_tab,
109 R.id.user_progress,
Victor Chang9583dce2016-04-14 22:51:38 +0100110 R.id.user_content,
111 false);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700112
113 private final String mTag;
114 private final int mLabel;
115 private final int mView;
116 private final int mProgress;
Victor Chang9583dce2016-04-14 22:51:38 +0100117 private final int mContentView;
Fabrice Di Meglio44db45a2014-10-17 14:06:22 -0700118 private final boolean mSwitch;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100119
Doris Ling72489722017-11-16 11:03:40 -0800120 private Tab(String tag, int label, int view, int progress, int contentView,
121 boolean withSwitch) {
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700122 mTag = tag;
123 mLabel = label;
124 mView = view;
125 mProgress = progress;
Victor Chang9583dce2016-04-14 22:51:38 +0100126 mContentView = contentView;
Fabrice Di Meglio44db45a2014-10-17 14:06:22 -0700127 mSwitch = withSwitch;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700128 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100129
Robin Leeb50e6812017-02-20 21:02:45 +0000130 private List<String> getAliases(IKeyChainService service) throws RemoteException {
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700131 switch (this) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100132 case SYSTEM: {
133 return service.getSystemCaAliases().getList();
134 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700135 case USER:
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100136 return service.getUserCaAliases().getList();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700137 }
138 throw new AssertionError();
139 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100140 private boolean deleted(IKeyChainService service, String alias) throws RemoteException {
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700141 switch (this) {
142 case SYSTEM:
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100143 return !service.containsCaAlias(alias);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700144 case USER:
145 return false;
146 }
147 throw new AssertionError();
148 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700149 }
150
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700151 private TabHost mTabHost;
Victor Chang9583dce2016-04-14 22:51:38 +0100152 private ArrayList<GroupAdapter> mGroupAdapters = new ArrayList<>(2);
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100153 private AliasOperation mAliasOperation;
Victor Chang01f4dbc2016-05-09 16:13:10 +0100154 private ArraySet<Integer> mConfirmedCredentialUsers;
155 private int mConfirmingCredentialUser;
Victor Chang45ca9062016-05-23 19:47:38 +0100156 private IntConsumer mConfirmingCredentialListener;
Victor Chang9583dce2016-04-14 22:51:38 +0100157 private Set<AdapterData.AliasLoader> mAliasLoaders = new ArraySet<AdapterData.AliasLoader>(2);
daqibe47df72017-09-05 09:53:05 +0800158 @GuardedBy("mKeyChainConnectionByProfileId")
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100159 private final SparseArray<KeyChainConnection>
160 mKeyChainConnectionByProfileId = new SparseArray<KeyChainConnection>();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700161
Ricky Wai78fb3d32016-03-11 07:09:24 +0000162 private BroadcastReceiver mWorkProfileChangedReceiver = new BroadcastReceiver() {
163
164 @Override
165 public void onReceive(Context context, Intent intent) {
166 final String action = intent.getAction();
Rubin Xu819f78e2016-04-04 17:23:46 +0100167 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
168 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
Ricky Wai78fb3d32016-03-11 07:09:24 +0000169 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
Victor Chang9583dce2016-04-14 22:51:38 +0100170 for (GroupAdapter adapter : mGroupAdapters) {
171 adapter.load();
Ricky Wai78fb3d32016-03-11 07:09:24 +0000172 }
173 }
174 }
175
176 };
177
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -0400178 @Override
179 public void onCreate(Bundle savedInstanceState) {
180 super.onCreate(savedInstanceState);
Doris Linged4685f2017-10-25 14:08:57 -0700181 final Activity activity = getActivity();
182 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
183 mKeyguardManager = (KeyguardManager) activity
Ricky Wai78fb3d32016-03-11 07:09:24 +0000184 .getSystemService(Context.KEYGUARD_SERVICE);
Doris Linged4685f2017-10-25 14:08:57 -0700185 mTrustAllCaUserId = activity.getIntent().getIntExtra(ARG_SHOW_NEW_FOR_USER,
Victor Changcaa88772016-04-05 22:06:49 +0100186 UserHandle.USER_NULL);
Victor Chang01f4dbc2016-05-09 16:13:10 +0100187 mConfirmedCredentialUsers = new ArraySet<>(2);
188 mConfirmingCredentialUser = UserHandle.USER_NULL;
189 if (savedInstanceState != null) {
190 mConfirmingCredentialUser = savedInstanceState.getInt(SAVED_CONFIRMING_CREDENTIAL_USER,
191 UserHandle.USER_NULL);
192 ArrayList<Integer> users = savedInstanceState.getIntegerArrayList(
193 SAVED_CONFIRMED_CREDENTIAL_USERS);
194 if (users != null) {
195 mConfirmedCredentialUsers.addAll(users);
196 }
197 }
Victor Changcaa88772016-04-05 22:06:49 +0100198
Victor Chang45ca9062016-05-23 19:47:38 +0100199 mConfirmingCredentialListener = null;
200
Ricky Wai78fb3d32016-03-11 07:09:24 +0000201 IntentFilter filter = new IntentFilter();
Rubin Xu819f78e2016-04-04 17:23:46 +0100202 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
203 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
Ricky Wai78fb3d32016-03-11 07:09:24 +0000204 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
Doris Linged4685f2017-10-25 14:08:57 -0700205 activity.registerReceiver(mWorkProfileChangedReceiver, filter);
206
Doris Ling4a012832017-11-13 17:58:13 -0800207 activity.setTitle(R.string.trusted_credentials);
Geoffrey Borggaard6e1102d2013-08-07 14:57:43 -0400208 }
209
Victor Chang01f4dbc2016-05-09 16:13:10 +0100210 @Override
211 public void onSaveInstanceState(Bundle outState) {
212 super.onSaveInstanceState(outState);
213 outState.putIntegerArrayList(SAVED_CONFIRMED_CREDENTIAL_USERS, new ArrayList<>(
214 mConfirmedCredentialUsers));
215 outState.putInt(SAVED_CONFIRMING_CREDENTIAL_USER, mConfirmingCredentialUser);
216 }
217
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700218 @Override public View onCreateView(
219 LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
220 mTabHost = (TabHost) inflater.inflate(R.layout.trusted_credentials, parent, false);
221 mTabHost.setup();
222 addTab(Tab.SYSTEM);
223 // TODO add Install button on Tab.USER to go to CertInstaller like KeyChainActivity
224 addTab(Tab.USER);
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -0400225 if (getActivity().getIntent() != null &&
226 USER_ACTION.equals(getActivity().getIntent().getAction())) {
227 mTabHost.setCurrentTabByTag(Tab.USER.mTag);
228 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700229 return mTabHost;
230 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100231 @Override
232 public void onDestroy() {
Ricky Wai78fb3d32016-03-11 07:09:24 +0000233 getActivity().unregisterReceiver(mWorkProfileChangedReceiver);
Victor Chang9583dce2016-04-14 22:51:38 +0100234 for (AdapterData.AliasLoader aliasLoader : mAliasLoaders) {
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100235 aliasLoader.cancel(true);
236 }
Victor Chang9583dce2016-04-14 22:51:38 +0100237 mAliasLoaders.clear();
238 mGroupAdapters.clear();
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100239 if (mAliasOperation != null) {
240 mAliasOperation.cancel(true);
241 mAliasOperation = null;
242 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100243 closeKeyChainConnections();
244 super.onDestroy();
245 }
246
Victor Chang01f4dbc2016-05-09 16:13:10 +0100247 @Override
248 public void onActivityResult(int requestCode, int resultCode, Intent data) {
249 if (requestCode == REQUEST_CONFIRM_CREDENTIALS) {
Victor Chang45ca9062016-05-23 19:47:38 +0100250 int userId = mConfirmingCredentialUser;
251 IntConsumer listener = mConfirmingCredentialListener;
252 // reset them before calling the listener because the listener may call back to start
253 // activity again. (though it should never happen.)
Victor Chang01f4dbc2016-05-09 16:13:10 +0100254 mConfirmingCredentialUser = UserHandle.USER_NULL;
Victor Chang45ca9062016-05-23 19:47:38 +0100255 mConfirmingCredentialListener = null;
256 if (resultCode == Activity.RESULT_OK) {
257 mConfirmedCredentialUsers.add(userId);
258 if (listener != null) {
259 listener.accept(userId);
260 }
261 }
Victor Chang01f4dbc2016-05-09 16:13:10 +0100262 }
263 }
264
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100265 private void closeKeyChainConnections() {
daqibe47df72017-09-05 09:53:05 +0800266 synchronized (mKeyChainConnectionByProfileId) {
267 final int n = mKeyChainConnectionByProfileId.size();
268 for (int i = 0; i < n; ++i) {
269 mKeyChainConnectionByProfileId.valueAt(i).close();
270 }
271 mKeyChainConnectionByProfileId.clear();
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100272 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100273 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700274
275 private void addTab(Tab tab) {
276 TabHost.TabSpec systemSpec = mTabHost.newTabSpec(tab.mTag)
277 .setIndicator(getActivity().getString(tab.mLabel))
278 .setContent(tab.mView);
279 mTabHost.addTab(systemSpec);
280
Victor Chang9583dce2016-04-14 22:51:38 +0100281 final GroupAdapter groupAdapter = new GroupAdapter(tab);
282 mGroupAdapters.add(groupAdapter);
Robin Lee232f0572016-08-02 10:52:22 +0100283 final int profilesSize = groupAdapter.getGroupCount();
Ricky Wai78fb3d32016-03-11 07:09:24 +0000284
Robin Lee232f0572016-08-02 10:52:22 +0100285 // Add a transition for non-visibility events like resizing the pane.
286 final ViewGroup contentView = (ViewGroup) mTabHost.findViewById(tab.mContentView);
287 contentView.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
Victor Chang9583dce2016-04-14 22:51:38 +0100288
Robin Lee232f0572016-08-02 10:52:22 +0100289 final LayoutInflater inflater = LayoutInflater.from(getActivity());
290 for (int i = 0; i < groupAdapter.getGroupCount(); i++) {
291 final boolean isWork = groupAdapter.getUserInfoByGroup(i).isManagedProfile();
292 final ChildAdapter adapter = groupAdapter.getChildAdapter(i);
Victor Chang9583dce2016-04-14 22:51:38 +0100293
Robin Lee232f0572016-08-02 10:52:22 +0100294 final LinearLayout containerView = (LinearLayout) inflater
295 .inflate(R.layout.trusted_credential_list_container, contentView, false);
296 adapter.setContainerView(containerView);
297
298 adapter.showHeader(profilesSize > 1);
299 adapter.showDivider(isWork);
300 adapter.setExpandIfAvailable(profilesSize <= 2 ? true : !isWork);
301 if (isWork) {
302 contentView.addView(containerView);
303 } else {
304 contentView.addView(containerView, 0);
305 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100306 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700307 }
308
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100309 /**
Victor Chang01f4dbc2016-05-09 16:13:10 +0100310 * Start work challenge activity.
311 * @return true if screenlock exists
Ricky Wai78fb3d32016-03-11 07:09:24 +0000312 */
Victor Chang01f4dbc2016-05-09 16:13:10 +0100313 private boolean startConfirmCredential(int userId) {
Ricky Wai78fb3d32016-03-11 07:09:24 +0000314 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
315 userId);
Victor Chang01f4dbc2016-05-09 16:13:10 +0100316 if (newIntent == null) {
317 return false;
318 }
319 mConfirmingCredentialUser = userId;
320 startActivityForResult(newIntent, REQUEST_CONFIRM_CREDENTIALS);
321 return true;
Ricky Wai78fb3d32016-03-11 07:09:24 +0000322 }
323
324 /**
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100325 * Adapter for expandable list view of certificates. Groups in the view correspond to profiles
326 * whereas children correspond to certificates.
327 */
Victor Chang9583dce2016-04-14 22:51:38 +0100328 private class GroupAdapter extends BaseExpandableListAdapter implements
Robin Leebed85592016-09-01 18:35:00 +0100329 ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener,
330 View.OnClickListener {
Victor Chang9583dce2016-04-14 22:51:38 +0100331 private final AdapterData mData;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100332
Victor Chang9583dce2016-04-14 22:51:38 +0100333 private GroupAdapter(Tab tab) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100334 mData = new AdapterData(tab, this);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700335 load();
336 }
Victor Chang9583dce2016-04-14 22:51:38 +0100337
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100338 @Override
339 public int getGroupCount() {
340 return mData.mCertHoldersByUserId.size();
341 }
342 @Override
343 public int getChildrenCount(int groupPosition) {
Zoltan Szatmary-Ban860e1e12014-09-10 12:22:36 +0100344 List<CertHolder> certHolders = mData.mCertHoldersByUserId.valueAt(groupPosition);
345 if (certHolders != null) {
346 return certHolders.size();
347 }
348 return 0;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100349 }
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) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100356 return mData.mCertHoldersByUserId.get(getUserIdByGroup(groupPosition)).get(
357 childPosition);
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100358 }
359 @Override
360 public long getGroupId(int groupPosition) {
Victor Chang9583dce2016-04-14 22:51:38 +0100361 return getUserIdByGroup(groupPosition);
362 }
363 private int getUserIdByGroup(int groupPosition) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100364 return mData.mCertHoldersByUserId.keyAt(groupPosition);
365 }
Victor Chang9583dce2016-04-14 22:51:38 +0100366 public UserInfo getUserInfoByGroup(int groupPosition) {
367 return mUserManager.getUserInfo(getUserIdByGroup(groupPosition));
368 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100369 @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);
Zoltan Szatmary-Ban3af2e4c2014-12-19 17:17:23 +0000383 convertView = Utils.inflateCategoryHeader(inflater, parent);
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100384 }
385
386 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
Victor Chang9583dce2016-04-14 22:51:38 +0100387 if (getUserInfoByGroup(groupPosition).isManagedProfile()) {
Zoltan Szatmary-Banaf0f89f2014-09-30 16:30:07 +0100388 title.setText(R.string.category_work);
389 } else {
390 title.setText(R.string.category_personal);
391 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100392 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 }
Victor Chang9583dce2016-04-14 22:51:38 +0100406
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100407 @Override
Victor Chang9583dce2016-04-14 22:51:38 +0100408 public boolean onChildClick(ExpandableListView expandableListView, View view,
409 int groupPosition, int childPosition, long id) {
410 showCertDialog(getChild(groupPosition, childPosition));
411 return true;
412 }
413
Robin Leebed85592016-09-01 18:35:00 +0100414 /**
415 * Called when the switch on a system certificate is clicked. This will toggle whether it
416 * is trusted as a credential.
417 */
418 @Override
419 public void onClick(View view) {
420 CertHolder holder = (CertHolder) view.getTag();
421 removeOrInstallCert(holder);
422 }
423
Victor Chang9583dce2016-04-14 22:51:38 +0100424 @Override
425 public boolean onGroupClick(ExpandableListView expandableListView, View view,
426 int groupPosition, long id) {
427 return !checkGroupExpandableAndStartWarningActivity(groupPosition);
428 }
429
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100430 public void load() {
431 mData.new AliasLoader().execute();
432 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100433
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100434 public void remove(CertHolder certHolder) {
435 mData.remove(certHolder);
436 }
Victor Chang9583dce2016-04-14 22:51:38 +0100437
438 public void setExpandableListView(ExpandableListView lv) {
439 lv.setAdapter(this);
440 lv.setOnGroupClickListener(this);
441 lv.setOnChildClickListener(this);
442 lv.setVisibility(View.VISIBLE);
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100443 }
Victor Chang9583dce2016-04-14 22:51:38 +0100444
445 public ChildAdapter getChildAdapter(int groupPosition) {
446 return new ChildAdapter(this, groupPosition);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700447 }
Victor Chang9583dce2016-04-14 22:51:38 +0100448
449 public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100450 return checkGroupExpandableAndStartWarningActivity(groupPosition, true);
451 }
452
453 public boolean checkGroupExpandableAndStartWarningActivity(int groupPosition,
454 boolean startActivity) {
Victor Chang9583dce2016-04-14 22:51:38 +0100455 final UserHandle groupUser = getGroup(groupPosition);
456 final int groupUserId = groupUser.getIdentifier();
457 if (mUserManager.isQuietModeEnabled(groupUser)) {
458 final Intent intent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
459 groupUserId);
Victor Chang01f4dbc2016-05-09 16:13:10 +0100460 if (startActivity) {
461 getActivity().startActivity(intent);
462 }
Victor Chang9583dce2016-04-14 22:51:38 +0100463 return false;
464 } else if (!mUserManager.isUserUnlocked(groupUser)) {
465 final LockPatternUtils lockPatternUtils = new LockPatternUtils(
466 getActivity());
467 if (lockPatternUtils.isSeparateProfileChallengeEnabled(groupUserId)) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100468 if (startActivity) {
469 startConfirmCredential(groupUserId);
470 }
Victor Chang9583dce2016-04-14 22:51:38 +0100471 return false;
472 }
Zoltan Szatmary-Ban860e1e12014-09-10 12:22:36 +0100473 }
Victor Chang9583dce2016-04-14 22:51:38 +0100474 return true;
475 }
476
477 private View getViewForCertificate(CertHolder certHolder, Tab mTab, View convertView,
478 ViewGroup parent) {
479 ViewHolder holder;
480 if (convertView == null) {
Robin Leebed85592016-09-01 18:35:00 +0100481 holder = new ViewHolder();
Victor Chang9583dce2016-04-14 22:51:38 +0100482 LayoutInflater inflater = LayoutInflater.from(getActivity());
483 convertView = inflater.inflate(R.layout.trusted_credential, parent, false);
Robin Leebed85592016-09-01 18:35:00 +0100484 convertView.setTag(holder);
Victor Chang9583dce2016-04-14 22:51:38 +0100485 holder.mSubjectPrimaryView = (TextView)
486 convertView.findViewById(R.id.trusted_credential_subject_primary);
487 holder.mSubjectSecondaryView = (TextView)
488 convertView.findViewById(R.id.trusted_credential_subject_secondary);
489 holder.mSwitch = (Switch) convertView.findViewById(
490 R.id.trusted_credential_status);
Robin Leebed85592016-09-01 18:35:00 +0100491 holder.mSwitch.setOnClickListener(this);
Victor Chang9583dce2016-04-14 22:51:38 +0100492 } else {
493 holder = (ViewHolder) convertView.getTag();
494 }
495 holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary);
496 holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary);
497 if (mTab.mSwitch) {
498 holder.mSwitch.setChecked(!certHolder.mDeleted);
499 holder.mSwitch.setEnabled(!mUserManager.hasUserRestriction(
500 UserManager.DISALLOW_CONFIG_CREDENTIALS,
501 new UserHandle(certHolder.mProfileId)));
502 holder.mSwitch.setVisibility(View.VISIBLE);
Robin Leebed85592016-09-01 18:35:00 +0100503 holder.mSwitch.setTag(certHolder);
Victor Chang9583dce2016-04-14 22:51:38 +0100504 }
505 return convertView;
506 }
507
508 private class ViewHolder {
509 private TextView mSubjectPrimaryView;
510 private TextView mSubjectSecondaryView;
511 private Switch mSwitch;
512 }
513 }
514
515 private class ChildAdapter extends BaseAdapter implements View.OnClickListener,
516 AdapterView.OnItemClickListener {
517 private final int[] GROUP_EXPANDED_STATE_SET = {com.android.internal.R.attr.state_expanded};
518 private final int[] EMPTY_STATE_SET = {};
Robin Lee232f0572016-08-02 10:52:22 +0100519 private final LinearLayout.LayoutParams HIDE_CONTAINER_LAYOUT_PARAMS =
520 new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0f);
521 private final LinearLayout.LayoutParams HIDE_LIST_LAYOUT_PARAMS =
522 new LinearLayout.LayoutParams(MATCH_PARENT, 0);
Victor Chang9583dce2016-04-14 22:51:38 +0100523 private final LinearLayout.LayoutParams SHOW_LAYOUT_PARAMS = new LinearLayout.LayoutParams(
Robin Lee232f0572016-08-02 10:52:22 +0100524 LinearLayout.LayoutParams.MATCH_PARENT, MATCH_PARENT, 1f);
Victor Chang9583dce2016-04-14 22:51:38 +0100525 private final GroupAdapter mParent;
526 private final int mGroupPosition;
527 /*
528 * This class doesn't hold the actual data. Events should notify parent.
529 * When notifying DataSet events in this class, events should be forwarded to mParent.
530 * i.e. this.notifyDataSetChanged -> mParent.notifyDataSetChanged -> mObserver.onChanged
531 * -> outsideObservers.onChanged() (e.g. ListView)
532 */
533 private final DataSetObserver mObserver = new DataSetObserver() {
534 @Override
535 public void onChanged() {
536 super.onChanged();
537 ChildAdapter.super.notifyDataSetChanged();
538 }
539 @Override
540 public void onInvalidated() {
541 super.onInvalidated();
542 ChildAdapter.super.notifyDataSetInvalidated();
543 }
544 };
545
546 private boolean mIsListExpanded = true;
547 private LinearLayout mContainerView;
548 private ViewGroup mHeaderView;
549 private ListView mListView;
550 private ImageView mIndicatorView;
551
552 private ChildAdapter(GroupAdapter parent, int groupPosition) {
553 mParent = parent;
554 mGroupPosition = groupPosition;
555 mParent.registerDataSetObserver(mObserver);
556 }
557
558 @Override public int getCount() {
559 return mParent.getChildrenCount(mGroupPosition);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700560 }
561 @Override public CertHolder getItem(int position) {
Victor Chang9583dce2016-04-14 22:51:38 +0100562 return mParent.getChild(mGroupPosition, position);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700563 }
564 @Override public long getItemId(int position) {
Victor Chang9583dce2016-04-14 22:51:38 +0100565 return mParent.getChildId(mGroupPosition, position);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700566 }
Victor Chang9583dce2016-04-14 22:51:38 +0100567 @Override public View getView(int position, View convertView, ViewGroup parent) {
568 return mParent.getChildView(mGroupPosition, position, false, convertView, parent);
569 }
570 // DataSet events
571 @Override
572 public void notifyDataSetChanged() {
573 // Don't call super as the parent will propagate this event back later in mObserver
574 mParent.notifyDataSetChanged();
575 }
576 @Override
577 public void notifyDataSetInvalidated() {
578 // Don't call super as the parent will propagate this event back later in mObserver
579 mParent.notifyDataSetInvalidated();
580 }
581
582 // View related codes
583 @Override
584 public void onClick(View view) {
585 mIsListExpanded = checkGroupExpandableAndStartWarningActivity() && !mIsListExpanded;
586 refreshViews();
587 }
588
589 @Override
590 public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
591 showCertDialog(getItem(pos));
592 }
593
Robin Lee232f0572016-08-02 10:52:22 +0100594 public void setContainerView(LinearLayout containerView) {
595 mContainerView = containerView;
Victor Chang9583dce2016-04-14 22:51:38 +0100596
597 mListView = (ListView) mContainerView.findViewById(R.id.cert_list);
598 mListView.setAdapter(this);
599 mListView.setOnItemClickListener(this);
Robin Leebed85592016-09-01 18:35:00 +0100600 mListView.setItemsCanFocus(true);
Victor Chang9583dce2016-04-14 22:51:38 +0100601
602 mHeaderView = (ViewGroup) mContainerView.findViewById(R.id.header_view);
603 mHeaderView.setOnClickListener(this);
604
605 mIndicatorView = (ImageView) mHeaderView.findViewById(R.id.group_indicator);
606 mIndicatorView.setImageDrawable(getGroupIndicator());
607
608 FrameLayout headerContentContainer = (FrameLayout)
609 mHeaderView.findViewById(R.id.header_content_container);
610 headerContentContainer.addView(
611 mParent.getGroupView(mGroupPosition, true /* parent ignores it */, null,
612 headerContentContainer));
613 }
614
615 public void showHeader(boolean showHeader) {
616 mHeaderView.setVisibility(showHeader ? View.VISIBLE : View.GONE);
617 }
618
619 public void showDivider(boolean showDivider) {
620 View dividerView = mHeaderView.findViewById(R.id.header_divider);
621 dividerView.setVisibility(showDivider ? View.VISIBLE : View.GONE );
622 }
623
Robin Lee232f0572016-08-02 10:52:22 +0100624 public void setExpandIfAvailable(boolean expanded) {
625 mIsListExpanded = expanded && mParent.checkGroupExpandableAndStartWarningActivity(
626 mGroupPosition, false /* startActivity */);
Victor Chang9583dce2016-04-14 22:51:38 +0100627 refreshViews();
628 }
629
630 private boolean checkGroupExpandableAndStartWarningActivity() {
631 return mParent.checkGroupExpandableAndStartWarningActivity(mGroupPosition);
632 }
633
634 private void refreshViews() {
635 mIndicatorView.setImageState(mIsListExpanded ? GROUP_EXPANDED_STATE_SET
636 : EMPTY_STATE_SET, false);
Robin Lee232f0572016-08-02 10:52:22 +0100637 mListView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
638 : HIDE_LIST_LAYOUT_PARAMS);
Victor Chang9583dce2016-04-14 22:51:38 +0100639 mContainerView.setLayoutParams(mIsListExpanded ? SHOW_LAYOUT_PARAMS
Robin Lee232f0572016-08-02 10:52:22 +0100640 : HIDE_CONTAINER_LAYOUT_PARAMS);
Victor Chang9583dce2016-04-14 22:51:38 +0100641 }
642
643 // Get group indicator from styles of ExpandableListView
644 private Drawable getGroupIndicator() {
645 final TypedArray a = getActivity().obtainStyledAttributes(null,
646 com.android.internal.R.styleable.ExpandableListView,
647 com.android.internal.R.attr.expandableListViewStyle, 0);
648 Drawable groupIndicator = a.getDrawable(
649 com.android.internal.R.styleable.ExpandableListView_groupIndicator);
650 a.recycle();
651 return groupIndicator;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100652 }
653 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700654
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100655 private class AdapterData {
656 private final SparseArray<List<CertHolder>> mCertHoldersByUserId =
657 new SparseArray<List<CertHolder>>();
658 private final Tab mTab;
Victor Chang9583dce2016-04-14 22:51:38 +0100659 private final GroupAdapter mAdapter;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100660
Victor Chang9583dce2016-04-14 22:51:38 +0100661 private AdapterData(Tab tab, GroupAdapter adapter) {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100662 mAdapter = adapter;
663 mTab = tab;
664 }
665
666 private class AliasLoader extends AsyncTask<Void, Integer, SparseArray<List<CertHolder>>> {
667 private ProgressBar mProgressBar;
Victor Chang9583dce2016-04-14 22:51:38 +0100668 private View mContentView;
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100669 private Context mContext;
670
671 public AliasLoader() {
672 mContext = getActivity();
Victor Chang9583dce2016-04-14 22:51:38 +0100673 mAliasLoaders.add(this);
674 List<UserHandle> profiles = mUserManager.getUserProfiles();
675 for (UserHandle profile : profiles) {
676 mCertHoldersByUserId.put(profile.getIdentifier(), new ArrayList<CertHolder>());
677 }
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100678 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100679
Ricky Wai78fb3d32016-03-11 07:09:24 +0000680 private boolean shouldSkipProfile(UserHandle userHandle) {
681 return mUserManager.isQuietModeEnabled(userHandle)
682 || !mUserManager.isUserUnlocked(userHandle.getIdentifier());
683 }
684
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700685 @Override protected void onPreExecute() {
686 View content = mTabHost.getTabContentView();
Brian Carlstrom729c6d92011-07-06 13:15:03 -0700687 mProgressBar = (ProgressBar) content.findViewById(mTab.mProgress);
Victor Chang9583dce2016-04-14 22:51:38 +0100688 mContentView = content.findViewById(mTab.mContentView);
Brian Carlstrom729c6d92011-07-06 13:15:03 -0700689 mProgressBar.setVisibility(View.VISIBLE);
Victor Chang9583dce2016-04-14 22:51:38 +0100690 mContentView.setVisibility(View.GONE);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700691 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100692 @Override protected SparseArray<List<CertHolder>> doInBackground(Void... params) {
693 SparseArray<List<CertHolder>> certHoldersByProfile =
694 new SparseArray<List<CertHolder>>();
695 try {
daqibe47df72017-09-05 09:53:05 +0800696 synchronized(mKeyChainConnectionByProfileId) {
697 List<UserHandle> profiles = mUserManager.getUserProfiles();
698 final int n = profiles.size();
699 // First we get all aliases for all profiles in order to show progress
700 // correctly. Otherwise this could all be in a single loop.
701 SparseArray<List<String>> aliasesByProfileId = new SparseArray<
702 List<String>>(n);
703 int max = 0;
704 int progress = 0;
705 for (int i = 0; i < n; ++i) {
706 UserHandle profile = profiles.get(i);
707 int profileId = profile.getIdentifier();
708 if (shouldSkipProfile(profile)) {
709 continue;
710 }
711 KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
712 profile);
713 // Saving the connection for later use on the certificate dialog.
714 mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
715 IKeyChainService service = keyChainConnection.getService();
716 List<String> aliases = mTab.getAliases(service);
717 if (isCancelled()) {
718 return new SparseArray<List<CertHolder>>();
719 }
720 max += aliases.size();
721 aliasesByProfileId.put(profileId, aliases);
Ricky Wai78fb3d32016-03-11 07:09:24 +0000722 }
daqibe47df72017-09-05 09:53:05 +0800723 for (int i = 0; i < n; ++i) {
724 UserHandle profile = profiles.get(i);
725 int profileId = profile.getIdentifier();
726 List<String> aliases = aliasesByProfileId.get(profileId);
727 if (isCancelled()) {
728 return new SparseArray<List<CertHolder>>();
729 }
730 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
731 profileId);
732 if (shouldSkipProfile(profile) || aliases == null
733 || keyChainConnection == null) {
734 certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0));
735 continue;
736 }
737 IKeyChainService service = keyChainConnection.getService();
738 List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
739 final int aliasMax = aliases.size();
740 for (int j = 0; j < aliasMax; ++j) {
741 String alias = aliases.get(j);
742 byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
743 true);
744 X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
745 certHolders.add(new CertHolder(service, mAdapter,
746 mTab, alias, cert, profileId));
747 publishProgress(++progress, max);
748 }
749 Collections.sort(certHolders);
750 certHoldersByProfile.put(profileId, certHolders);
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100751 }
daqibe47df72017-09-05 09:53:05 +0800752 return certHoldersByProfile;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100753 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100754 } catch (RemoteException e) {
755 Log.e(TAG, "Remote exception while loading aliases.", e);
756 return new SparseArray<List<CertHolder>>();
757 } catch (InterruptedException e) {
758 Log.e(TAG, "InterruptedException while loading aliases.", e);
759 return new SparseArray<List<CertHolder>>();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700760 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700761 }
Brian Carlstrom729c6d92011-07-06 13:15:03 -0700762 @Override protected void onProgressUpdate(Integer... progressAndMax) {
763 int progress = progressAndMax[0];
764 int max = progressAndMax[1];
765 if (max != mProgressBar.getMax()) {
766 mProgressBar.setMax(max);
767 }
768 mProgressBar.setProgress(progress);
769 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100770 @Override protected void onPostExecute(SparseArray<List<CertHolder>> certHolders) {
771 mCertHoldersByUserId.clear();
772 final int n = certHolders.size();
773 for (int i = 0; i < n; ++i) {
774 mCertHoldersByUserId.put(certHolders.keyAt(i), certHolders.valueAt(i));
775 }
776 mAdapter.notifyDataSetChanged();
Brian Carlstrom945e3832011-07-06 15:13:03 -0700777 mProgressBar.setVisibility(View.GONE);
Victor Chang9583dce2016-04-14 22:51:38 +0100778 mContentView.setVisibility(View.VISIBLE);
Brian Carlstrom945e3832011-07-06 15:13:03 -0700779 mProgressBar.setProgress(0);
Victor Chang9583dce2016-04-14 22:51:38 +0100780 mAliasLoaders.remove(this);
Victor Changcaa88772016-04-05 22:06:49 +0100781 showTrustAllCaDialogIfNeeded();
782 }
783
784 private boolean isUserTabAndTrustAllCertMode() {
785 return isTrustAllCaCertModeInProgress() && mTab == Tab.USER;
786 }
787
788 @UiThread
789 private void showTrustAllCaDialogIfNeeded() {
790 if (!isUserTabAndTrustAllCertMode()) {
791 return;
792 }
793 List<CertHolder> certHolders = mCertHoldersByUserId.get(mTrustAllCaUserId);
794 if (certHolders == null) {
795 return;
796 }
797
798 List<CertHolder> unapprovedUserCertHolders = new ArrayList<>();
799 final DevicePolicyManager dpm = mContext.getSystemService(
800 DevicePolicyManager.class);
801 for (CertHolder cert : certHolders) {
802 if (cert != null && !dpm.isCaCertApproved(cert.mAlias, mTrustAllCaUserId)) {
803 unapprovedUserCertHolders.add(cert);
804 }
805 }
806
807 if (unapprovedUserCertHolders.size() == 0) {
808 Log.w(TAG, "no cert is pending approval for user " + mTrustAllCaUserId);
809 return;
810 }
811 showTrustAllCaDialog(unapprovedUserCertHolders);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700812 }
813 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100814
815 public void remove(CertHolder certHolder) {
Zoltan Szatmary-Ban50263ef2014-10-09 13:00:32 +0100816 if (mCertHoldersByUserId != null) {
817 final List<CertHolder> certs = mCertHoldersByUserId.get(certHolder.mProfileId);
818 if (certs != null) {
819 certs.remove(certHolder);
820 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100821 }
822 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700823 }
824
Victor Changd7d0e1b2016-04-05 20:01:24 +0100825 /* package */ static class CertHolder implements Comparable<CertHolder> {
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100826 public int mProfileId;
827 private final IKeyChainService mService;
Victor Chang9583dce2016-04-14 22:51:38 +0100828 private final GroupAdapter mAdapter;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700829 private final Tab mTab;
830 private final String mAlias;
831 private final X509Certificate mX509Cert;
832
833 private final SslCertificate mSslCert;
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700834 private final String mSubjectPrimary;
835 private final String mSubjectSecondary;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700836 private boolean mDeleted;
837
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100838 private CertHolder(IKeyChainService service,
Victor Chang9583dce2016-04-14 22:51:38 +0100839 GroupAdapter adapter,
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700840 Tab tab,
841 String alias,
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100842 X509Certificate x509Cert,
843 int profileId) {
844 mProfileId = profileId;
845 mService = service;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700846 mAdapter = adapter;
847 mTab = tab;
848 mAlias = alias;
849 mX509Cert = x509Cert;
850
851 mSslCert = new SslCertificate(x509Cert);
852
853 String cn = mSslCert.getIssuedTo().getCName();
854 String o = mSslCert.getIssuedTo().getOName();
855 String ou = mSslCert.getIssuedTo().getUName();
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700856 // if we have a O, use O as primary subject, secondary prefer CN over OU
857 // if we don't have an O, use CN as primary, empty secondary
858 // if we don't have O or CN, use DName as primary, empty secondary
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700859 if (!o.isEmpty()) {
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700860 if (!cn.isEmpty()) {
861 mSubjectPrimary = o;
862 mSubjectSecondary = cn;
863 } else {
864 mSubjectPrimary = o;
865 mSubjectSecondary = ou;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700866 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700867 } else {
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700868 if (!cn.isEmpty()) {
869 mSubjectPrimary = cn;
870 mSubjectSecondary = "";
871 } else {
872 mSubjectPrimary = mSslCert.getIssuedTo().getDName();
873 mSubjectSecondary = "";
874 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700875 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100876 try {
877 mDeleted = mTab.deleted(mService, mAlias);
878 } catch (RemoteException e) {
879 Log.e(TAG, "Remote exception while checking if alias " + mAlias + " is deleted.",
880 e);
881 mDeleted = false;
882 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700883 }
884 @Override public int compareTo(CertHolder o) {
Brian Carlstrom10cc9892011-07-05 23:51:39 -0700885 int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary);
886 if (primary != 0) {
887 return primary;
888 }
889 return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary);
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700890 }
891 @Override public boolean equals(Object o) {
892 if (!(o instanceof CertHolder)) {
893 return false;
894 }
895 CertHolder other = (CertHolder) o;
896 return mAlias.equals(other.mAlias);
897 }
898 @Override public int hashCode() {
899 return mAlias.hashCode();
900 }
Victor Changd7d0e1b2016-04-05 20:01:24 +0100901
902 public int getUserId() {
903 return mProfileId;
904 }
905
906 public String getAlias() {
907 return mAlias;
908 }
909
910 public boolean isSystemCert() {
911 return mTab == Tab.SYSTEM;
912 }
913
914 public boolean isDeleted() {
915 return mDeleted;
916 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700917 }
918
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700919
Victor Changcaa88772016-04-05 22:06:49 +0100920 private boolean isTrustAllCaCertModeInProgress() {
921 return mTrustAllCaUserId != UserHandle.USER_NULL;
922 }
923
924 private void showTrustAllCaDialog(List<CertHolder> unapprovedCertHolders) {
925 final CertHolder[] arr = unapprovedCertHolders.toArray(
926 new CertHolder[unapprovedCertHolders.size()]);
927 new TrustedCredentialsDialogBuilder(getActivity(), this)
928 .setCertHolders(arr)
929 .setOnDismissListener(new DialogInterface.OnDismissListener() {
930 @Override
931 public void onDismiss(DialogInterface dialogInterface) {
932 // Avoid starting dialog again after Activity restart.
933 getActivity().getIntent().removeExtra(ARG_SHOW_NEW_FOR_USER);
934 mTrustAllCaUserId = UserHandle.USER_NULL;
935 }
936 })
937 .show();
938 }
939
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700940 private void showCertDialog(final CertHolder certHolder) {
Victor Changd7d0e1b2016-04-05 20:01:24 +0100941 new TrustedCredentialsDialogBuilder(getActivity(), this)
942 .setCertHolder(certHolder)
943 .show();
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700944 }
945
Victor Changd7d0e1b2016-04-05 20:01:24 +0100946 @Override
947 public List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder) {
Selim Gurunf64facc2013-12-27 18:05:26 -0800948 List<X509Certificate> certificates = null;
949 try {
daqibe47df72017-09-05 09:53:05 +0800950 synchronized (mKeyChainConnectionByProfileId) {
951 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
952 certHolder.mProfileId);
953 IKeyChainService service = keyChainConnection.getService();
954 List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
955 final int n = chain.size();
956 certificates = new ArrayList<X509Certificate>(n);
957 for (int i = 0; i < n; ++i) {
958 byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
959 X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
960 certificates.add(certificate);
961 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100962 }
963 } catch (RemoteException ex) {
964 Log.e(TAG, "RemoteException while retrieving certificate chain for root "
965 + certHolder.mAlias, ex);
Selim Gurunf64facc2013-12-27 18:05:26 -0800966 }
Victor Changd7d0e1b2016-04-05 20:01:24 +0100967 return certificates;
Selim Gurunf64facc2013-12-27 18:05:26 -0800968 }
969
Victor Changd7d0e1b2016-04-05 20:01:24 +0100970 @Override
971 public void removeOrInstallCert(CertHolder certHolder) {
972 new AliasOperation(certHolder).execute();
Selim Gurunf64facc2013-12-27 18:05:26 -0800973 }
974
Victor Chang01f4dbc2016-05-09 16:13:10 +0100975 @Override
Victor Chang45ca9062016-05-23 19:47:38 +0100976 public boolean startConfirmCredentialIfNotConfirmed(int userId,
977 IntConsumer onCredentialConfirmedListener) {
Victor Chang01f4dbc2016-05-09 16:13:10 +0100978 if (mConfirmedCredentialUsers.contains(userId)) {
979 // Credential has been confirmed. Don't start activity.
980 return false;
981 }
Victor Chang45ca9062016-05-23 19:47:38 +0100982
983 boolean result = startConfirmCredential(userId);
984 if (result) {
985 mConfirmingCredentialListener = onCredentialConfirmedListener;
986 }
987 return result;
Victor Chang01f4dbc2016-05-09 16:13:10 +0100988 }
989
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700990 private class AliasOperation extends AsyncTask<Void, Void, Boolean> {
991 private final CertHolder mCertHolder;
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100992
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700993 private AliasOperation(CertHolder certHolder) {
994 mCertHolder = certHolder;
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +0100995 mAliasOperation = this;
Brian Carlstromf6f4e302011-06-26 16:05:21 -0700996 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +0100997
998 @Override
999 protected Boolean doInBackground(Void... params) {
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001000 try {
daqibe47df72017-09-05 09:53:05 +08001001 synchronized (mKeyChainConnectionByProfileId) {
1002 KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
1003 mCertHolder.mProfileId);
1004 IKeyChainService service = keyChainConnection.getService();
1005 if (mCertHolder.mDeleted) {
1006 byte[] bytes = mCertHolder.mX509Cert.getEncoded();
1007 service.installCaCertificate(bytes);
1008 return true;
1009 } else {
1010 return service.deleteCaCertificate(mCertHolder.mAlias);
1011 }
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001012 }
Zoltan Szatmary-Banfb1bdf02015-01-05 16:41:35 +00001013 } catch (CertificateEncodingException | SecurityException | IllegalStateException
1014 | RemoteException e) {
Victor Changd7d0e1b2016-04-05 20:01:24 +01001015 Log.w(TAG, "Error while toggling alias " + mCertHolder.mAlias, e);
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001016 return false;
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001017 }
1018 }
Zoltan Szatmary-Ban1613cb02014-07-15 16:52:39 +01001019
1020 @Override
1021 protected void onPostExecute(Boolean ok) {
Victor Chang9583dce2016-04-14 22:51:38 +01001022 if (ok) {
1023 if (mCertHolder.mTab.mSwitch) {
1024 mCertHolder.mDeleted = !mCertHolder.mDeleted;
1025 } else {
1026 mCertHolder.mAdapter.remove(mCertHolder);
1027 }
1028 mCertHolder.mAdapter.notifyDataSetChanged();
1029 } else {
1030 // bail, reload to reset to known state
1031 mCertHolder.mAdapter.load();
1032 }
Zoltan Szatmary-Bancfe521e2014-10-15 13:01:05 +01001033 mAliasOperation = null;
Brian Carlstromf6f4e302011-06-26 16:05:21 -07001034 }
1035 }
1036}