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