• 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.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