Home | History | Annotate | Download | only in network
      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.support.annotation.VisibleForTesting;
     33 import android.support.v7.preference.Preference;
     34 import android.support.v7.preference.PreferenceScreen;
     35 import android.util.Log;
     36 import android.util.SparseArray;
     37 
     38 import com.android.internal.net.LegacyVpnInfo;
     39 import com.android.internal.net.VpnConfig;
     40 import com.android.settings.R;
     41 import com.android.settings.core.PreferenceControllerMixin;
     42 import com.android.settingslib.RestrictedLockUtils;
     43 import com.android.settingslib.core.AbstractPreferenceController;
     44 import com.android.settingslib.core.lifecycle.LifecycleObserver;
     45 import com.android.settingslib.core.lifecycle.events.OnPause;
     46 import com.android.settingslib.core.lifecycle.events.OnResume;
     47 import com.android.settingslib.utils.ThreadUtils;
     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 
     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
     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(SettingsSlicesContract.KEY_AIRPLANE_MODE);
     88             }
     89         }
     90     }
     91 
     92     @Override
     93     public boolean isAvailable() {
     94         return !RestrictedLockUtils.hasBaseUserRestriction(mContext,
     95                 UserManager.DISALLOW_CONFIG_VPN, UserHandle.myUserId());
     96     }
     97 
     98     @Override
     99     public String getPreferenceKey() {
    100         return KEY_VPN_SETTINGS;
    101     }
    102 
    103     @Override
    104     public void onPause() {
    105         if (isAvailable()) {
    106             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
    107         }
    108     }
    109 
    110     @Override
    111     public void onResume() {
    112         if (isAvailable()) {
    113             mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
    114         }
    115     }
    116 
    117     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    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         ThreadUtils.postOnMainThread(() -> mPreference.setSummary(summary));
    161     }
    162 
    163     @VisibleForTesting
    164     String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
    165         if (cfg.legacy) {
    166             return mContext.getString(R.string.wifi_display_status_connected);
    167         }
    168         // The package name for an active VPN is stored in the 'user' field of its VpnConfig
    169         final String vpnPackage = cfg.user;
    170         try {
    171             Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(),
    172                     0 /* flags */, user);
    173             return VpnConfig.getVpnLabel(userContext, vpnPackage).toString();
    174         } catch (PackageManager.NameNotFoundException nnfe) {
    175             Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe);
    176             return null;
    177         }
    178     }
    179 
    180     // Copied from SystemUI::SecurityControllerImpl
    181     private final ConnectivityManager.NetworkCallback
    182             mNetworkCallback = new ConnectivityManager.NetworkCallback() {
    183         @Override
    184         public void onAvailable(Network network) {
    185             updateSummary();
    186         }
    187 
    188         @Override
    189         public void onLost(Network network) {
    190             updateSummary();
    191         }
    192     };
    193 }
    194