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