blob: 571a32c41ea078dbc5276e2fa0d57aa61935ff2f [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 Leebaefdcf2015-08-26 10:57:44 +010021import android.app.AlertDialog;
Robin Lee04046a12016-01-19 11:42:57 +000022import android.app.Dialog;
23import android.app.DialogFragment;
24import android.app.Fragment;
Robin Leebaefdcf2015-08-26 10:57:44 +010025import android.content.Context;
26import android.content.DialogInterface;
27import android.os.AsyncTask;
28import android.os.Bundle;
Robin Lee04046a12016-01-19 11:42:57 +000029import android.os.Parcel;
30import android.os.Parcelable;
Robin Leee68d9572016-07-19 16:10:39 +010031import android.os.Process;
Robin Leeda7bc512016-02-24 17:39:32 +000032import android.os.RemoteException;
Robin Leec421db72016-03-11 16:22:23 +000033import android.os.UserHandle;
34import android.os.UserManager;
Robin Leebaefdcf2015-08-26 10:57:44 +010035import android.security.Credentials;
Robin Leeda7bc512016-02-24 17:39:32 +000036import android.security.IKeyChainService;
37import android.security.KeyChain;
38import android.security.KeyChain.KeyChainConnection;
Robin Leebaefdcf2015-08-26 10:57:44 +010039import android.security.KeyStore;
Janis Danisevskisb705e1a2017-04-19 16:23:02 -070040import android.security.keymaster.KeyCharacteristics;
41import android.security.keymaster.KeymasterDefs;
Aurimas Liutikase0069d32018-04-17 11:22:43 -070042import androidx.recyclerview.widget.RecyclerView;
Robin Leeda7bc512016-02-24 17:39:32 +000043import android.util.Log;
Robin Leee68d9572016-07-19 16:10:39 +010044import android.util.SparseArray;
Robin Leebaefdcf2015-08-26 10:57:44 +010045import android.view.LayoutInflater;
46import android.view.View;
47import android.view.ViewGroup;
Robin Leebaefdcf2015-08-26 10:57:44 +010048import android.widget.TextView;
49
Tamas Berghammer265d3c22016-06-22 15:34:45 +010050import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Ricky Wai95792742016-05-24 19:28:53 +010051import com.android.internal.widget.LockPatternUtils;
Fan Zhang1e516282016-09-16 12:45:07 -070052import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
Robin Leec421db72016-03-11 16:22:23 +000053import com.android.settingslib.RestrictedLockUtils;
54import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
Janis Danisevskisb705e1a2017-04-19 16:23:02 -070055import java.security.UnrecoverableKeyException;
Robin Leee68d9572016-07-19 16:10:39 +010056import java.util.ArrayList;
Robin Leebaefdcf2015-08-26 10:57:44 +010057import java.util.EnumSet;
Robin Leee68d9572016-07-19 16:10:39 +010058import java.util.List;
Robin Leebaefdcf2015-08-26 10:57:44 +010059import java.util.SortedMap;
60import java.util.TreeMap;
61
Robin Leeccaf9c92017-03-24 14:50:05 +000062public class UserCredentialsSettings extends SettingsPreferenceFragment
63 implements View.OnClickListener {
Robin Leebaefdcf2015-08-26 10:57:44 +010064 private static final String TAG = "UserCredentialsSettings";
65
Robin Leebaefdcf2015-08-26 10:57:44 +010066 @Override
Fan Zhang65076132016-08-08 10:25:13 -070067 public int getMetricsCategory() {
Robin Leeb70b3d82016-02-01 12:52:16 +000068 return MetricsEvent.USER_CREDENTIALS;
Robin Leebaefdcf2015-08-26 10:57:44 +010069 }
70
71 @Override
72 public void onResume() {
73 super.onResume();
Robin Lee04046a12016-01-19 11:42:57 +000074 refreshItems();
Robin Leebaefdcf2015-08-26 10:57:44 +010075 }
76
77 @Override
Robin Leeccaf9c92017-03-24 14:50:05 +000078 public void onClick(final View view) {
79 final Credential item = (Credential) view.getTag();
80 if (item != null) {
81 CredentialDialogFragment.show(this, item);
82 }
Robin Lee04046a12016-01-19 11:42:57 +000083 }
Robin Leebaefdcf2015-08-26 10:57:44 +010084
Doris Ling03a3b512017-10-18 14:25:01 -070085 @Override
Doris Linged4685f2017-10-25 14:08:57 -070086 public void onCreate(@Nullable Bundle savedInstanceState) {
87 super.onCreate(savedInstanceState);
Doris Ling4a012832017-11-13 17:58:13 -080088 getActivity().setTitle(R.string.user_credentials);
Doris Ling03a3b512017-10-18 14:25:01 -070089 }
90
Robin Lee11fd5502016-05-16 15:42:34 +010091 protected void announceRemoval(String alias) {
Robin Leeccaf9c92017-03-24 14:50:05 +000092 if (!isAdded()) {
93 return;
Robin Lee11fd5502016-05-16 15:42:34 +010094 }
Robin Leeccaf9c92017-03-24 14:50:05 +000095 getListView().announceForAccessibility(getString(R.string.user_credential_removed, alias));
Robin Lee11fd5502016-05-16 15:42:34 +010096 }
97
Robin Lee04046a12016-01-19 11:42:57 +000098 protected void refreshItems() {
99 if (isAdded()) {
100 new AliasLoader().execute();
101 }
102 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100103
Fan Zhang1e516282016-09-16 12:45:07 -0700104 public static class CredentialDialogFragment extends InstrumentedDialogFragment {
Robin Lee04046a12016-01-19 11:42:57 +0000105 private static final String TAG = "CredentialDialogFragment";
106 private static final String ARG_CREDENTIAL = "credential";
107
108 public static void show(Fragment target, Credential item) {
109 final Bundle args = new Bundle();
110 args.putParcelable(ARG_CREDENTIAL, item);
111
Robin Leef8e2dbf2016-04-07 13:17:24 +0100112 if (target.getFragmentManager().findFragmentByTag(TAG) == null) {
113 final DialogFragment frag = new CredentialDialogFragment();
114 frag.setTargetFragment(target, /* requestCode */ -1);
115 frag.setArguments(args);
116 frag.show(target.getFragmentManager(), TAG);
117 }
Robin Lee04046a12016-01-19 11:42:57 +0000118 }
119
120 @Override
121 public Dialog onCreateDialog(Bundle savedInstanceState) {
122 final Credential item = (Credential) getArguments().getParcelable(ARG_CREDENTIAL);
Robin Leee68d9572016-07-19 16:10:39 +0100123
Robin Lee04046a12016-01-19 11:42:57 +0000124 View root = getActivity().getLayoutInflater()
125 .inflate(R.layout.user_credential_dialog, null);
126 ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container);
Robin Leee68d9572016-07-19 16:10:39 +0100127 View contentView = getCredentialView(item, R.layout.user_credential, null,
128 infoContainer, /* expanded */ true);
129 infoContainer.addView(contentView);
Robin Leec421db72016-03-11 16:22:23 +0000130
131 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
Robin Lee04046a12016-01-19 11:42:57 +0000132 .setView(root)
133 .setTitle(R.string.user_credential_title)
Robin Leec421db72016-03-11 16:22:23 +0000134 .setPositiveButton(R.string.done, null);
135
136 final String restriction = UserManager.DISALLOW_CONFIG_CREDENTIALS;
137 final int myUserId = UserHandle.myUserId();
138 if (!RestrictedLockUtils.hasBaseUserRestriction(getContext(), restriction, myUserId)) {
139 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
140 @Override public void onClick(DialogInterface dialog, int id) {
141 final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
142 getContext(), restriction, myUserId);
143 if (admin != null) {
144 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
145 admin);
146 } else {
Robin Leee68d9572016-07-19 16:10:39 +0100147 new RemoveCredentialsTask(getContext(), getTargetFragment())
148 .execute(item);
Robin Leec421db72016-03-11 16:22:23 +0000149 }
150 dialog.dismiss();
151 }
152 };
Robin Leee68d9572016-07-19 16:10:39 +0100153 if (item.isSystem()) {
154 // TODO: a safe means of clearing wifi certificates. Configs refer to aliases
155 // directly so deleting certs will break dependent access points.
156 builder.setNegativeButton(R.string.trusted_credentials_remove_label, listener);
157 }
Robin Leec421db72016-03-11 16:22:23 +0000158 }
159 return builder.create();
Robin Lee04046a12016-01-19 11:42:57 +0000160 }
161
Fan Zhang1e516282016-09-16 12:45:07 -0700162 @Override
163 public int getMetricsCategory() {
164 return MetricsEvent.DIALOG_USER_CREDENTIAL;
165 }
166
Robin Leee68d9572016-07-19 16:10:39 +0100167 /**
168 * Deletes all certificates and keys under a given alias.
169 *
170 * If the {@link Credential} is for a system alias, all active grants to the alias will be
171 * removed using {@link KeyChain}.
172 */
173 private class RemoveCredentialsTask extends AsyncTask<Credential, Void, Credential[]> {
174 private Context context;
Robin Leeda7bc512016-02-24 17:39:32 +0000175 private Fragment targetFragment;
176
Robin Leee68d9572016-07-19 16:10:39 +0100177 public RemoveCredentialsTask(Context context, Fragment targetFragment) {
178 this.context = context;
Robin Leeda7bc512016-02-24 17:39:32 +0000179 this.targetFragment = targetFragment;
Robin Lee04046a12016-01-19 11:42:57 +0000180 }
Robin Leeda7bc512016-02-24 17:39:32 +0000181
182 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100183 protected Credential[] doInBackground(Credential... credentials) {
184 for (final Credential credential : credentials) {
185 if (credential.isSystem()) {
186 removeGrantsAndDelete(credential);
187 continue;
Robin Leeda7bc512016-02-24 17:39:32 +0000188 }
Robin Leee68d9572016-07-19 16:10:39 +0100189 throw new UnsupportedOperationException(
190 "Not implemented for wifi certificates. This should not be reachable.");
Robin Leeda7bc512016-02-24 17:39:32 +0000191 }
Robin Leee68d9572016-07-19 16:10:39 +0100192 return credentials;
193 }
194
195 private void removeGrantsAndDelete(final Credential credential) {
196 final KeyChainConnection conn;
197 try {
198 conn = KeyChain.bind(getContext());
199 } catch (InterruptedException e) {
200 Log.w(TAG, "Connecting to KeyChain", e);
201 return;
202 }
203
204 try {
205 IKeyChainService keyChain = conn.getService();
206 keyChain.removeKeyPair(credential.alias);
207 } catch (RemoteException e) {
208 Log.w(TAG, "Removing credentials", e);
209 } finally {
210 conn.close();
211 }
Robin Leeda7bc512016-02-24 17:39:32 +0000212 }
213
214 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100215 protected void onPostExecute(Credential... credentials) {
Robin Lee11fd5502016-05-16 15:42:34 +0100216 if (targetFragment instanceof UserCredentialsSettings && targetFragment.isAdded()) {
217 final UserCredentialsSettings target = (UserCredentialsSettings) targetFragment;
Robin Leee68d9572016-07-19 16:10:39 +0100218 for (final Credential credential : credentials) {
219 target.announceRemoval(credential.alias);
Robin Lee11fd5502016-05-16 15:42:34 +0100220 }
221 target.refreshItems();
Robin Leeda7bc512016-02-24 17:39:32 +0000222 }
223 }
Robin Lee04046a12016-01-19 11:42:57 +0000224 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100225 }
226
227 /**
228 * Opens a background connection to KeyStore to list user credentials.
229 * The credentials are stored in a {@link CredentialAdapter} attached to the main
230 * {@link ListView} in the fragment.
231 */
Robin Leee68d9572016-07-19 16:10:39 +0100232 private class AliasLoader extends AsyncTask<Void, Void, List<Credential>> {
233 /**
234 * @return a list of credentials ordered:
235 * <ol>
236 * <li>first by purpose;</li>
237 * <li>then by alias.</li>
238 * </ol>
239 */
Robin Leebaefdcf2015-08-26 10:57:44 +0100240 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100241 protected List<Credential> doInBackground(Void... params) {
242 final KeyStore keyStore = KeyStore.getInstance();
243
244 // Certificates can be installed into SYSTEM_UID or WIFI_UID through CertInstaller.
245 final int myUserId = UserHandle.myUserId();
246 final int systemUid = UserHandle.getUid(myUserId, Process.SYSTEM_UID);
247 final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
248
249 List<Credential> credentials = new ArrayList<>();
250 credentials.addAll(getCredentialsForUid(keyStore, systemUid).values());
251 credentials.addAll(getCredentialsForUid(keyStore, wifiUid).values());
252 return credentials;
253 }
254
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700255 private boolean isAsymmetric(KeyStore keyStore, String alias, int uid)
256 throws UnrecoverableKeyException {
257 KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
258 int errorCode = keyStore.getKeyCharacteristics(alias, null, null, uid,
259 keyCharacteristics);
260 if (errorCode != KeyStore.NO_ERROR) {
261 throw (UnrecoverableKeyException)
262 new UnrecoverableKeyException("Failed to obtain information about key")
263 .initCause(KeyStore.getKeyStoreException(errorCode));
264 }
265 Integer keymasterAlgorithm = keyCharacteristics.getEnum(
266 KeymasterDefs.KM_TAG_ALGORITHM);
267 if (keymasterAlgorithm == null) {
268 throw new UnrecoverableKeyException("Key algorithm unknown");
269 }
270 return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
271 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC;
272 }
273
Robin Leee68d9572016-07-19 16:10:39 +0100274 private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
275 final SortedMap<String, Credential> aliasMap = new TreeMap<>();
Robin Leebaefdcf2015-08-26 10:57:44 +0100276 for (final Credential.Type type : Credential.Type.values()) {
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700277 for (final String prefix : type.prefix) {
278 for (final String alias : keyStore.list(prefix, uid)) {
279 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
280 // Do not show work profile keys in user credentials
281 if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) ||
282 alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) {
283 continue;
284 }
285 // Do not show synthetic password keys in user credential
286 if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
287 continue;
288 }
289 }
290 try {
291 if (type == Credential.Type.USER_KEY &&
292 !isAsymmetric(keyStore, prefix + alias, uid)) {
293 continue;
294 }
295 } catch (UnrecoverableKeyException e) {
296 Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e);
Rubin Xu52221d82017-02-09 11:09:11 +0000297 continue;
298 }
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700299 Credential c = aliasMap.get(alias);
300 if (c == null) {
301 c = new Credential(alias, uid);
302 aliasMap.put(alias, c);
Rubin Xu52221d82017-02-09 11:09:11 +0000303 }
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700304 c.storedTypes.add(type);
Ricky Wai95792742016-05-24 19:28:53 +0100305 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100306 }
307 }
Robin Leee68d9572016-07-19 16:10:39 +0100308 return aliasMap;
Robin Leebaefdcf2015-08-26 10:57:44 +0100309 }
310
311 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100312 protected void onPostExecute(List<Credential> credentials) {
Robin Leeccaf9c92017-03-24 14:50:05 +0000313 if (!isAdded()) {
314 return;
315 }
316
317 if (credentials == null || credentials.size() == 0) {
318 // Create a "no credentials installed" message for the empty case.
319 TextView emptyTextView = (TextView) getActivity().findViewById(android.R.id.empty);
320 emptyTextView.setText(R.string.user_credential_none_installed);
321 setEmptyView(emptyTextView);
322 } else {
323 setEmptyView(null);
324 }
325
326 getListView().setAdapter(
327 new CredentialAdapter(credentials, UserCredentialsSettings.this));
Robin Leebaefdcf2015-08-26 10:57:44 +0100328 }
329 }
330
331 /**
332 * Helper class to display {@link Credential}s in a list.
333 */
Robin Leeccaf9c92017-03-24 14:50:05 +0000334 private static class CredentialAdapter extends RecyclerView.Adapter<ViewHolder> {
Robin Leee68d9572016-07-19 16:10:39 +0100335 private static final int LAYOUT_RESOURCE = R.layout.user_credential_preference;
336
Robin Leeccaf9c92017-03-24 14:50:05 +0000337 private final List<Credential> mItems;
338 private final View.OnClickListener mListener;
339
340 public CredentialAdapter(List<Credential> items, @Nullable View.OnClickListener listener) {
341 mItems = items;
342 mListener = listener;
Robin Leebaefdcf2015-08-26 10:57:44 +0100343 }
344
345 @Override
Robin Leeccaf9c92017-03-24 14:50:05 +0000346 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
347 final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
348 return new ViewHolder(inflater.inflate(LAYOUT_RESOURCE, parent, false));
349 }
350
351 @Override
352 public void onBindViewHolder(ViewHolder h, int position) {
353 getCredentialView(mItems.get(position), LAYOUT_RESOURCE, h.itemView, null, false);
354 h.itemView.setTag(mItems.get(position));
355 h.itemView.setOnClickListener(mListener);
356 }
357
358 @Override
359 public int getItemCount() {
360 return mItems.size();
361 }
362 }
363
364 private static class ViewHolder extends RecyclerView.ViewHolder {
365 public ViewHolder(View item) {
366 super(item);
Robin Leebaefdcf2015-08-26 10:57:44 +0100367 }
368 }
369
Robin Leee68d9572016-07-19 16:10:39 +0100370 /**
371 * Mapping from View IDs in {@link R} to the types of credentials they describe.
372 */
373 private static final SparseArray<Credential.Type> credentialViewTypes = new SparseArray<>();
374 static {
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700375 credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_KEY);
Robin Leee68d9572016-07-19 16:10:39 +0100376 credentialViewTypes.put(R.id.contents_usercrt, Credential.Type.USER_CERTIFICATE);
377 credentialViewTypes.put(R.id.contents_cacrt, Credential.Type.CA_CERTIFICATE);
378 }
379
380 protected static View getCredentialView(Credential item, @LayoutRes int layoutResource,
381 @Nullable View view, ViewGroup parent, boolean expanded) {
382 if (view == null) {
383 view = LayoutInflater.from(parent.getContext()).inflate(layoutResource, parent, false);
384 }
385
386 ((TextView) view.findViewById(R.id.alias)).setText(item.alias);
387 ((TextView) view.findViewById(R.id.purpose)).setText(item.isSystem()
388 ? R.string.credential_for_vpn_and_apps
389 : R.string.credential_for_wifi);
390
391 view.findViewById(R.id.contents).setVisibility(expanded ? View.VISIBLE : View.GONE);
392 if (expanded) {
393 for (int i = 0; i < credentialViewTypes.size(); i++) {
394 final View detail = view.findViewById(credentialViewTypes.keyAt(i));
395 detail.setVisibility(item.storedTypes.contains(credentialViewTypes.valueAt(i))
396 ? View.VISIBLE : View.GONE);
397 }
398 }
399 return view;
400 }
401
402 static class AliasEntry {
403 public String alias;
404 public int uid;
405 }
406
Robin Leee2680422016-01-25 12:24:27 +0000407 static class Credential implements Parcelable {
408 static enum Type {
Robin Leebaefdcf2015-08-26 10:57:44 +0100409 CA_CERTIFICATE (Credentials.CA_CERTIFICATE),
410 USER_CERTIFICATE (Credentials.USER_CERTIFICATE),
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700411 USER_KEY(Credentials.USER_PRIVATE_KEY, Credentials.USER_SECRET_KEY);
Robin Leebaefdcf2015-08-26 10:57:44 +0100412
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700413 final String[] prefix;
Robin Leebaefdcf2015-08-26 10:57:44 +0100414
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700415 Type(String... prefix) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100416 this.prefix = prefix;
417 }
418 }
419
420 /**
421 * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the
422 * prefixes from {@link CredentialItem.storedTypes}.
423 */
424 final String alias;
425
426 /**
Robin Leee68d9572016-07-19 16:10:39 +0100427 * UID under which this credential is stored. Typically {@link Process#SYSTEM_UID} but can
428 * also be {@link Process#WIFI_UID} for credentials installed as wifi certificates.
429 */
430 final int uid;
431
432 /**
Robin Leebaefdcf2015-08-26 10:57:44 +0100433 * Should contain some non-empty subset of:
434 * <ul>
435 * <li>{@link Credentials.CA_CERTIFICATE}</li>
436 * <li>{@link Credentials.USER_CERTIFICATE}</li>
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700437 * <li>{@link Credentials.USER_KEY}</li>
Robin Leebaefdcf2015-08-26 10:57:44 +0100438 * </ul>
439 */
Robin Lee04046a12016-01-19 11:42:57 +0000440 final EnumSet<Type> storedTypes = EnumSet.noneOf(Type.class);
Robin Leebaefdcf2015-08-26 10:57:44 +0100441
Robin Leee68d9572016-07-19 16:10:39 +0100442 Credential(final String alias, final int uid) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100443 this.alias = alias;
Robin Leee68d9572016-07-19 16:10:39 +0100444 this.uid = uid;
Robin Leebaefdcf2015-08-26 10:57:44 +0100445 }
Robin Lee04046a12016-01-19 11:42:57 +0000446
447 Credential(Parcel in) {
Robin Leee68d9572016-07-19 16:10:39 +0100448 this(in.readString(), in.readInt());
Robin Lee04046a12016-01-19 11:42:57 +0000449
450 long typeBits = in.readLong();
451 for (Type i : Type.values()) {
452 if ((typeBits & (1L << i.ordinal())) != 0L) {
453 storedTypes.add(i);
454 }
455 }
456 }
457
458 public void writeToParcel(Parcel out, int flags) {
459 out.writeString(alias);
Robin Leee68d9572016-07-19 16:10:39 +0100460 out.writeInt(uid);
Robin Lee04046a12016-01-19 11:42:57 +0000461
462 long typeBits = 0;
463 for (Type i : storedTypes) {
464 typeBits |= 1L << i.ordinal();
465 }
466 out.writeLong(typeBits);
467 }
468
469 public int describeContents() {
470 return 0;
471 }
472
473 public static final Parcelable.Creator<Credential> CREATOR
474 = new Parcelable.Creator<Credential>() {
475 public Credential createFromParcel(Parcel in) {
476 return new Credential(in);
477 }
478
479 public Credential[] newArray(int size) {
480 return new Credential[size];
481 }
482 };
Robin Leee68d9572016-07-19 16:10:39 +0100483
484 public boolean isSystem() {
485 return UserHandle.getAppId(uid) == Process.SYSTEM_UID;
486 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100487 }
488}