1 /* 2 * Copyright (C) 2016 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 package com.android.settings.network; 17 18 import android.content.Context; 19 import android.content.pm.PackageManager; 20 import android.content.pm.UserInfo; 21 import android.net.ConnectivityManager; 22 import android.net.IConnectivityManager; 23 import android.net.Network; 24 import android.net.NetworkCapabilities; 25 import android.net.NetworkRequest; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.os.RemoteException; 29 import android.os.ServiceManager; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.provider.Settings; 33 import android.support.annotation.VisibleForTesting; 34 import android.support.v7.preference.Preference; 35 import android.support.v7.preference.PreferenceScreen; 36 import android.util.Log; 37 import android.util.SparseArray; 38 39 import com.android.internal.net.LegacyVpnInfo; 40 import com.android.internal.net.VpnConfig; 41 import com.android.settings.R; 42 import com.android.settings.core.PreferenceController; 43 import com.android.settings.core.lifecycle.LifecycleObserver; 44 import com.android.settings.core.lifecycle.events.OnPause; 45 import com.android.settings.core.lifecycle.events.OnResume; 46 import com.android.settingslib.RestrictedLockUtils; 47 48 import java.util.List; 49 50 51 public class VpnPreferenceController extends PreferenceController implements LifecycleObserver, 52 OnResume, OnPause { 53 54 private static final String KEY_VPN_SETTINGS = "vpn_settings"; 55 private static final NetworkRequest REQUEST = new NetworkRequest.Builder() 56 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 57 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 58 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) 59 .build(); 60 private static final String TAG = "VpnPreferenceController"; 61 62 private final String mToggleable; 63 private final UserManager mUserManager; 64 private final ConnectivityManager mConnectivityManager; 65 private final IConnectivityManager mConnectivityManagerService; 66 private Preference mPreference; 67 VpnPreferenceController(Context context)68 public VpnPreferenceController(Context context) { 69 super(context); 70 mToggleable = Settings.Global.getString(context.getContentResolver(), 71 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); 72 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 73 mConnectivityManager = 74 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 75 mConnectivityManagerService = IConnectivityManager.Stub.asInterface( 76 ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); 77 } 78 79 @Override displayPreference(PreferenceScreen screen)80 public void displayPreference(PreferenceScreen screen) { 81 super.displayPreference(screen); 82 mPreference = screen.findPreference(KEY_VPN_SETTINGS); 83 // Manually set dependencies for Wifi when not toggleable. 84 if (mToggleable == null || !mToggleable.contains(Settings.Global.RADIO_WIFI)) { 85 if (mPreference != null) { 86 mPreference.setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE); 87 } 88 } 89 } 90 91 @Override isAvailable()92 public boolean isAvailable() { 93 return !RestrictedLockUtils.hasBaseUserRestriction(mContext, 94 UserManager.DISALLOW_CONFIG_VPN, UserHandle.myUserId()); 95 } 96 97 @Override getPreferenceKey()98 public String getPreferenceKey() { 99 return KEY_VPN_SETTINGS; 100 } 101 102 @Override onPause()103 public void onPause() { 104 if (isAvailable()) { 105 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 106 } 107 } 108 109 @Override onResume()110 public void onResume() { 111 if (isAvailable()) { 112 mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback); 113 } 114 } 115 116 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) updateSummary()117 void updateSummary() { 118 if (mPreference == null) { 119 return; 120 } 121 // Copied from SystemUI::SecurityControllerImpl 122 SparseArray<VpnConfig> vpns = new SparseArray<>(); 123 try { 124 final List<UserInfo> users = mUserManager.getUsers(); 125 for (UserInfo user : users) { 126 VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id); 127 if (cfg == null) { 128 continue; 129 } else if (cfg.legacy) { 130 // Legacy VPNs should do nothing if the network is disconnected. Third-party 131 // VPN warnings need to continue as traffic can still go to the app. 132 final LegacyVpnInfo legacyVpn = 133 mConnectivityManagerService.getLegacyVpnInfo(user.id); 134 if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) { 135 continue; 136 } 137 } 138 vpns.put(user.id, cfg); 139 } 140 } catch (RemoteException rme) { 141 // Roll back to previous state 142 Log.e(TAG, "Unable to list active VPNs", rme); 143 return; 144 } 145 final UserInfo userInfo = mUserManager.getUserInfo(UserHandle.myUserId()); 146 final int uid; 147 if (userInfo.isRestricted()) { 148 uid = userInfo.restrictedProfileParentId; 149 } else { 150 uid = userInfo.id; 151 } 152 VpnConfig vpn = vpns.get(uid); 153 final String summary; 154 if (vpn == null) { 155 summary = mContext.getString(R.string.vpn_disconnected_summary); 156 } else { 157 summary = getNameForVpnConfig(vpn, UserHandle.of(uid)); 158 } 159 new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(summary)); 160 } 161 getNameForVpnConfig(VpnConfig cfg, UserHandle user)162 private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) { 163 if (cfg.legacy) { 164 return mContext.getString(R.string.bluetooth_connected); 165 } 166 // The package name for an active VPN is stored in the 'user' field of its VpnConfig 167 final String vpnPackage = cfg.user; 168 try { 169 Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 170 0 /* flags */, user); 171 return VpnConfig.getVpnLabel(userContext, vpnPackage).toString(); 172 } catch (PackageManager.NameNotFoundException nnfe) { 173 Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe); 174 return null; 175 } 176 } 177 178 // Copied from SystemUI::SecurityControllerImpl 179 private final ConnectivityManager.NetworkCallback 180 mNetworkCallback = new ConnectivityManager.NetworkCallback() { 181 @Override 182 public void onAvailable(Network network) { 183 Log.d(TAG, "onAvailable " + network.netId); 184 updateSummary(); 185 } 186 187 @Override 188 public void onLost(Network network) { 189 Log.d(TAG, "onLost " + network.netId); 190 updateSummary(); 191 } 192 }; 193 } 194