blob: 643876473f070f03101f3195fdbabe8c27f29a31 [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;
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 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;
Robin Leeda7bc512016-02-24 17:39:32 +000037import android.util.Log;
Robin Leebaefdcf2015-08-26 10:57:44 +010038import android.view.LayoutInflater;
39import android.view.View;
40import android.view.ViewGroup;
Robin Leebaefdcf2015-08-26 10:57:44 +010041import android.widget.AdapterView;
42import android.widget.AdapterView.OnItemClickListener;
43import android.widget.ArrayAdapter;
Robin Leebaefdcf2015-08-26 10:57:44 +010044import android.widget.ListView;
45import android.widget.TextView;
46
Robin Leeb70b3d82016-02-01 12:52:16 +000047import com.android.internal.logging.MetricsProto.MetricsEvent;
Robin Leec421db72016-03-11 16:22:23 +000048import com.android.settingslib.RestrictedLockUtils;
49import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
Robin Leeb70b3d82016-02-01 12:52:16 +000050
Robin Leebaefdcf2015-08-26 10:57:44 +010051import java.util.EnumSet;
Robin Leebaefdcf2015-08-26 10:57:44 +010052import java.util.SortedMap;
53import java.util.TreeMap;
54
Robin Leebaefdcf2015-08-26 10:57:44 +010055import static android.view.View.GONE;
Jason Monk39b46742015-09-10 15:52:51 -040056import static android.view.View.VISIBLE;
Robin Leebaefdcf2015-08-26 10:57:44 +010057
Udam Saini0708d9e2016-03-28 16:35:13 -070058public class UserCredentialsSettings extends OptionsMenuFragment implements OnItemClickListener {
Robin Leebaefdcf2015-08-26 10:57:44 +010059 private static final String TAG = "UserCredentialsSettings";
60
61 private View mRootView;
62 private ListView mListView;
63
64 @Override
65 protected int getMetricsCategory() {
Robin Leeb70b3d82016-02-01 12:52:16 +000066 return MetricsEvent.USER_CREDENTIALS;
Robin Leebaefdcf2015-08-26 10:57:44 +010067 }
68
69 @Override
70 public void onResume() {
71 super.onResume();
Robin Lee04046a12016-01-19 11:42:57 +000072 refreshItems();
Robin Leebaefdcf2015-08-26 10:57:44 +010073 }
74
75 @Override
76 public View onCreateView(
77 LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
78 mRootView = inflater.inflate(R.layout.user_credentials, parent, false);
79
80 // Set up an OnItemClickListener for the credential list.
81 mListView = (ListView) mRootView.findViewById(R.id.credential_list);
82 mListView.setOnItemClickListener(this);
83
84 return mRootView;
85 }
86
87 @Override
88 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
89 final Credential item = (Credential) parent.getItemAtPosition(position);
Robin Lee04046a12016-01-19 11:42:57 +000090 CredentialDialogFragment.show(this, item);
91 }
Robin Leebaefdcf2015-08-26 10:57:44 +010092
Robin Lee04046a12016-01-19 11:42:57 +000093 protected void refreshItems() {
94 if (isAdded()) {
95 new AliasLoader().execute();
96 }
97 }
Robin Leebaefdcf2015-08-26 10:57:44 +010098
Robin Lee04046a12016-01-19 11:42:57 +000099 public static class CredentialDialogFragment extends DialogFragment {
100 private static final String TAG = "CredentialDialogFragment";
101 private static final String ARG_CREDENTIAL = "credential";
102
103 public static void show(Fragment target, Credential item) {
104 final Bundle args = new Bundle();
105 args.putParcelable(ARG_CREDENTIAL, item);
106
107 final CredentialDialogFragment frag = new CredentialDialogFragment();
108 frag.setTargetFragment(target, /* requestCode */ -1);
109 frag.setArguments(args);
110 frag.show(target.getFragmentManager(), TAG);
111 }
112
113 @Override
114 public Dialog onCreateDialog(Bundle savedInstanceState) {
115 final Credential item = (Credential) getArguments().getParcelable(ARG_CREDENTIAL);
116 View root = getActivity().getLayoutInflater()
117 .inflate(R.layout.user_credential_dialog, null);
118 ViewGroup infoContainer = (ViewGroup) root.findViewById(R.id.credential_container);
119 View view = new CredentialAdapter(getActivity(), R.layout.user_credential,
120 new Credential[] {item}).getView(0, null, null);
121 infoContainer.addView(view);
122
Robin Leec421db72016-03-11 16:22:23 +0000123 UserManager userManager
124 = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
125
126 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
Robin Lee04046a12016-01-19 11:42:57 +0000127 .setView(root)
128 .setTitle(R.string.user_credential_title)
Robin Leec421db72016-03-11 16:22:23 +0000129 .setPositiveButton(R.string.done, null);
130
131 final String restriction = UserManager.DISALLOW_CONFIG_CREDENTIALS;
132 final int myUserId = UserHandle.myUserId();
133 if (!RestrictedLockUtils.hasBaseUserRestriction(getContext(), restriction, myUserId)) {
134 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
135 @Override public void onClick(DialogInterface dialog, int id) {
136 final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(
137 getContext(), restriction, myUserId);
138 if (admin != null) {
139 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
140 admin);
141 } else {
142 new RemoveCredentialsTask(getContext(), getTargetFragment())
143 .execute(item.alias);
144 }
145 dialog.dismiss();
146 }
147 };
148 builder.setNegativeButton(R.string.trusted_credentials_remove_label, listener);
149 }
150 return builder.create();
Robin Lee04046a12016-01-19 11:42:57 +0000151 }
152
Robin Leeda7bc512016-02-24 17:39:32 +0000153 private class RemoveCredentialsTask extends AsyncTask<String, Void, Void> {
154 private Context context;
155 private Fragment targetFragment;
156
157 public RemoveCredentialsTask(Context context, Fragment targetFragment) {
158 this.context = context;
159 this.targetFragment = targetFragment;
Robin Lee04046a12016-01-19 11:42:57 +0000160 }
Robin Leeda7bc512016-02-24 17:39:32 +0000161
162 @Override
163 protected Void doInBackground(String... aliases) {
164 try {
165 final KeyChainConnection conn = KeyChain.bind(getContext());
166 try {
167 IKeyChainService keyChain = conn.getService();
168 for (String alias : aliases) {
169 keyChain.removeKeyPair(alias);
170 }
171 } catch (RemoteException e) {
172 Log.w(TAG, "Removing credentials", e);
173 } finally {
174 conn.close();
175 }
176 } catch (InterruptedException e) {
177 Log.w(TAG, "Connecting to keychain", e);
178 }
179 return null;
180 }
181
182 @Override
183 protected void onPostExecute(Void result) {
184 if (targetFragment instanceof UserCredentialsSettings) {
185 ((UserCredentialsSettings) targetFragment).refreshItems();
186 }
187 }
Robin Lee04046a12016-01-19 11:42:57 +0000188 }
Robin Leebaefdcf2015-08-26 10:57:44 +0100189 }
190
191 /**
192 * Opens a background connection to KeyStore to list user credentials.
193 * The credentials are stored in a {@link CredentialAdapter} attached to the main
194 * {@link ListView} in the fragment.
195 */
Robin Lee04046a12016-01-19 11:42:57 +0000196 private class AliasLoader extends AsyncTask<Void, Void, SortedMap<String, Credential>> {
Robin Leebaefdcf2015-08-26 10:57:44 +0100197 @Override
Robin Lee04046a12016-01-19 11:42:57 +0000198 protected SortedMap<String, Credential> doInBackground(Void... params) {
Robin Leebaefdcf2015-08-26 10:57:44 +0100199 // Create a list of names for credential sets, ordered by name.
200 SortedMap<String, Credential> credentials = new TreeMap<>();
201 KeyStore keyStore = KeyStore.getInstance();
202 for (final Credential.Type type : Credential.Type.values()) {
203 for (final String alias : keyStore.list(type.prefix)) {
204 Credential c = credentials.get(alias);
205 if (c == null) {
206 credentials.put(alias, (c = new Credential(alias)));
207 }
208 c.storedTypes.add(type);
209 }
210 }
Robin Lee04046a12016-01-19 11:42:57 +0000211 return credentials;
Robin Leebaefdcf2015-08-26 10:57:44 +0100212 }
213
214 @Override
Robin Lee04046a12016-01-19 11:42:57 +0000215 protected void onPostExecute(SortedMap<String, Credential> credentials) {
216 // Convert the results to an array and present them using an ArrayAdapter.
217 mListView.setAdapter(new CredentialAdapter(getContext(), R.layout.user_credential,
218 credentials.values().toArray(new Credential[0])));
Robin Leebaefdcf2015-08-26 10:57:44 +0100219 }
220 }
221
222 /**
223 * Helper class to display {@link Credential}s in a list.
224 */
225 private static class CredentialAdapter extends ArrayAdapter<Credential> {
226 public CredentialAdapter(Context context, int resource, Credential[] objects) {
227 super(context, resource, objects);
228 }
229
230 @Override
231 public View getView(int position, View view, ViewGroup parent) {
232 if (view == null) {
233 view = LayoutInflater.from(getContext())
234 .inflate(R.layout.user_credential, parent, false);
235 }
236 Credential item = getItem(position);
237 ((TextView) view.findViewById(R.id.alias)).setText(item.alias);
238 view.findViewById(R.id.contents_userkey).setVisibility(
239 item.storedTypes.contains(Credential.Type.USER_PRIVATE_KEY) ? VISIBLE : GONE);
240 view.findViewById(R.id.contents_usercrt).setVisibility(
241 item.storedTypes.contains(Credential.Type.USER_CERTIFICATE) ? VISIBLE : GONE);
242 view.findViewById(R.id.contents_cacrt).setVisibility(
243 item.storedTypes.contains(Credential.Type.CA_CERTIFICATE) ? VISIBLE : GONE);
244 return view;
245 }
246 }
247
Robin Leee2680422016-01-25 12:24:27 +0000248 static class Credential implements Parcelable {
249 static enum Type {
Robin Leebaefdcf2015-08-26 10:57:44 +0100250 CA_CERTIFICATE (Credentials.CA_CERTIFICATE),
251 USER_CERTIFICATE (Credentials.USER_CERTIFICATE),
252 USER_PRIVATE_KEY (Credentials.USER_PRIVATE_KEY),
253 USER_SECRET_KEY (Credentials.USER_SECRET_KEY);
254
255 final String prefix;
256
257 Type(String prefix) {
258 this.prefix = prefix;
259 }
260 }
261
262 /**
263 * Main part of the credential's alias. To fetch an item from KeyStore, prepend one of the
264 * prefixes from {@link CredentialItem.storedTypes}.
265 */
266 final String alias;
267
268 /**
269 * Should contain some non-empty subset of:
270 * <ul>
271 * <li>{@link Credentials.CA_CERTIFICATE}</li>
272 * <li>{@link Credentials.USER_CERTIFICATE}</li>
273 * <li>{@link Credentials.USER_PRIVATE_KEY}</li>
274 * <li>{@link Credentials.USER_SECRET_KEY}</li>
275 * </ul>
276 */
Robin Lee04046a12016-01-19 11:42:57 +0000277 final EnumSet<Type> storedTypes = EnumSet.noneOf(Type.class);
Robin Leebaefdcf2015-08-26 10:57:44 +0100278
279 Credential(final String alias) {
280 this.alias = alias;
281 }
Robin Lee04046a12016-01-19 11:42:57 +0000282
283 Credential(Parcel in) {
284 this(in.readString());
285
286 long typeBits = in.readLong();
287 for (Type i : Type.values()) {
288 if ((typeBits & (1L << i.ordinal())) != 0L) {
289 storedTypes.add(i);
290 }
291 }
292 }
293
294 public void writeToParcel(Parcel out, int flags) {
295 out.writeString(alias);
296
297 long typeBits = 0;
298 for (Type i : storedTypes) {
299 typeBits |= 1L << i.ordinal();
300 }
301 out.writeLong(typeBits);
302 }
303
304 public int describeContents() {
305 return 0;
306 }
307
308 public static final Parcelable.Creator<Credential> CREATOR
309 = new Parcelable.Creator<Credential>() {
310 public Credential createFromParcel(Parcel in) {
311 return new Credential(in);
312 }
313
314 public Credential[] newArray(int size) {
315 return new Credential[size];
316 }
317 };
Robin Leebaefdcf2015-08-26 10:57:44 +0100318 }
319}