blob: d322819ba980db4473a4f99b98f3288ed41b324f [file] [log] [blame]
Robin Leebaefdcf2015-08-26 10:57:44 +01001/*
2 * Copyright (C) 2015 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 Leee68d9572016-07-19 16:10:39 +010019import android.annotation.LayoutRes;
20import android.annotation.Nullable;
Robin Lee04046a12016-01-19 11:42:57 +000021import android.app.Dialog;
Fan Zhang31b21002019-01-16 13:49:47 -080022import android.app.settings.SettingsEnums;
Robin Leebaefdcf2015-08-26 10:57:44 +010023import android.content.Context;
24import android.content.DialogInterface;
25import android.os.AsyncTask;
26import android.os.Bundle;
Robin Lee04046a12016-01-19 11:42:57 +000027import android.os.Parcel;
28import android.os.Parcelable;
Robin Leee68d9572016-07-19 16:10:39 +010029import android.os.Process;
Robin Leeda7bc512016-02-24 17:39:32 +000030import android.os.RemoteException;
Robin Leec421db72016-03-11 16:22:23 +000031import android.os.UserHandle;
32import android.os.UserManager;
Robin Leebaefdcf2015-08-26 10:57:44 +010033import android.security.Credentials;
Robin Leeda7bc512016-02-24 17:39:32 +000034import android.security.IKeyChainService;
35import android.security.KeyChain;
36import android.security.KeyChain.KeyChainConnection;
Robin Leebaefdcf2015-08-26 10:57:44 +010037import android.security.KeyStore;
Janis Danisevskisb705e1a2017-04-19 16:23:02 -070038import android.security.keymaster.KeyCharacteristics;
39import android.security.keymaster.KeymasterDefs;
Robin Leeda7bc512016-02-24 17:39:32 +000040import android.util.Log;
Robin Leee68d9572016-07-19 16:10:39 +010041import android.util.SparseArray;
Robin Leebaefdcf2015-08-26 10:57:44 +010042import android.view.LayoutInflater;
43import android.view.View;
44import android.view.ViewGroup;
Robin Leebaefdcf2015-08-26 10:57:44 +010045import android.widget.TextView;
46
Fan Zhang23f8d592018-08-28 15:11:40 -070047import androidx.appcompat.app.AlertDialog;
48import androidx.fragment.app.DialogFragment;
49import androidx.fragment.app.Fragment;
50import androidx.recyclerview.widget.RecyclerView;
51
Ricky Wai95792742016-05-24 19:28:53 +010052import com.android.internal.widget.LockPatternUtils;
Fan Zhang1e516282016-09-16 12:45:07 -070053import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
Robin Leec421db72016-03-11 16:22:23 +000054import com.android.settingslib.RestrictedLockUtils;
55import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
Philip P. Moltmanne3f72112018-08-28 15:01:43 -070056import com.android.settingslib.RestrictedLockUtilsInternal;
Fan Zhangc7162cd2018-06-18 15:21:41 -070057
Janis Danisevskisb705e1a2017-04-19 16:23:02 -070058import java.security.UnrecoverableKeyException;
Robin Leee68d9572016-07-19 16:10:39 +010059import java.util.ArrayList;
Robin Leebaefdcf2015-08-26 10:57:44 +010060import java.util.EnumSet;
Robin Leee68d9572016-07-19 16:10:39 +010061import java.util.List;
Robin Leebaefdcf2015-08-26 10:57:44 +010062import java.util.SortedMap;
63import java.util.TreeMap;
64
Robin Leeccaf9c92017-03-24 14:50:05 +000065public class UserCredentialsSettings extends SettingsPreferenceFragment
66 implements View.OnClickListener {
Robin Leebaefdcf2015-08-26 10:57:44 +010067 private static final String TAG = "UserCredentialsSettings";
68
Robin Leebaefdcf2015-08-26 10:57:44 +010069 @Override
Fan Zhang65076132016-08-08 10:25:13 -070070 public int getMetricsCategory() {
Fan Zhang31b21002019-01-16 13:49:47 -080071 return SettingsEnums.USER_CREDENTIALS;
Robin Leebaefdcf2015-08-26 10:57:44 +010072 }
73
74 @Override
75 public void onResume() {
76 super.onResume();
Robin Lee04046a12016-01-19 11:42:57 +000077 refreshItems();
Robin Leebaefdcf2015-08-26 10:57:44 +010078 }
79
80 @Override
Robin Leeccaf9c92017-03-24 14:50:05 +000081 public void onClick(final View view) {
82 final Credential item = (Credential) view.getTag();
83 if (item != null) {
84 CredentialDialogFragment.show(this, item);
85 }
Robin Lee04046a12016-01-19 11:42:57 +000086 }
Robin Leebaefdcf2015-08-26 10:57:44 +010087
Doris Ling03a3b512017-10-18 14:25:01 -070088 @Override
Doris Linged4685f2017-10-25 14:08:57 -070089 public void onCreate(@Nullable Bundle savedInstanceState) {
90 super.onCreate(savedInstanceState);
Doris Ling4a012832017-11-13 17:58:13 -080091 getActivity().setTitle(R.string.user_credentials);
Doris Ling03a3b512017-10-18 14:25:01 -070092 }
93
Robin Lee11fd5502016-05-16 15:42:34 +010094 protected void announceRemoval(String alias) {
Robin Leeccaf9c92017-03-24 14:50:05 +000095 if (!isAdded()) {
96 return;
Robin Lee11fd5502016-05-16 15:42:34 +010097 }
Robin Leeccaf9c92017-03-24 14:50:05 +000098 getListView().announceForAccessibility(getString(R.string.user_credential_removed, alias));
Robin Lee11fd5502016-05-16 15:42:34 +010099 }
100
Robin Lee04046a12016-01-19 11:42:57 +0000101 protected void refreshItems() {
102 if (isAdded()) {
103 new AliasLoader().execute();
104 }
105 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100106
Fan Zhang1e516282016-09-16 12:45:07 -0700107 public static class CredentialDialogFragment extends InstrumentedDialogFragment {
Robin Lee04046a12016-01-19 11:42:57 +0000108 private static final String TAG = "CredentialDialogFragment";
109 private static final String ARG_CREDENTIAL = "credential";
110
111 public static void show(Fragment target, Credential item) {
112 final Bundle args = new Bundle();
113 args.putParcelable(ARG_CREDENTIAL, item);
114
Robin Leef8e2dbf2016-04-07 13:17:24 +0100115 if (target.getFragmentManager().findFragmentByTag(TAG) == null) {
116 final DialogFragment frag = new CredentialDialogFragment();
117 frag.setTargetFragment(target, /* requestCode */ -1);
118 frag.setArguments(args);
119 frag.show(target.getFragmentManager(), TAG);
120 }
Robin Lee04046a12016-01-19 11:42:57 +0000121 }
122
123 @Override
124 public Dialog onCreateDialog(Bundle savedInstanceState) {
125 final Credential item = (Credential) getArguments().getParcelable(ARG_CREDENTIAL);
Robin Leee68d9572016-07-19 16:10:39 +0100126
Robin Lee04046a12016-01-19 11:42:57 +0000127 View root = getActivity().getLayoutInflater()
128 .inflate(R.layout.user_credential_dialog, null);
129 ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container);
Robin Leee68d9572016-07-19 16:10:39 +0100130 View contentView = getCredentialView(item, R.layout.user_credential, null,
131 infoContainer, /* expanded */ true);
132 infoContainer.addView(contentView);
Robin Leec421db72016-03-11 16:22:23 +0000133
134 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
Robin Lee04046a12016-01-19 11:42:57 +0000135 .setView(root)
136 .setTitle(R.string.user_credential_title)
Robin Leec421db72016-03-11 16:22:23 +0000137 .setPositiveButton(R.string.done, null);
138
139 final String restriction = UserManager.DISALLOW_CONFIG_CREDENTIALS;
140 final int myUserId = UserHandle.myUserId();
Philip P. Moltmanne3f72112018-08-28 15:01:43 -0700141 if (!RestrictedLockUtilsInternal.hasBaseUserRestriction(getContext(), restriction,
142 myUserId)) {
Robin Leec421db72016-03-11 16:22:23 +0000143 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
144 @Override public void onClick(DialogInterface dialog, int id) {
Philip P. Moltmanne3f72112018-08-28 15:01:43 -0700145 final EnforcedAdmin admin = RestrictedLockUtilsInternal
146 .checkIfRestrictionEnforced(getContext(), restriction, myUserId);
Robin Leec421db72016-03-11 16:22:23 +0000147 if (admin != null) {
148 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
149 admin);
150 } else {
Robin Leee68d9572016-07-19 16:10:39 +0100151 new RemoveCredentialsTask(getContext(), getTargetFragment())
152 .execute(item);
Robin Leec421db72016-03-11 16:22:23 +0000153 }
154 dialog.dismiss();
155 }
156 };
Hai Shalom23666052019-03-07 15:54:25 -0800157 // TODO: b/127865361
158 // a safe means of clearing wifi certificates. Configs refer to aliases
159 // directly so deleting certs will break dependent access points.
160 // However, Wi-Fi used to remove this certificate from storage if the network
161 // was removed, regardless if it is used in more than one network.
162 // It has been decided to allow removing certificates from this menu, as we
163 // assume that the user who manually adds certificates must have a way to
164 // manually remove them.
165 builder.setNegativeButton(R.string.trusted_credentials_remove_label, listener);
Robin Leec421db72016-03-11 16:22:23 +0000166 }
167 return builder.create();
Robin Lee04046a12016-01-19 11:42:57 +0000168 }
169
Fan Zhang1e516282016-09-16 12:45:07 -0700170 @Override
171 public int getMetricsCategory() {
Fan Zhang31b21002019-01-16 13:49:47 -0800172 return SettingsEnums.DIALOG_USER_CREDENTIAL;
Fan Zhang1e516282016-09-16 12:45:07 -0700173 }
174
Robin Leee68d9572016-07-19 16:10:39 +0100175 /**
176 * Deletes all certificates and keys under a given alias.
177 *
178 * If the {@link Credential} is for a system alias, all active grants to the alias will be
Hai Shalom23666052019-03-07 15:54:25 -0800179 * removed using {@link KeyChain}. If the {@link Credential} is for Wi-Fi alias, all
180 * credentials and keys will be removed using {@link KeyStore}.
Robin Leee68d9572016-07-19 16:10:39 +0100181 */
182 private class RemoveCredentialsTask extends AsyncTask<Credential, Void, Credential[]> {
183 private Context context;
Robin Leeda7bc512016-02-24 17:39:32 +0000184 private Fragment targetFragment;
185
Robin Leee68d9572016-07-19 16:10:39 +0100186 public RemoveCredentialsTask(Context context, Fragment targetFragment) {
187 this.context = context;
Robin Leeda7bc512016-02-24 17:39:32 +0000188 this.targetFragment = targetFragment;
Robin Lee04046a12016-01-19 11:42:57 +0000189 }
Robin Leeda7bc512016-02-24 17:39:32 +0000190
191 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100192 protected Credential[] doInBackground(Credential... credentials) {
193 for (final Credential credential : credentials) {
194 if (credential.isSystem()) {
195 removeGrantsAndDelete(credential);
Hai Shalom23666052019-03-07 15:54:25 -0800196 } else {
197 deleteWifiCredential(credential);
Robin Leeda7bc512016-02-24 17:39:32 +0000198 }
Robin Leeda7bc512016-02-24 17:39:32 +0000199 }
Robin Leee68d9572016-07-19 16:10:39 +0100200 return credentials;
201 }
202
Hai Shalom23666052019-03-07 15:54:25 -0800203 private void deleteWifiCredential(final Credential credential) {
204 final KeyStore keyStore = KeyStore.getInstance();
205 final EnumSet<Credential.Type> storedTypes = credential.getStoredTypes();
206
207 // Remove all Wi-Fi credentials
208 if (storedTypes.contains(Credential.Type.USER_KEY)) {
209 keyStore.delete(Credentials.USER_PRIVATE_KEY + credential.getAlias(),
210 Process.WIFI_UID);
211 }
212 if (storedTypes.contains(Credential.Type.USER_CERTIFICATE)) {
213 keyStore.delete(Credentials.USER_CERTIFICATE + credential.getAlias(),
214 Process.WIFI_UID);
215 }
216 if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) {
217 keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(),
218 Process.WIFI_UID);
219 }
220 }
221
Robin Leee68d9572016-07-19 16:10:39 +0100222 private void removeGrantsAndDelete(final Credential credential) {
223 final KeyChainConnection conn;
224 try {
225 conn = KeyChain.bind(getContext());
226 } catch (InterruptedException e) {
227 Log.w(TAG, "Connecting to KeyChain", e);
228 return;
229 }
230
231 try {
232 IKeyChainService keyChain = conn.getService();
233 keyChain.removeKeyPair(credential.alias);
234 } catch (RemoteException e) {
235 Log.w(TAG, "Removing credentials", e);
236 } finally {
237 conn.close();
238 }
Robin Leeda7bc512016-02-24 17:39:32 +0000239 }
240
241 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100242 protected void onPostExecute(Credential... credentials) {
Robin Lee11fd5502016-05-16 15:42:34 +0100243 if (targetFragment instanceof UserCredentialsSettings && targetFragment.isAdded()) {
244 final UserCredentialsSettings target = (UserCredentialsSettings) targetFragment;
Robin Leee68d9572016-07-19 16:10:39 +0100245 for (final Credential credential : credentials) {
246 target.announceRemoval(credential.alias);
Robin Lee11fd5502016-05-16 15:42:34 +0100247 }
248 target.refreshItems();
Robin Leeda7bc512016-02-24 17:39:32 +0000249 }
250 }
Robin Lee04046a12016-01-19 11:42:57 +0000251 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100252 }
253
254 /**
255 * Opens a background connection to KeyStore to list user credentials.
256 * The credentials are stored in a {@link CredentialAdapter} attached to the main
257 * {@link ListView} in the fragment.
258 */
Robin Leee68d9572016-07-19 16:10:39 +0100259 private class AliasLoader extends AsyncTask<Void, Void, List<Credential>> {
260 /**
261 * @return a list of credentials ordered:
262 * <ol>
263 * <li>first by purpose;</li>
264 * <li>then by alias.</li>
265 * </ol>
266 */
Robin Leebaefdcf2015-08-26 10:57:44 +0100267 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100268 protected List<Credential> doInBackground(Void... params) {
269 final KeyStore keyStore = KeyStore.getInstance();
270
271 // Certificates can be installed into SYSTEM_UID or WIFI_UID through CertInstaller.
272 final int myUserId = UserHandle.myUserId();
273 final int systemUid = UserHandle.getUid(myUserId, Process.SYSTEM_UID);
274 final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
275
276 List<Credential> credentials = new ArrayList<>();
277 credentials.addAll(getCredentialsForUid(keyStore, systemUid).values());
278 credentials.addAll(getCredentialsForUid(keyStore, wifiUid).values());
279 return credentials;
280 }
281
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700282 private boolean isAsymmetric(KeyStore keyStore, String alias, int uid)
283 throws UnrecoverableKeyException {
284 KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
285 int errorCode = keyStore.getKeyCharacteristics(alias, null, null, uid,
286 keyCharacteristics);
287 if (errorCode != KeyStore.NO_ERROR) {
288 throw (UnrecoverableKeyException)
289 new UnrecoverableKeyException("Failed to obtain information about key")
290 .initCause(KeyStore.getKeyStoreException(errorCode));
291 }
292 Integer keymasterAlgorithm = keyCharacteristics.getEnum(
293 KeymasterDefs.KM_TAG_ALGORITHM);
294 if (keymasterAlgorithm == null) {
295 throw new UnrecoverableKeyException("Key algorithm unknown");
296 }
297 return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
298 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC;
299 }
300
Robin Leee68d9572016-07-19 16:10:39 +0100301 private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
302 final SortedMap<String, Credential> aliasMap = new TreeMap<>();
Robin Leebaefdcf2015-08-26 10:57:44 +0100303 for (final Credential.Type type : Credential.Type.values()) {
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700304 for (final String prefix : type.prefix) {
305 for (final String alias : keyStore.list(prefix, uid)) {
306 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
307 // Do not show work profile keys in user credentials
308 if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) ||
309 alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) {
310 continue;
311 }
312 // Do not show synthetic password keys in user credential
313 if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
314 continue;
315 }
316 }
317 try {
318 if (type == Credential.Type.USER_KEY &&
319 !isAsymmetric(keyStore, prefix + alias, uid)) {
320 continue;
321 }
322 } catch (UnrecoverableKeyException e) {
323 Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e);
Rubin Xu52221d82017-02-09 11:09:11 +0000324 continue;
325 }
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700326 Credential c = aliasMap.get(alias);
327 if (c == null) {
328 c = new Credential(alias, uid);
329 aliasMap.put(alias, c);
Rubin Xu52221d82017-02-09 11:09:11 +0000330 }
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700331 c.storedTypes.add(type);
Ricky Wai95792742016-05-24 19:28:53 +0100332 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100333 }
334 }
Robin Leee68d9572016-07-19 16:10:39 +0100335 return aliasMap;
Robin Leebaefdcf2015-08-26 10:57:44 +0100336 }
337
338 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100339 protected void onPostExecute(List<Credential> credentials) {
Robin Leeccaf9c92017-03-24 14:50:05 +0000340 if (!isAdded()) {
341 return;
342 }
343
344 if (credentials == null || credentials.size() == 0) {
345 // Create a "no credentials installed" message for the empty case.
346 TextView emptyTextView = (TextView) getActivity().findViewById(android.R.id.empty);
347 emptyTextView.setText(R.string.user_credential_none_installed);
348 setEmptyView(emptyTextView);
349 } else {
350 setEmptyView(null);
351 }
352
353 getListView().setAdapter(
354 new CredentialAdapter(credentials, UserCredentialsSettings.this));
Robin Leebaefdcf2015-08-26 10:57:44 +0100355 }
356 }
357
358 /**
359 * Helper class to display {@link Credential}s in a list.
360 */
Robin Leeccaf9c92017-03-24 14:50:05 +0000361 private static class CredentialAdapter extends RecyclerView.Adapter<ViewHolder> {
Robin Leee68d9572016-07-19 16:10:39 +0100362 private static final int LAYOUT_RESOURCE = R.layout.user_credential_preference;
363
Robin Leeccaf9c92017-03-24 14:50:05 +0000364 private final List<Credential> mItems;
365 private final View.OnClickListener mListener;
366
367 public CredentialAdapter(List<Credential> items, @Nullable View.OnClickListener listener) {
368 mItems = items;
369 mListener = listener;
Robin Leebaefdcf2015-08-26 10:57:44 +0100370 }
371
372 @Override
Robin Leeccaf9c92017-03-24 14:50:05 +0000373 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
374 final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
375 return new ViewHolder(inflater.inflate(LAYOUT_RESOURCE, parent, false));
376 }
377
378 @Override
379 public void onBindViewHolder(ViewHolder h, int position) {
380 getCredentialView(mItems.get(position), LAYOUT_RESOURCE, h.itemView, null, false);
381 h.itemView.setTag(mItems.get(position));
382 h.itemView.setOnClickListener(mListener);
383 }
384
385 @Override
386 public int getItemCount() {
387 return mItems.size();
388 }
389 }
390
391 private static class ViewHolder extends RecyclerView.ViewHolder {
392 public ViewHolder(View item) {
393 super(item);
Robin Leebaefdcf2015-08-26 10:57:44 +0100394 }
395 }
396
Robin Leee68d9572016-07-19 16:10:39 +0100397 /**
398 * Mapping from View IDs in {@link R} to the types of credentials they describe.
399 */
400 private static final SparseArray<Credential.Type> credentialViewTypes = new SparseArray<>();
401 static {
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700402 credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_KEY);
Robin Leee68d9572016-07-19 16:10:39 +0100403 credentialViewTypes.put(R.id.contents_usercrt, Credential.Type.USER_CERTIFICATE);
404 credentialViewTypes.put(R.id.contents_cacrt, Credential.Type.CA_CERTIFICATE);
405 }
406
407 protected static View getCredentialView(Credential item, @LayoutRes int layoutResource,
408 @Nullable View view, ViewGroup parent, boolean expanded) {
409 if (view == null) {
410 view = LayoutInflater.from(parent.getContext()).inflate(layoutResource, parent, false);
411 }
412
413 ((TextView) view.findViewById(R.id.alias)).setText(item.alias);
414 ((TextView) view.findViewById(R.id.purpose)).setText(item.isSystem()
415 ? R.string.credential_for_vpn_and_apps
416 : R.string.credential_for_wifi);
417
418 view.findViewById(R.id.contents).setVisibility(expanded ? View.VISIBLE : View.GONE);
419 if (expanded) {
420 for (int i = 0; i < credentialViewTypes.size(); i++) {
421 final View detail = view.findViewById(credentialViewTypes.keyAt(i));
422 detail.setVisibility(item.storedTypes.contains(credentialViewTypes.valueAt(i))
423 ? View.VISIBLE : View.GONE);
424 }
425 }
426 return view;
427 }
428
429 static class AliasEntry {
430 public String alias;
431 public int uid;
432 }
433
Robin Leee2680422016-01-25 12:24:27 +0000434 static class Credential implements Parcelable {
435 static enum Type {
Robin Leebaefdcf2015-08-26 10:57:44 +0100436 CA_CERTIFICATE (Credentials.CA_CERTIFICATE),
437 USER_CERTIFICATE (Credentials.USER_CERTIFICATE),
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700438 USER_KEY(Credentials.USER_PRIVATE_KEY, Credentials.USER_SECRET_KEY);
Robin Leebaefdcf2015-08-26 10:57:44 +0100439
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700440 final String[] prefix;
Robin Leebaefdcf2015-08-26 10:57:44 +0100441
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700442 Type(String... prefix) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100443 this.prefix = prefix;
444 }
445 }
446
447 /**
448 * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the
449 * prefixes from {@link CredentialItem.storedTypes}.
450 */
451 final String alias;
452
453 /**
Robin Leee68d9572016-07-19 16:10:39 +0100454 * UID under which this credential is stored. Typically {@link Process#SYSTEM_UID} but can
455 * also be {@link Process#WIFI_UID} for credentials installed as wifi certificates.
456 */
457 final int uid;
458
459 /**
Robin Leebaefdcf2015-08-26 10:57:44 +0100460 * Should contain some non-empty subset of:
461 * <ul>
462 * <li>{@link Credentials.CA_CERTIFICATE}</li>
463 * <li>{@link Credentials.USER_CERTIFICATE}</li>
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700464 * <li>{@link Credentials.USER_KEY}</li>
Robin Leebaefdcf2015-08-26 10:57:44 +0100465 * </ul>
466 */
Robin Lee04046a12016-01-19 11:42:57 +0000467 final EnumSet<Type> storedTypes = EnumSet.noneOf(Type.class);
Robin Leebaefdcf2015-08-26 10:57:44 +0100468
Robin Leee68d9572016-07-19 16:10:39 +0100469 Credential(final String alias, final int uid) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100470 this.alias = alias;
Robin Leee68d9572016-07-19 16:10:39 +0100471 this.uid = uid;
Robin Leebaefdcf2015-08-26 10:57:44 +0100472 }
Robin Lee04046a12016-01-19 11:42:57 +0000473
474 Credential(Parcel in) {
Robin Leee68d9572016-07-19 16:10:39 +0100475 this(in.readString(), in.readInt());
Robin Lee04046a12016-01-19 11:42:57 +0000476
477 long typeBits = in.readLong();
478 for (Type i : Type.values()) {
479 if ((typeBits & (1L << i.ordinal())) != 0L) {
480 storedTypes.add(i);
481 }
482 }
483 }
484
485 public void writeToParcel(Parcel out, int flags) {
486 out.writeString(alias);
Robin Leee68d9572016-07-19 16:10:39 +0100487 out.writeInt(uid);
Robin Lee04046a12016-01-19 11:42:57 +0000488
489 long typeBits = 0;
490 for (Type i : storedTypes) {
491 typeBits |= 1L << i.ordinal();
492 }
493 out.writeLong(typeBits);
494 }
495
496 public int describeContents() {
497 return 0;
498 }
499
500 public static final Parcelable.Creator<Credential> CREATOR
501 = new Parcelable.Creator<Credential>() {
502 public Credential createFromParcel(Parcel in) {
503 return new Credential(in);
504 }
505
506 public Credential[] newArray(int size) {
507 return new Credential[size];
508 }
509 };
Robin Leee68d9572016-07-19 16:10:39 +0100510
511 public boolean isSystem() {
512 return UserHandle.getAppId(uid) == Process.SYSTEM_UID;
513 }
Hai Shalom23666052019-03-07 15:54:25 -0800514
515 public String getAlias() { return alias; }
516
517 public EnumSet<Type> getStoredTypes() {
518 return storedTypes;
519 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100520 }
521}