Home | History | Annotate | Download | only in policy
      1 /*
      2  * Copyright (C) 2014 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.systemui.statusbar.policy;
     17 
     18 import android.app.ActivityManager;
     19 import android.app.admin.DevicePolicyManager;
     20 import android.content.Context;
     21 import android.content.pm.ApplicationInfo;
     22 import android.content.pm.PackageManager;
     23 import android.content.pm.PackageManager.NameNotFoundException;
     24 import android.content.pm.UserInfo;
     25 import android.net.ConnectivityManager;
     26 import android.net.ConnectivityManager.NetworkCallback;
     27 import android.net.IConnectivityManager;
     28 import android.net.Network;
     29 import android.net.NetworkCapabilities;
     30 import android.net.NetworkRequest;
     31 import android.os.RemoteException;
     32 import android.os.ServiceManager;
     33 import android.os.UserHandle;
     34 import android.os.UserManager;
     35 import android.util.Log;
     36 import android.util.SparseArray;
     37 
     38 import com.android.internal.annotations.GuardedBy;
     39 import com.android.internal.net.LegacyVpnInfo;
     40 import com.android.internal.net.VpnConfig;
     41 import com.android.systemui.R;
     42 
     43 import java.io.FileDescriptor;
     44 import java.io.PrintWriter;
     45 import java.util.ArrayList;
     46 
     47 public class SecurityControllerImpl implements SecurityController {
     48 
     49     private static final String TAG = "SecurityController";
     50     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     51 
     52     private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
     53             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
     54             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
     55             .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
     56             .build();
     57     private static final int NO_NETWORK = -1;
     58 
     59     private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED";
     60 
     61     private final Context mContext;
     62     private final ConnectivityManager mConnectivityManager;
     63     private final IConnectivityManager mConnectivityManagerService;
     64     private final DevicePolicyManager mDevicePolicyManager;
     65     private final PackageManager mPackageManager;
     66     private final UserManager mUserManager;
     67 
     68     @GuardedBy("mCallbacks")
     69     private final ArrayList<SecurityControllerCallback> mCallbacks = new ArrayList<>();
     70 
     71     private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
     72     private int mCurrentUserId;
     73     private int mVpnUserId;
     74 
     75     public SecurityControllerImpl(Context context) {
     76         mContext = context;
     77         mDevicePolicyManager = (DevicePolicyManager)
     78                 context.getSystemService(Context.DEVICE_POLICY_SERVICE);
     79         mConnectivityManager = (ConnectivityManager)
     80                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
     81         mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
     82                 ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
     83         mPackageManager = context.getPackageManager();
     84         mUserManager = (UserManager)
     85                 context.getSystemService(Context.USER_SERVICE);
     86 
     87         // TODO: re-register network callback on user change.
     88         mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
     89         onUserSwitched(ActivityManager.getCurrentUser());
     90     }
     91 
     92     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
     93         pw.println("SecurityController state:");
     94         pw.print("  mCurrentVpns={");
     95         for (int i = 0 ; i < mCurrentVpns.size(); i++) {
     96             if (i > 0) {
     97                 pw.print(", ");
     98             }
     99             pw.print(mCurrentVpns.keyAt(i));
    100             pw.print('=');
    101             pw.print(mCurrentVpns.valueAt(i).user);
    102         }
    103         pw.println("}");
    104     }
    105 
    106     @Override
    107     public boolean isDeviceManaged() {
    108         return mDevicePolicyManager.isDeviceManaged();
    109     }
    110 
    111     @Override
    112     public String getDeviceOwnerName() {
    113         return mDevicePolicyManager.getDeviceOwnerNameOnAnyUser();
    114     }
    115 
    116     @Override
    117     public boolean hasProfileOwner() {
    118         return mDevicePolicyManager.getProfileOwnerAsUser(mCurrentUserId) != null;
    119     }
    120 
    121     @Override
    122     public String getProfileOwnerName() {
    123         for (int profileId : mUserManager.getProfileIdsWithDisabled(mCurrentUserId)) {
    124             String name = mDevicePolicyManager.getProfileOwnerNameAsUser(profileId);
    125             if (name != null) {
    126                 return name;
    127             }
    128         }
    129         return null;
    130     }
    131 
    132     @Override
    133     public String getPrimaryVpnName() {
    134         VpnConfig cfg = mCurrentVpns.get(mVpnUserId);
    135         if (cfg != null) {
    136             return getNameForVpnConfig(cfg, new UserHandle(mVpnUserId));
    137         } else {
    138             return null;
    139         }
    140     }
    141 
    142     @Override
    143     public String getProfileVpnName() {
    144         for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
    145             if (profileId == mVpnUserId) {
    146                 continue;
    147             }
    148             VpnConfig cfg = mCurrentVpns.get(profileId);
    149             if (cfg != null) {
    150                 return getNameForVpnConfig(cfg, UserHandle.of(profileId));
    151             }
    152         }
    153         return null;
    154     }
    155 
    156     @Override
    157     public boolean isVpnEnabled() {
    158         for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
    159             if (mCurrentVpns.get(profileId) != null) {
    160                 return true;
    161             }
    162         }
    163         return false;
    164     }
    165 
    166     @Override
    167     public boolean isVpnRestricted() {
    168         UserHandle currentUser = new UserHandle(mCurrentUserId);
    169         return mUserManager.getUserInfo(mCurrentUserId).isRestricted()
    170                 || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, currentUser);
    171     }
    172 
    173     @Override
    174     public boolean isVpnBranded() {
    175         VpnConfig cfg = mCurrentVpns.get(mVpnUserId);
    176         if (cfg == null) {
    177             return false;
    178         }
    179 
    180         String packageName = getPackageNameForVpnConfig(cfg);
    181         if (packageName == null) {
    182             return false;
    183         }
    184 
    185         return isVpnPackageBranded(packageName);
    186     }
    187 
    188     @Override
    189     public void removeCallback(SecurityControllerCallback callback) {
    190         synchronized (mCallbacks) {
    191             if (callback == null) return;
    192             if (DEBUG) Log.d(TAG, "removeCallback " + callback);
    193             mCallbacks.remove(callback);
    194         }
    195     }
    196 
    197     @Override
    198     public void addCallback(SecurityControllerCallback callback) {
    199         synchronized (mCallbacks) {
    200             if (callback == null || mCallbacks.contains(callback)) return;
    201             if (DEBUG) Log.d(TAG, "addCallback " + callback);
    202             mCallbacks.add(callback);
    203         }
    204     }
    205 
    206     @Override
    207     public void onUserSwitched(int newUserId) {
    208         mCurrentUserId = newUserId;
    209         final UserInfo newUserInfo = mUserManager.getUserInfo(newUserId);
    210         if (newUserInfo.isRestricted()) {
    211             // VPN for a restricted profile is routed through its owner user
    212             mVpnUserId = newUserInfo.restrictedProfileParentId;
    213         } else {
    214             mVpnUserId = mCurrentUserId;
    215         }
    216         fireCallbacks();
    217     }
    218 
    219     private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
    220         if (cfg.legacy) {
    221             return mContext.getString(R.string.legacy_vpn_name);
    222         }
    223         // The package name for an active VPN is stored in the 'user' field of its VpnConfig
    224         final String vpnPackage = cfg.user;
    225         try {
    226             Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(),
    227                     0 /* flags */, user);
    228             return VpnConfig.getVpnLabel(userContext, vpnPackage).toString();
    229         } catch (NameNotFoundException nnfe) {
    230             Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe);
    231             return null;
    232         }
    233     }
    234 
    235     private void fireCallbacks() {
    236         synchronized (mCallbacks) {
    237             for (SecurityControllerCallback callback : mCallbacks) {
    238                 callback.onStateChanged();
    239             }
    240         }
    241     }
    242 
    243     private void updateState() {
    244         // Find all users with an active VPN
    245         SparseArray<VpnConfig> vpns = new SparseArray<>();
    246         try {
    247             for (UserInfo user : mUserManager.getUsers()) {
    248                 VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id);
    249                 if (cfg == null) {
    250                     continue;
    251                 } else if (cfg.legacy) {
    252                     // Legacy VPNs should do nothing if the network is disconnected. Third-party
    253                     // VPN warnings need to continue as traffic can still go to the app.
    254                     LegacyVpnInfo legacyVpn = mConnectivityManagerService.getLegacyVpnInfo(user.id);
    255                     if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
    256                         continue;
    257                     }
    258                 }
    259                 vpns.put(user.id, cfg);
    260             }
    261         } catch (RemoteException rme) {
    262             // Roll back to previous state
    263             Log.e(TAG, "Unable to list active VPNs", rme);
    264             return;
    265         }
    266         mCurrentVpns = vpns;
    267     }
    268 
    269     private String getPackageNameForVpnConfig(VpnConfig cfg) {
    270         if (cfg.legacy) {
    271             return null;
    272         }
    273         return cfg.user;
    274     }
    275 
    276     private boolean isVpnPackageBranded(String packageName) {
    277         boolean isBranded;
    278         try {
    279             ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
    280                 PackageManager.GET_META_DATA);
    281             if (info == null || info.metaData == null || !info.isSystemApp()) {
    282                 return false;
    283             }
    284             isBranded = info.metaData.getBoolean(VPN_BRANDED_META_DATA, false);
    285         } catch (NameNotFoundException e) {
    286             return false;
    287         }
    288         return isBranded;
    289     }
    290 
    291     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
    292         @Override
    293         public void onAvailable(Network network) {
    294             if (DEBUG) Log.d(TAG, "onAvailable " + network.netId);
    295             updateState();
    296             fireCallbacks();
    297         };
    298 
    299         // TODO Find another way to receive VPN lost.  This may be delayed depending on
    300         // how long the VPN connection is held on to.
    301         @Override
    302         public void onLost(Network network) {
    303             if (DEBUG) Log.d(TAG, "onLost " + network.netId);
    304             updateState();
    305             fireCallbacks();
    306         };
    307     };
    308 }
    309