• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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