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