1 /* 2 * Copyright (C) 2020 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 17 package com.android.settings.security; 18 19 import android.app.Dialog; 20 import android.app.admin.DevicePolicyEventLogger; 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.RemoteException; 27 import android.security.IKeyChainService; 28 import android.security.KeyChain; 29 import android.stats.devicepolicy.DevicePolicyEnums; 30 import android.util.Log; 31 import android.view.View; 32 33 import androidx.appcompat.app.AlertDialog; 34 import androidx.fragment.app.Fragment; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.settings.R; 38 import com.android.settings.core.BasePreferenceController; 39 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 40 import com.android.settingslib.widget.ActionButtonsPreference; 41 42 import java.util.concurrent.ExecutorService; 43 import java.util.concurrent.Executors; 44 45 /** 46 * Controller that shows the remove button of the credential management app, which allows the user 47 * to remove the credential management app and its certificates. 48 */ 49 public class CredentialManagementAppButtonsController extends BasePreferenceController { 50 51 private static final String TAG = "CredentialManagementApp"; 52 53 private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); 54 private final Handler mHandler = new Handler(Looper.getMainLooper()); 55 private boolean mHasCredentialManagerPackage; 56 private Fragment mFragment; 57 private final int mRemoveIcon; 58 CredentialManagementAppButtonsController(Context context, String preferenceKey)59 public CredentialManagementAppButtonsController(Context context, String preferenceKey) { 60 super(context, preferenceKey); 61 if (context.getResources().getConfiguration().getLayoutDirection() 62 == View.LAYOUT_DIRECTION_RTL) { 63 mRemoveIcon = R.drawable.ic_redo_24; 64 } else { 65 mRemoveIcon = R.drawable.ic_undo_24; 66 } 67 } 68 setParentFragment(Fragment fragment)69 public void setParentFragment(Fragment fragment) { 70 mFragment = fragment; 71 } 72 73 @Override getAvailabilityStatus()74 public int getAvailabilityStatus() { 75 return AVAILABLE_UNSEARCHABLE; 76 } 77 78 @Override displayPreference(PreferenceScreen screen)79 public void displayPreference(PreferenceScreen screen) { 80 super.displayPreference(screen); 81 82 mExecutor.execute(() -> { 83 try { 84 IKeyChainService service = KeyChain.bind(mContext).getService(); 85 mHasCredentialManagerPackage = service.hasCredentialManagementApp(); 86 } catch (InterruptedException | RemoteException e) { 87 Log.e(TAG, "Unable to display credential management app buttons"); 88 } 89 mHandler.post(() -> displayButtons(screen)); 90 }); 91 } 92 displayButtons(PreferenceScreen screen)93 private void displayButtons(PreferenceScreen screen) { 94 if (mHasCredentialManagerPackage) { 95 ((ActionButtonsPreference) screen.findPreference(getPreferenceKey())) 96 .setButton1Text(R.string.uninstall_certs_credential_management_app) 97 .setButton1Icon(R.drawable.ic_upload) 98 .setButton1OnClickListener(view -> uninstallCertificates()) 99 .setButton2Text(R.string.remove_credential_management_app) 100 .setButton2Icon(mRemoveIcon) 101 .setButton2OnClickListener(view -> showRemoveCredentialManagementAppDialog()); 102 } 103 } 104 uninstallCertificates()105 private void uninstallCertificates() { 106 mExecutor.execute(() -> { 107 try { 108 IKeyChainService service = KeyChain.bind(mContext).getService(); 109 for (String existingAlias : 110 service.getCredentialManagementAppPolicy().getAliases()) { 111 service.removeKeyPair(existingAlias); 112 } 113 } catch (InterruptedException | RemoteException e) { 114 Log.e(TAG, "Unable to uninstall certificates"); 115 } 116 }); 117 } 118 showRemoveCredentialManagementAppDialog()119 private void showRemoveCredentialManagementAppDialog() { 120 final RemoveCredentialManagementAppDialog dialog = 121 RemoveCredentialManagementAppDialog.newInstance(); 122 dialog.show(mFragment.getParentFragmentManager(), 123 RemoveCredentialManagementAppDialog.class.getName()); 124 } 125 126 /** 127 * Implements an AlertDialog for confirming that a user wants to remove the credential 128 * management app. The app will no longer be able to manage certificates, but it will stay on 129 * the device. All certificates installed by the credential management app will be uninstalled. 130 */ 131 public static class RemoveCredentialManagementAppDialog extends InstrumentedDialogFragment { 132 newInstance()133 public static RemoveCredentialManagementAppDialog newInstance() { 134 return new RemoveCredentialManagementAppDialog(); 135 } 136 137 @Override onCreateDialog(Bundle savedInstanceState)138 public Dialog onCreateDialog(Bundle savedInstanceState) { 139 return new AlertDialog.Builder(getContext(), R.style.Theme_AlertDialog) 140 .setTitle(R.string.remove_credential_management_app_dialog_title) 141 .setMessage(R.string.remove_credential_management_app_dialog_message) 142 .setPositiveButton(R.string.remove_credential_management_app, 143 (dialog, which) -> removeCredentialManagementApp()) 144 .setNegativeButton(R.string.cancel, (dialog, which) -> dismiss()) 145 .create(); 146 } 147 removeCredentialManagementApp()148 private void removeCredentialManagementApp() { 149 final ExecutorService executor = Executors.newSingleThreadExecutor(); 150 executor.execute(() -> { 151 try { 152 IKeyChainService service = KeyChain.bind(getContext()).getService(); 153 service.removeCredentialManagementApp(); 154 DevicePolicyEventLogger 155 .createEvent(DevicePolicyEnums.CREDENTIAL_MANAGEMENT_APP_REMOVED) 156 .write(); 157 getParentFragment().getActivity().finish(); 158 } catch (InterruptedException | RemoteException e) { 159 Log.e(TAG, "Unable to remove the credential management app"); 160 } 161 }); 162 } 163 164 @Override getMetricsCategory()165 public int getMetricsCategory() { 166 return SettingsEnums.CREDENTIAL_MANAGEMENT_APP_REMOVE_APP; 167 } 168 } 169 }