blob: c440acf2e0398873a46d25cdc4d73843c7c9a667 [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 Leebaefdcf2015-08-26 10:57:44 +010020import android.content.Context;
21import android.content.DialogInterface;
22import android.os.AsyncTask;
23import android.os.Bundle;
24import android.security.Credentials;
25import android.security.KeyStore;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
Robin Leebaefdcf2015-08-26 10:57:44 +010029import android.widget.AdapterView;
30import android.widget.AdapterView.OnItemClickListener;
31import android.widget.ArrayAdapter;
32import android.widget.ListAdapter;
Robin Leebaefdcf2015-08-26 10:57:44 +010033import android.widget.ListView;
34import android.widget.TextView;
35
36import java.util.EnumSet;
Robin Leebaefdcf2015-08-26 10:57:44 +010037import java.util.Set;
38import java.util.SortedMap;
39import java.util.TreeMap;
40
Robin Leebaefdcf2015-08-26 10:57:44 +010041import static android.view.View.GONE;
Jason Monk39b46742015-09-10 15:52:51 -040042import static android.view.View.VISIBLE;
Robin Leebaefdcf2015-08-26 10:57:44 +010043
44public class UserCredentialsSettings extends InstrumentedFragment implements OnItemClickListener {
45 private static final String TAG = "UserCredentialsSettings";
46
47 private View mRootView;
48 private ListView mListView;
49
50 @Override
51 protected int getMetricsCategory() {
52 // TODO (rgl): Declare a metrics category for user credentials.
53 return UNDECLARED;
54 }
55
56 @Override
57 public void onResume() {
58 super.onResume();
59 new AliasLoader().execute();
60 }
61
62 @Override
63 public View onCreateView(
64 LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
65 mRootView = inflater.inflate(R.layout.user_credentials, parent, false);
66
67 // Set up an OnItemClickListener for the credential list.
68 mListView = (ListView) mRootView.findViewById(R.id.credential_list);
69 mListView.setOnItemClickListener(this);
70
71 return mRootView;
72 }
73
74 @Override
75 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
76 final Credential item = (Credential) parent.getItemAtPosition(position);
77
78 View root = getActivity().getLayoutInflater()
79 .inflate(R.layout.user_credential_dialog, null);
80 ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container);
81 infoContainer.addView(parent.getAdapter().getView(position, null, null));
82
83 new AlertDialog.Builder(getActivity())
84 .setView(root)
85 .setTitle(R.string.user_credential_title)
86 .setPositiveButton(R.string.done, null)
87 .setNegativeButton(R.string.trusted_credentials_remove_label,
88 new DialogInterface.OnClickListener() {
89 @Override public void onClick(DialogInterface dialog, int id) {
90 final KeyStore ks = KeyStore.getInstance();
91 Credentials.deleteAllTypesForAlias(ks, item.alias);
92 new AliasLoader().execute();
93 dialog.dismiss();
94 }
95 })
96 .show();
97 }
98
99 /**
100 * Opens a background connection to KeyStore to list user credentials.
101 * The credentials are stored in a {@link CredentialAdapter} attached to the main
102 * {@link ListView} in the fragment.
103 */
104 private class AliasLoader extends AsyncTask<Void, Void, ListAdapter> {
105 @Override
106 protected ListAdapter doInBackground(Void... params) {
107 // Create a list of names for credential sets, ordered by name.
108 SortedMap<String, Credential> credentials = new TreeMap<>();
109 KeyStore keyStore = KeyStore.getInstance();
110 for (final Credential.Type type : Credential.Type.values()) {
111 for (final String alias : keyStore.list(type.prefix)) {
112 Credential c = credentials.get(alias);
113 if (c == null) {
114 credentials.put(alias, (c = new Credential(alias)));
115 }
116 c.storedTypes.add(type);
117 }
118 }
119
120 // Flatten to array so that the list can be presented via ArrayAdapter.
121 Credential[] results = credentials.values().toArray(new Credential[0]);
122 return new CredentialAdapter(getActivity(), R.layout.user_credential, results);
123 }
124
125 @Override
126 protected void onPostExecute(ListAdapter credentials) {
127 mListView.setAdapter(credentials);
128 }
129 }
130
131 /**
132 * Helper class to display {@link Credential}s in a list.
133 */
134 private static class CredentialAdapter extends ArrayAdapter<Credential> {
135 public CredentialAdapter(Context context, int resource, Credential[] objects) {
136 super(context, resource, objects);
137 }
138
139 @Override
140 public View getView(int position, View view, ViewGroup parent) {
141 if (view == null) {
142 view = LayoutInflater.from(getContext())
143 .inflate(R.layout.user_credential, parent, false);
144 }
145 Credential item = getItem(position);
146 ((TextView) view.findViewById(R.id.alias)).setText(item.alias);
147 view.findViewById(R.id.contents_userkey).setVisibility(
148 item.storedTypes.contains(Credential.Type.USER_PRIVATE_KEY) ? VISIBLE : GONE);
149 view.findViewById(R.id.contents_usercrt).setVisibility(
150 item.storedTypes.contains(Credential.Type.USER_CERTIFICATE) ? VISIBLE : GONE);
151 view.findViewById(R.id.contents_cacrt).setVisibility(
152 item.storedTypes.contains(Credential.Type.CA_CERTIFICATE) ? VISIBLE : GONE);
153 return view;
154 }
155 }
156
157 private static class Credential {
158 private static enum Type {
159 CA_CERTIFICATE (Credentials.CA_CERTIFICATE),
160 USER_CERTIFICATE (Credentials.USER_CERTIFICATE),
161 USER_PRIVATE_KEY (Credentials.USER_PRIVATE_KEY),
162 USER_SECRET_KEY (Credentials.USER_SECRET_KEY);
163
164 final String prefix;
165
166 Type(String prefix) {
167 this.prefix = prefix;
168 }
169 }
170
171 /**
172 * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the
173 * prefixes from {@link CredentialItem.storedTypes}.
174 */
175 final String alias;
176
177 /**
178 * Should contain some non-empty subset of:
179 * <ul>
180 * <li>{@link Credentials.CA_CERTIFICATE}</li>
181 * <li>{@link Credentials.USER_CERTIFICATE}</li>
182 * <li>{@link Credentials.USER_PRIVATE_KEY}</li>
183 * <li>{@link Credentials.USER_SECRET_KEY}</li>
184 * </ul>
185 */
186 final Set<Type> storedTypes = EnumSet.noneOf(Type.class);
187
188 Credential(final String alias) {
189 this.alias = alias;
190 }
191 }
192}