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