blob: 06d0be67cf61eac0028f46931b4d8c7cf49a5f4b [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;
Robin Leebaefdcf2015-08-26 10:57:44 +010022import android.content.Context;
23import android.content.DialogInterface;
24import android.os.AsyncTask;
25import android.os.Bundle;
Robin Lee04046a12016-01-19 11:42:57 +000026import android.os.Parcel;
27import android.os.Parcelable;
Robin Leee68d9572016-07-19 16:10:39 +010028import android.os.Process;
Robin Leeda7bc512016-02-24 17:39:32 +000029import android.os.RemoteException;
Robin Leec421db72016-03-11 16:22:23 +000030import android.os.UserHandle;
31import android.os.UserManager;
Robin Leebaefdcf2015-08-26 10:57:44 +010032import android.security.Credentials;
Robin Leeda7bc512016-02-24 17:39:32 +000033import android.security.IKeyChainService;
34import android.security.KeyChain;
35import android.security.KeyChain.KeyChainConnection;
Robin Leebaefdcf2015-08-26 10:57:44 +010036import android.security.KeyStore;
Janis Danisevskisb705e1a2017-04-19 16:23:02 -070037import android.security.keymaster.KeyCharacteristics;
38import android.security.keymaster.KeymasterDefs;
Robin Leeda7bc512016-02-24 17:39:32 +000039import android.util.Log;
Robin Leee68d9572016-07-19 16:10:39 +010040import android.util.SparseArray;
Robin Leebaefdcf2015-08-26 10:57:44 +010041import android.view.LayoutInflater;
42import android.view.View;
43import android.view.ViewGroup;
Robin Leebaefdcf2015-08-26 10:57:44 +010044import android.widget.TextView;
45
Tamas Berghammer265d3c22016-06-22 15:34:45 +010046import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Ricky Wai95792742016-05-24 19:28:53 +010047import com.android.internal.widget.LockPatternUtils;
Fan Zhang1e516282016-09-16 12:45:07 -070048import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
Robin Leec421db72016-03-11 16:22:23 +000049import com.android.settingslib.RestrictedLockUtils;
50import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
Fan Zhangc7162cd2018-06-18 15:21:41 -070051
Janis Danisevskisb705e1a2017-04-19 16:23:02 -070052import java.security.UnrecoverableKeyException;
Robin Leee68d9572016-07-19 16:10:39 +010053import java.util.ArrayList;
Robin Leebaefdcf2015-08-26 10:57:44 +010054import java.util.EnumSet;
Robin Leee68d9572016-07-19 16:10:39 +010055import java.util.List;
Robin Leebaefdcf2015-08-26 10:57:44 +010056import java.util.SortedMap;
57import java.util.TreeMap;
58
tmfang41ab6b42018-07-17 13:53:04 +080059import androidx.appcompat.app.AlertDialog;
tmfang99cc23d2018-06-26 19:01:57 +080060import androidx.fragment.app.DialogFragment;
61import androidx.fragment.app.Fragment;
Fan Zhangc7162cd2018-06-18 15:21:41 -070062import androidx.recyclerview.widget.RecyclerView;
63
Robin Leeccaf9c92017-03-24 14:50:05 +000064public class UserCredentialsSettings extends SettingsPreferenceFragment
65 implements View.OnClickListener {
Robin Leebaefdcf2015-08-26 10:57:44 +010066 private static final String TAG = "UserCredentialsSettings";
67
Robin Leebaefdcf2015-08-26 10:57:44 +010068 @Override
Fan Zhang65076132016-08-08 10:25:13 -070069 public int getMetricsCategory() {
Robin Leeb70b3d82016-02-01 12:52:16 +000070 return MetricsEvent.USER_CREDENTIALS;
Robin Leebaefdcf2015-08-26 10:57:44 +010071 }
72
73 @Override
74 public void onResume() {
75 super.onResume();
Robin Lee04046a12016-01-19 11:42:57 +000076 refreshItems();
Robin Leebaefdcf2015-08-26 10:57:44 +010077 }
78
79 @Override
Robin Leeccaf9c92017-03-24 14:50:05 +000080 public void onClick(final View view) {
81 final Credential item = (Credential) view.getTag();
82 if (item != null) {
83 CredentialDialogFragment.show(this, item);
84 }
Robin Lee04046a12016-01-19 11:42:57 +000085 }
Robin Leebaefdcf2015-08-26 10:57:44 +010086
Doris Ling03a3b512017-10-18 14:25:01 -070087 @Override
Doris Linged4685f2017-10-25 14:08:57 -070088 public void onCreate(@Nullable Bundle savedInstanceState) {
89 super.onCreate(savedInstanceState);
Doris Ling4a012832017-11-13 17:58:13 -080090 getActivity().setTitle(R.string.user_credentials);
Doris Ling03a3b512017-10-18 14:25:01 -070091 }
92
Robin Lee11fd5502016-05-16 15:42:34 +010093 protected void announceRemoval(String alias) {
Robin Leeccaf9c92017-03-24 14:50:05 +000094 if (!isAdded()) {
95 return;
Robin Lee11fd5502016-05-16 15:42:34 +010096 }
Robin Leeccaf9c92017-03-24 14:50:05 +000097 getListView().announceForAccessibility(getString(R.string.user_credential_removed, alias));
Robin Lee11fd5502016-05-16 15:42:34 +010098 }
99
Robin Lee04046a12016-01-19 11:42:57 +0000100 protected void refreshItems() {
101 if (isAdded()) {
102 new AliasLoader().execute();
103 }
104 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100105
Fan Zhang1e516282016-09-16 12:45:07 -0700106 public static class CredentialDialogFragment extends InstrumentedDialogFragment {
Robin Lee04046a12016-01-19 11:42:57 +0000107 private static final String TAG = "CredentialDialogFragment";
108 private static final String ARG_CREDENTIAL = "credential";
109
110 public static void show(Fragment target, Credential item) {
111 final Bundle args = new Bundle();
112 args.putParcelable(ARG_CREDENTIAL, item);
113
Robin Leef8e2dbf2016-04-07 13:17:24 +0100114 if (target.getFragmentManager().findFragmentByTag(TAG) == null) {
115 final DialogFragment frag = new CredentialDialogFragment();
116 frag.setTargetFragment(target, /* requestCode */ -1);
117 frag.setArguments(args);
118 frag.show(target.getFragmentManager(), TAG);
119 }
Robin Lee04046a12016-01-19 11:42:57 +0000120 }
121
122 @Override
123 public Dialog onCreateDialog(Bundle savedInstanceState) {
124 final Credential item = (Credential) getArguments().getParcelable(ARG_CREDENTIAL);
Robin Leee68d9572016-07-19 16:10:39 +0100125
Robin Lee04046a12016-01-19 11:42:57 +0000126 View root = getActivity().getLayoutInflater()
127 .inflate(R.layout.user_credential_dialog, null);
128 ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container);
Robin Leee68d9572016-07-19 16:10:39 +0100129 View contentView = getCredentialView(item, R.layout.user_credential, null,
130 infoContainer, /* expanded */ true);
131 infoContainer.addView(contentView);
Robin Leec421db72016-03-11 16:22:23 +0000132
133 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
Robin Lee04046a12016-01-19 11:42:57 +0000134 .setView(root)
135 .setTitle(R.string.user_credential_title)
Robin Leec421db72016-03-11 16:22:23 +0000136 .setPositiveButton(R.string.done, null);
137
138 final String restriction = UserManager.DISALLOW_CONFIG_CREDENTIALS;
139 final int myUserId = UserHandle.myUserId();
140 if (!RestrictedLockUtils.hasBaseUserRestriction(getContext(), restriction, myUserId)) {
141 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
142 @Override public void onClick(DialogInterface dialog, int id) {
143 final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
144 getContext(), restriction, myUserId);
145 if (admin != null) {
146 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
147 admin);
148 } else {
Robin Leee68d9572016-07-19 16:10:39 +0100149 new RemoveCredentialsTask(getContext(), getTargetFragment())
150 .execute(item);
Robin Leec421db72016-03-11 16:22:23 +0000151 }
152 dialog.dismiss();
153 }
154 };
Robin Leee68d9572016-07-19 16:10:39 +0100155 if (item.isSystem()) {
156 // TODO: a safe means of clearing wifi certificates. Configs refer to aliases
157 // directly so deleting certs will break dependent access points.
158 builder.setNegativeButton(R.string.trusted_credentials_remove_label, listener);
159 }
Robin Leec421db72016-03-11 16:22:23 +0000160 }
161 return builder.create();
Robin Lee04046a12016-01-19 11:42:57 +0000162 }
163
Fan Zhang1e516282016-09-16 12:45:07 -0700164 @Override
165 public int getMetricsCategory() {
166 return MetricsEvent.DIALOG_USER_CREDENTIAL;
167 }
168
Robin Leee68d9572016-07-19 16:10:39 +0100169 /**
170 * Deletes all certificates and keys under a given alias.
171 *
172 * If the {@link Credential} is for a system alias, all active grants to the alias will be
173 * removed using {@link KeyChain}.
174 */
175 private class RemoveCredentialsTask extends AsyncTask<Credential, Void, Credential[]> {
176 private Context context;
Robin Leeda7bc512016-02-24 17:39:32 +0000177 private Fragment targetFragment;
178
Robin Leee68d9572016-07-19 16:10:39 +0100179 public RemoveCredentialsTask(Context context, Fragment targetFragment) {
180 this.context = context;
Robin Leeda7bc512016-02-24 17:39:32 +0000181 this.targetFragment = targetFragment;
Robin Lee04046a12016-01-19 11:42:57 +0000182 }
Robin Leeda7bc512016-02-24 17:39:32 +0000183
184 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100185 protected Credential[] doInBackground(Credential... credentials) {
186 for (final Credential credential : credentials) {
187 if (credential.isSystem()) {
188 removeGrantsAndDelete(credential);
189 continue;
Robin Leeda7bc512016-02-24 17:39:32 +0000190 }
Robin Leee68d9572016-07-19 16:10:39 +0100191 throw new UnsupportedOperationException(
192 "Not implemented for wifi certificates. This should not be reachable.");
Robin Leeda7bc512016-02-24 17:39:32 +0000193 }
Robin Leee68d9572016-07-19 16:10:39 +0100194 return credentials;
195 }
196
197 private void removeGrantsAndDelete(final Credential credential) {
198 final KeyChainConnection conn;
199 try {
200 conn = KeyChain.bind(getContext());
201 } catch (InterruptedException e) {
202 Log.w(TAG, "Connecting to KeyChain", e);
203 return;
204 }
205
206 try {
207 IKeyChainService keyChain = conn.getService();
208 keyChain.removeKeyPair(credential.alias);
209 } catch (RemoteException e) {
210 Log.w(TAG, "Removing credentials", e);
211 } finally {
212 conn.close();
213 }
Robin Leeda7bc512016-02-24 17:39:32 +0000214 }
215
216 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100217 protected void onPostExecute(Credential... credentials) {
Robin Lee11fd5502016-05-16 15:42:34 +0100218 if (targetFragment instanceof UserCredentialsSettings && targetFragment.isAdded()) {
219 final UserCredentialsSettings target = (UserCredentialsSettings) targetFragment;
Robin Leee68d9572016-07-19 16:10:39 +0100220 for (final Credential credential : credentials) {
221 target.announceRemoval(credential.alias);
Robin Lee11fd5502016-05-16 15:42:34 +0100222 }
223 target.refreshItems();
Robin Leeda7bc512016-02-24 17:39:32 +0000224 }
225 }
Robin Lee04046a12016-01-19 11:42:57 +0000226 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100227 }
228
229 /**
230 * Opens a background connection to KeyStore to list user credentials.
231 * The credentials are stored in a {@link CredentialAdapter} attached to the main
232 * {@link ListView} in the fragment.
233 */
Robin Leee68d9572016-07-19 16:10:39 +0100234 private class AliasLoader extends AsyncTask<Void, Void, List<Credential>> {
235 /**
236 * @return a list of credentials ordered:
237 * <ol>
238 * <li>first by purpose;</li>
239 * <li>then by alias.</li>
240 * </ol>
241 */
Robin Leebaefdcf2015-08-26 10:57:44 +0100242 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100243 protected List<Credential> doInBackground(Void... params) {
244 final KeyStore keyStore = KeyStore.getInstance();
245
246 // Certificates can be installed into SYSTEM_UID or WIFI_UID through CertInstaller.
247 final int myUserId = UserHandle.myUserId();
248 final int systemUid = UserHandle.getUid(myUserId, Process.SYSTEM_UID);
249 final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
250
251 List<Credential> credentials = new ArrayList<>();
252 credentials.addAll(getCredentialsForUid(keyStore, systemUid).values());
253 credentials.addAll(getCredentialsForUid(keyStore, wifiUid).values());
254 return credentials;
255 }
256
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700257 private boolean isAsymmetric(KeyStore keyStore, String alias, int uid)
258 throws UnrecoverableKeyException {
259 KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
260 int errorCode = keyStore.getKeyCharacteristics(alias, null, null, uid,
261 keyCharacteristics);
262 if (errorCode != KeyStore.NO_ERROR) {
263 throw (UnrecoverableKeyException)
264 new UnrecoverableKeyException("Failed to obtain information about key")
265 .initCause(KeyStore.getKeyStoreException(errorCode));
266 }
267 Integer keymasterAlgorithm = keyCharacteristics.getEnum(
268 KeymasterDefs.KM_TAG_ALGORITHM);
269 if (keymasterAlgorithm == null) {
270 throw new UnrecoverableKeyException("Key algorithm unknown");
271 }
272 return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
273 keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC;
274 }
275
Robin Leee68d9572016-07-19 16:10:39 +0100276 private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
277 final SortedMap<String, Credential> aliasMap = new TreeMap<>();
Robin Leebaefdcf2015-08-26 10:57:44 +0100278 for (final Credential.Type type : Credential.Type.values()) {
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700279 for (final String prefix : type.prefix) {
280 for (final String alias : keyStore.list(prefix, uid)) {
281 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
282 // Do not show work profile keys in user credentials
283 if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) ||
284 alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) {
285 continue;
286 }
287 // Do not show synthetic password keys in user credential
288 if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
289 continue;
290 }
291 }
292 try {
293 if (type == Credential.Type.USER_KEY &&
294 !isAsymmetric(keyStore, prefix + alias, uid)) {
295 continue;
296 }
297 } catch (UnrecoverableKeyException e) {
298 Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e);
Rubin Xu52221d82017-02-09 11:09:11 +0000299 continue;
300 }
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700301 Credential c = aliasMap.get(alias);
302 if (c == null) {
303 c = new Credential(alias, uid);
304 aliasMap.put(alias, c);
Rubin Xu52221d82017-02-09 11:09:11 +0000305 }
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700306 c.storedTypes.add(type);
Ricky Wai95792742016-05-24 19:28:53 +0100307 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100308 }
309 }
Robin Leee68d9572016-07-19 16:10:39 +0100310 return aliasMap;
Robin Leebaefdcf2015-08-26 10:57:44 +0100311 }
312
313 @Override
Robin Leee68d9572016-07-19 16:10:39 +0100314 protected void onPostExecute(List<Credential> credentials) {
Robin Leeccaf9c92017-03-24 14:50:05 +0000315 if (!isAdded()) {
316 return;
317 }
318
319 if (credentials == null || credentials.size() == 0) {
320 // Create a "no credentials installed" message for the empty case.
321 TextView emptyTextView = (TextView) getActivity().findViewById(android.R.id.empty);
322 emptyTextView.setText(R.string.user_credential_none_installed);
323 setEmptyView(emptyTextView);
324 } else {
325 setEmptyView(null);
326 }
327
328 getListView().setAdapter(
329 new CredentialAdapter(credentials, UserCredentialsSettings.this));
Robin Leebaefdcf2015-08-26 10:57:44 +0100330 }
331 }
332
333 /**
334 * Helper class to display {@link Credential}s in a list.
335 */
Robin Leeccaf9c92017-03-24 14:50:05 +0000336 private static class CredentialAdapter extends RecyclerView.Adapter<ViewHolder> {
Robin Leee68d9572016-07-19 16:10:39 +0100337 private static final int LAYOUT_RESOURCE = R.layout.user_credential_preference;
338
Robin Leeccaf9c92017-03-24 14:50:05 +0000339 private final List<Credential> mItems;
340 private final View.OnClickListener mListener;
341
342 public CredentialAdapter(List<Credential> items, @Nullable View.OnClickListener listener) {
343 mItems = items;
344 mListener = listener;
Robin Leebaefdcf2015-08-26 10:57:44 +0100345 }
346
347 @Override
Robin Leeccaf9c92017-03-24 14:50:05 +0000348 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
349 final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
350 return new ViewHolder(inflater.inflate(LAYOUT_RESOURCE, parent, false));
351 }
352
353 @Override
354 public void onBindViewHolder(ViewHolder h, int position) {
355 getCredentialView(mItems.get(position), LAYOUT_RESOURCE, h.itemView, null, false);
356 h.itemView.setTag(mItems.get(position));
357 h.itemView.setOnClickListener(mListener);
358 }
359
360 @Override
361 public int getItemCount() {
362 return mItems.size();
363 }
364 }
365
366 private static class ViewHolder extends RecyclerView.ViewHolder {
367 public ViewHolder(View item) {
368 super(item);
Robin Leebaefdcf2015-08-26 10:57:44 +0100369 }
370 }
371
Robin Leee68d9572016-07-19 16:10:39 +0100372 /**
373 * Mapping from View IDs in {@link R} to the types of credentials they describe.
374 */
375 private static final SparseArray<Credential.Type> credentialViewTypes = new SparseArray<>();
376 static {
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700377 credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_KEY);
Robin Leee68d9572016-07-19 16:10:39 +0100378 credentialViewTypes.put(R.id.contents_usercrt, Credential.Type.USER_CERTIFICATE);
379 credentialViewTypes.put(R.id.contents_cacrt, Credential.Type.CA_CERTIFICATE);
380 }
381
382 protected static View getCredentialView(Credential item, @LayoutRes int layoutResource,
383 @Nullable View view, ViewGroup parent, boolean expanded) {
384 if (view == null) {
385 view = LayoutInflater.from(parent.getContext()).inflate(layoutResource, parent, false);
386 }
387
388 ((TextView) view.findViewById(R.id.alias)).setText(item.alias);
389 ((TextView) view.findViewById(R.id.purpose)).setText(item.isSystem()
390 ? R.string.credential_for_vpn_and_apps
391 : R.string.credential_for_wifi);
392
393 view.findViewById(R.id.contents).setVisibility(expanded ? View.VISIBLE : View.GONE);
394 if (expanded) {
395 for (int i = 0; i < credentialViewTypes.size(); i++) {
396 final View detail = view.findViewById(credentialViewTypes.keyAt(i));
397 detail.setVisibility(item.storedTypes.contains(credentialViewTypes.valueAt(i))
398 ? View.VISIBLE : View.GONE);
399 }
400 }
401 return view;
402 }
403
404 static class AliasEntry {
405 public String alias;
406 public int uid;
407 }
408
Robin Leee2680422016-01-25 12:24:27 +0000409 static class Credential implements Parcelable {
410 static enum Type {
Robin Leebaefdcf2015-08-26 10:57:44 +0100411 CA_CERTIFICATE (Credentials.CA_CERTIFICATE),
412 USER_CERTIFICATE (Credentials.USER_CERTIFICATE),
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700413 USER_KEY(Credentials.USER_PRIVATE_KEY, Credentials.USER_SECRET_KEY);
Robin Leebaefdcf2015-08-26 10:57:44 +0100414
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700415 final String[] prefix;
Robin Leebaefdcf2015-08-26 10:57:44 +0100416
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700417 Type(String... prefix) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100418 this.prefix = prefix;
419 }
420 }
421
422 /**
423 * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the
424 * prefixes from {@link CredentialItem.storedTypes}.
425 */
426 final String alias;
427
428 /**
Robin Leee68d9572016-07-19 16:10:39 +0100429 * UID under which this credential is stored. Typically {@link Process#SYSTEM_UID} but can
430 * also be {@link Process#WIFI_UID} for credentials installed as wifi certificates.
431 */
432 final int uid;
433
434 /**
Robin Leebaefdcf2015-08-26 10:57:44 +0100435 * Should contain some non-empty subset of:
436 * <ul>
437 * <li>{@link Credentials.CA_CERTIFICATE}</li>
438 * <li>{@link Credentials.USER_CERTIFICATE}</li>
Janis Danisevskisb705e1a2017-04-19 16:23:02 -0700439 * <li>{@link Credentials.USER_KEY}</li>
Robin Leebaefdcf2015-08-26 10:57:44 +0100440 * </ul>
441 */
Robin Lee04046a12016-01-19 11:42:57 +0000442 final EnumSet<Type> storedTypes = EnumSet.noneOf(Type.class);
Robin Leebaefdcf2015-08-26 10:57:44 +0100443
Robin Leee68d9572016-07-19 16:10:39 +0100444 Credential(final String alias, final int uid) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100445 this.alias = alias;
Robin Leee68d9572016-07-19 16:10:39 +0100446 this.uid = uid;
Robin Leebaefdcf2015-08-26 10:57:44 +0100447 }
Robin Lee04046a12016-01-19 11:42:57 +0000448
449 Credential(Parcel in) {
Robin Leee68d9572016-07-19 16:10:39 +0100450 this(in.readString(), in.readInt());
Robin Lee04046a12016-01-19 11:42:57 +0000451
452 long typeBits = in.readLong();
453 for (Type i : Type.values()) {
454 if ((typeBits & (1L << i.ordinal())) != 0L) {
455 storedTypes.add(i);
456 }
457 }
458 }
459
460 public void writeToParcel(Parcel out, int flags) {
461 out.writeString(alias);
Robin Leee68d9572016-07-19 16:10:39 +0100462 out.writeInt(uid);
Robin Lee04046a12016-01-19 11:42:57 +0000463
464 long typeBits = 0;
465 for (Type i : storedTypes) {
466 typeBits |= 1L << i.ordinal();
467 }
468 out.writeLong(typeBits);
469 }
470
471 public int describeContents() {
472 return 0;
473 }
474
475 public static final Parcelable.Creator<Credential> CREATOR
476 = new Parcelable.Creator<Credential>() {
477 public Credential createFromParcel(Parcel in) {
478 return new Credential(in);
479 }
480
481 public Credential[] newArray(int size) {
482 return new Credential[size];
483 }
484 };
Robin Leee68d9572016-07-19 16:10:39 +0100485
486 public boolean isSystem() {
487 return UserHandle.getAppId(uid) == Process.SYSTEM_UID;
488 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100489 }
490}