1 /* 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 17 package com.android.settings.vpn2; 18 19 import java.util.Arrays; 20 21 import android.app.Dialog; 22 import android.app.DialogFragment; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.net.ConnectivityManager; 26 import android.net.IConnectivityManager; 27 import android.os.Bundle; 28 import android.os.RemoteException; 29 import android.os.ServiceManager; 30 import android.os.UserHandle; 31 import android.security.Credentials; 32 import android.security.KeyStore; 33 import android.util.Log; 34 import android.widget.Toast; 35 36 import com.android.internal.net.LegacyVpnInfo; 37 import com.android.internal.net.VpnConfig; 38 import com.android.internal.net.VpnProfile; 39 import com.android.settings.R; 40 41 /** 42 * Fragment wrapper around a {@link ConfigDialog}. 43 */ 44 public class ConfigDialogFragment extends DialogFragment implements 45 DialogInterface.OnClickListener { 46 private static final String TAG_CONFIG_DIALOG = "vpnconfigdialog"; 47 private static final String TAG = "ConfigDialogFragment"; 48 49 private static final String ARG_PROFILE = "profile"; 50 private static final String ARG_EDITING = "editing"; 51 private static final String ARG_EXISTS = "exists"; 52 53 private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface( 54 ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); 55 56 private boolean mUnlocking = false; 57 58 public static void show(VpnSettings parent, VpnProfile profile, boolean edit, boolean exists) { 59 if (!parent.isAdded()) return; 60 61 Bundle args = new Bundle(); 62 args.putParcelable(ARG_PROFILE, profile); 63 args.putBoolean(ARG_EDITING, edit); 64 args.putBoolean(ARG_EXISTS, exists); 65 66 final ConfigDialogFragment frag = new ConfigDialogFragment(); 67 frag.setArguments(args); 68 frag.setTargetFragment(parent, 0); 69 frag.show(parent.getFragmentManager(), TAG_CONFIG_DIALOG); 70 } 71 72 @Override 73 public void onResume() { 74 super.onResume(); 75 76 // Check KeyStore here, so others do not need to deal with it. 77 if (!KeyStore.getInstance().isUnlocked()) { 78 if (!mUnlocking) { 79 // Let us unlock KeyStore. See you later! 80 Credentials.getInstance().unlock(getActivity()); 81 } else { 82 // We already tried, but it is still not working! 83 dismiss(); 84 } 85 mUnlocking = !mUnlocking; 86 return; 87 } 88 89 // Now KeyStore is always unlocked. Reset the flag. 90 mUnlocking = false; 91 } 92 93 @Override 94 public Dialog onCreateDialog(Bundle savedInstanceState) { 95 Bundle args = getArguments(); 96 VpnProfile profile = (VpnProfile) args.getParcelable(ARG_PROFILE); 97 boolean editing = args.getBoolean(ARG_EDITING); 98 boolean exists = args.getBoolean(ARG_EXISTS); 99 100 return new ConfigDialog(getActivity(), this, profile, editing, exists); 101 } 102 103 @Override 104 public void onClick(DialogInterface dialogInterface, int button) { 105 ConfigDialog dialog = (ConfigDialog) getDialog(); 106 VpnProfile profile = dialog.getProfile(); 107 108 if (button == DialogInterface.BUTTON_POSITIVE) { 109 // Update KeyStore entry 110 KeyStore.getInstance().put(Credentials.VPN + profile.key, profile.encode(), 111 KeyStore.UID_SELF, /* flags */ 0); 112 113 // Flush out old version of profile 114 disconnect(profile); 115 116 updateLockdownVpn(dialog.isVpnAlwaysOn(), profile); 117 118 // If we are not editing, connect! 119 if (!dialog.isEditing() && !VpnUtils.isVpnLockdown(profile.key)) { 120 try { 121 connect(profile); 122 } catch (RemoteException e) { 123 Log.e(TAG, "Failed to connect", e); 124 } 125 } 126 } else if (button == DialogInterface.BUTTON_NEUTRAL) { 127 // Disable profile if connected 128 disconnect(profile); 129 130 // Delete from KeyStore 131 KeyStore keyStore = KeyStore.getInstance(); 132 keyStore.delete(Credentials.VPN + profile.key, KeyStore.UID_SELF); 133 134 updateLockdownVpn(false, profile); 135 } 136 dismiss(); 137 } 138 139 @Override 140 public void onCancel(DialogInterface dialog) { 141 dismiss(); 142 super.onCancel(dialog); 143 } 144 145 private void updateLockdownVpn(boolean isVpnAlwaysOn, VpnProfile profile) { 146 // Save lockdown vpn 147 if (isVpnAlwaysOn) { 148 // Show toast if vpn profile is not valid 149 if (!profile.isValidLockdownProfile()) { 150 Toast.makeText(getContext(), R.string.vpn_lockdown_config_error, 151 Toast.LENGTH_LONG).show(); 152 return; 153 } 154 155 final ConnectivityManager conn = ConnectivityManager.from(getActivity()); 156 conn.setAlwaysOnVpnPackageForUser(UserHandle.myUserId(), null, 157 /* lockdownEnabled */ false); 158 VpnUtils.setLockdownVpn(getContext(), profile.key); 159 } else { 160 // update only if lockdown vpn has been changed 161 if (VpnUtils.isVpnLockdown(profile.key)) { 162 VpnUtils.clearLockdownVpn(getContext()); 163 } 164 } 165 } 166 167 private void connect(VpnProfile profile) throws RemoteException { 168 try { 169 mService.startLegacyVpn(profile); 170 } catch (IllegalStateException e) { 171 Toast.makeText(getActivity(), R.string.vpn_no_network, Toast.LENGTH_LONG).show(); 172 } 173 } 174 175 private void disconnect(VpnProfile profile) { 176 try { 177 LegacyVpnInfo connected = mService.getLegacyVpnInfo(UserHandle.myUserId()); 178 if (connected != null && profile.key.equals(connected.key)) { 179 VpnUtils.clearLockdownVpn(getContext()); 180 mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, 181 UserHandle.myUserId()); 182 } 183 } catch (RemoteException e) { 184 Log.e(TAG, "Failed to disconnect", e); 185 } 186 } 187 } 188