blob: 41bcf433179ab49b4e870b05bd40fae75bbbcc29 [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
19import android.app.AlertDialog;
Robin Lee04046a12016-01-19 11:42:57 +000020import android.app.Dialog;
21import android.app.DialogFragment;
22import android.app.Fragment;
23import android.app.FragmentManager;
Robin Leebaefdcf2015-08-26 10:57:44 +010024import android.content.Context;
25import android.content.DialogInterface;
26import android.os.AsyncTask;
27import android.os.Bundle;
Robin Lee04046a12016-01-19 11:42:57 +000028import android.os.Parcel;
29import android.os.Parcelable;
Robin Leebaefdcf2015-08-26 10:57:44 +010030import android.security.Credentials;
31import android.security.KeyStore;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.ViewGroup;
Robin Leebaefdcf2015-08-26 10:57:44 +010035import android.widget.AdapterView;
36import android.widget.AdapterView.OnItemClickListener;
37import android.widget.ArrayAdapter;
Robin Leebaefdcf2015-08-26 10:57:44 +010038import android.widget.ListView;
39import android.widget.TextView;
40
Robin Leeb70b3d82016-02-01 12:52:16 +000041import com.android.internal.logging.MetricsProto.MetricsEvent;
42
Robin Leebaefdcf2015-08-26 10:57:44 +010043import java.util.EnumSet;
Robin Leebaefdcf2015-08-26 10:57:44 +010044import java.util.Set;
45import java.util.SortedMap;
46import java.util.TreeMap;
47
Robin Leebaefdcf2015-08-26 10:57:44 +010048import static android.view.View.GONE;
Jason Monk39b46742015-09-10 15:52:51 -040049import static android.view.View.VISIBLE;
Robin Leebaefdcf2015-08-26 10:57:44 +010050
51public class UserCredentialsSettings extends InstrumentedFragment implements OnItemClickListener {
52 private static final String TAG = "UserCredentialsSettings";
53
54 private View mRootView;
55 private ListView mListView;
56
57 @Override
58 protected int getMetricsCategory() {
Robin Leeb70b3d82016-02-01 12:52:16 +000059 return MetricsEvent.USER_CREDENTIALS;
Robin Leebaefdcf2015-08-26 10:57:44 +010060 }
61
62 @Override
63 public void onResume() {
64 super.onResume();
Robin Lee04046a12016-01-19 11:42:57 +000065 refreshItems();
Robin Leebaefdcf2015-08-26 10:57:44 +010066 }
67
68 @Override
69 public View onCreateView(
70 LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
71 mRootView = inflater.inflate(R.layout.user_credentials, parent, false);
72
73 // Set up an OnItemClickListener for the credential list.
74 mListView = (ListView) mRootView.findViewById(R.id.credential_list);
75 mListView.setOnItemClickListener(this);
76
77 return mRootView;
78 }
79
80 @Override
81 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
82 final Credential item = (Credential) parent.getItemAtPosition(position);
Robin Lee04046a12016-01-19 11:42:57 +000083 CredentialDialogFragment.show(this, item);
84 }
Robin Leebaefdcf2015-08-26 10:57:44 +010085
Robin Lee04046a12016-01-19 11:42:57 +000086 protected void refreshItems() {
87 if (isAdded()) {
88 new AliasLoader().execute();
89 }
90 }
Robin Leebaefdcf2015-08-26 10:57:44 +010091
Robin Lee04046a12016-01-19 11:42:57 +000092 public static class CredentialDialogFragment extends DialogFragment {
93 private static final String TAG = "CredentialDialogFragment";
94 private static final String ARG_CREDENTIAL = "credential";
95
96 public static void show(Fragment target, Credential item) {
97 final Bundle args = new Bundle();
98 args.putParcelable(ARG_CREDENTIAL, item);
99
100 final CredentialDialogFragment frag = new CredentialDialogFragment();
101 frag.setTargetFragment(target, /* requestCode */ -1);
102 frag.setArguments(args);
103 frag.show(target.getFragmentManager(), TAG);
104 }
105
106 @Override
107 public Dialog onCreateDialog(Bundle savedInstanceState) {
108 final Credential item = (Credential) getArguments().getParcelable(ARG_CREDENTIAL);
109 View root = getActivity().getLayoutInflater()
110 .inflate(R.layout.user_credential_dialog, null);
111 ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container);
112 View view = new CredentialAdapter(getActivity(), R.layout.user_credential,
113 new Credential[] {item}).getView(0, null, null);
114 infoContainer.addView(view);
115
116 return new AlertDialog.Builder(getActivity())
117 .setView(root)
118 .setTitle(R.string.user_credential_title)
119 .setPositiveButton(R.string.done, null)
120 .setNegativeButton(R.string.trusted_credentials_remove_label,
121 new DialogInterface.OnClickListener() {
122 @Override public void onClick(DialogInterface dialog, int id) {
123 final KeyStore ks = KeyStore.getInstance();
124 Credentials.deleteAllTypesForAlias(ks, item.alias);
125 dialog.dismiss();
126 }
127 })
128 .create();
129 }
130
131 @Override
132 public void onDismiss(DialogInterface dialog) {
133 final Fragment target = getTargetFragment();
134 if (target instanceof UserCredentialsSettings) {
135 ((UserCredentialsSettings) target).refreshItems();
136 }
137 super.onDismiss(dialog);
138 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100139 }
140
141 /**
142 * Opens a background connection to KeyStore to list user credentials.
143 * The credentials are stored in a {@link CredentialAdapter} attached to the main
144 * {@link ListView} in the fragment.
145 */
Robin Lee04046a12016-01-19 11:42:57 +0000146 private class AliasLoader extends AsyncTask<Void, Void, SortedMap<String, Credential>> {
Robin Leebaefdcf2015-08-26 10:57:44 +0100147 @Override
Robin Lee04046a12016-01-19 11:42:57 +0000148 protected SortedMap<String, Credential> doInBackground(Void... params) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100149 // Create a list of names for credential sets, ordered by name.
150 SortedMap<String, Credential> credentials = new TreeMap<>();
151 KeyStore keyStore = KeyStore.getInstance();
152 for (final Credential.Type type : Credential.Type.values()) {
153 for (final String alias : keyStore.list(type.prefix)) {
154 Credential c = credentials.get(alias);
155 if (c == null) {
156 credentials.put(alias, (c = new Credential(alias)));
157 }
158 c.storedTypes.add(type);
159 }
160 }
Robin Lee04046a12016-01-19 11:42:57 +0000161 return credentials;
Robin Leebaefdcf2015-08-26 10:57:44 +0100162 }
163
164 @Override
Robin Lee04046a12016-01-19 11:42:57 +0000165 protected void onPostExecute(SortedMap<String, Credential> credentials) {
166 // Convert the results to an array and present them using an ArrayAdapter.
167 mListView.setAdapter(new CredentialAdapter(getContext(), R.layout.user_credential,
168 credentials.values().toArray(new Credential[0])));
Robin Leebaefdcf2015-08-26 10:57:44 +0100169 }
170 }
171
172 /**
173 * Helper class to display {@link Credential}s in a list.
174 */
175 private static class CredentialAdapter extends ArrayAdapter<Credential> {
176 public CredentialAdapter(Context context, int resource, Credential[] objects) {
177 super(context, resource, objects);
178 }
179
180 @Override
181 public View getView(int position, View view, ViewGroup parent) {
182 if (view == null) {
183 view = LayoutInflater.from(getContext())
184 .inflate(R.layout.user_credential, parent, false);
185 }
186 Credential item = getItem(position);
187 ((TextView) view.findViewById(R.id.alias)).setText(item.alias);
188 view.findViewById(R.id.contents_userkey).setVisibility(
189 item.storedTypes.contains(Credential.Type.USER_PRIVATE_KEY) ? VISIBLE : GONE);
190 view.findViewById(R.id.contents_usercrt).setVisibility(
191 item.storedTypes.contains(Credential.Type.USER_CERTIFICATE) ? VISIBLE : GONE);
192 view.findViewById(R.id.contents_cacrt).setVisibility(
193 item.storedTypes.contains(Credential.Type.CA_CERTIFICATE) ? VISIBLE : GONE);
194 return view;
195 }
196 }
197
Robin Leee2680422016-01-25 12:24:27 +0000198 static class Credential implements Parcelable {
199 static enum Type {
Robin Leebaefdcf2015-08-26 10:57:44 +0100200 CA_CERTIFICATE (Credentials.CA_CERTIFICATE),
201 USER_CERTIFICATE (Credentials.USER_CERTIFICATE),
202 USER_PRIVATE_KEY (Credentials.USER_PRIVATE_KEY),
203 USER_SECRET_KEY (Credentials.USER_SECRET_KEY);
204
205 final String prefix;
206
207 Type(String prefix) {
208 this.prefix = prefix;
209 }
210 }
211
212 /**
213 * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the
214 * prefixes from {@link CredentialItem.storedTypes}.
215 */
216 final String alias;
217
218 /**
219 * Should contain some non-empty subset of:
220 * <ul>
221 * <li>{@link Credentials.CA_CERTIFICATE}</li>
222 * <li>{@link Credentials.USER_CERTIFICATE}</li>
223 * <li>{@link Credentials.USER_PRIVATE_KEY}</li>
224 * <li>{@link Credentials.USER_SECRET_KEY}</li>
225 * </ul>
226 */
Robin Lee04046a12016-01-19 11:42:57 +0000227 final EnumSet<Type> storedTypes = EnumSet.noneOf(Type.class);
Robin Leebaefdcf2015-08-26 10:57:44 +0100228
229 Credential(final String alias) {
230 this.alias = alias;
231 }
Robin Lee04046a12016-01-19 11:42:57 +0000232
233 Credential(Parcel in) {
234 this(in.readString());
235
236 long typeBits = in.readLong();
237 for (Type i : Type.values()) {
238 if ((typeBits & (1L << i.ordinal())) != 0L) {
239 storedTypes.add(i);
240 }
241 }
242 }
243
244 public void writeToParcel(Parcel out, int flags) {
245 out.writeString(alias);
246
247 long typeBits = 0;
248 for (Type i : storedTypes) {
249 typeBits |= 1L << i.ordinal();
250 }
251 out.writeLong(typeBits);
252 }
253
254 public int describeContents() {
255 return 0;
256 }
257
258 public static final Parcelable.Creator<Credential> CREATOR
259 = new Parcelable.Creator<Credential>() {
260 public Credential createFromParcel(Parcel in) {
261 return new Credential(in);
262 }
263
264 public Credential[] newArray(int size) {
265 return new Credential[size];
266 }
267 };
Robin Leebaefdcf2015-08-26 10:57:44 +0100268 }
269}