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