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.annotation.Nullable; 19 import android.app.admin.DeviceAdminInfo; 20 import android.app.admin.DevicePolicyManager; 21 import android.app.admin.DevicePolicyManager.DeviceOwnerType; 22 import android.content.BroadcastReceiver; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.PackageManager.NameNotFoundException; 30 import android.content.pm.ResolveInfo; 31 import android.content.pm.UserInfo; 32 import android.graphics.drawable.Drawable; 33 import android.net.ConnectivityManager; 34 import android.net.ConnectivityManager.NetworkCallback; 35 import android.net.Network; 36 import android.net.NetworkRequest; 37 import android.net.VpnManager; 38 import android.os.Handler; 39 import android.os.RemoteException; 40 import android.os.UserHandle; 41 import android.os.UserManager; 42 import android.security.KeyChain; 43 import android.util.ArrayMap; 44 import android.util.Log; 45 import android.util.Pair; 46 import android.util.SparseArray; 47 48 import androidx.annotation.NonNull; 49 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.net.LegacyVpnInfo; 52 import com.android.internal.net.VpnConfig; 53 import com.android.systemui.R; 54 import com.android.systemui.broadcast.BroadcastDispatcher; 55 import com.android.systemui.dagger.SysUISingleton; 56 import com.android.systemui.dagger.qualifiers.Background; 57 import com.android.systemui.dagger.qualifiers.Main; 58 import com.android.systemui.dump.DumpManager; 59 import com.android.systemui.settings.UserTracker; 60 61 import org.xmlpull.v1.XmlPullParserException; 62 63 import java.io.IOException; 64 import java.io.PrintWriter; 65 import java.util.ArrayList; 66 import java.util.concurrent.Executor; 67 68 import javax.inject.Inject; 69 70 /** 71 */ 72 @SysUISingleton 73 public class SecurityControllerImpl implements SecurityController { 74 75 private static final String TAG = "SecurityController"; 76 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 77 78 private static final NetworkRequest REQUEST = 79 new NetworkRequest.Builder().clearCapabilities().build(); 80 private static final int NO_NETWORK = -1; 81 82 private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED"; 83 84 private static final int CA_CERT_LOADING_RETRY_TIME_IN_MS = 30_000; 85 86 private final Context mContext; 87 private final UserTracker mUserTracker; 88 private final ConnectivityManager mConnectivityManager; 89 private final VpnManager mVpnManager; 90 private final DevicePolicyManager mDevicePolicyManager; 91 private final PackageManager mPackageManager; 92 private final UserManager mUserManager; 93 private final Executor mMainExecutor; 94 private final Executor mBgExecutor; 95 96 @GuardedBy("mCallbacks") 97 private final ArrayList<SecurityControllerCallback> mCallbacks = new ArrayList<>(); 98 99 private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>(); 100 private int mCurrentUserId; 101 private int mVpnUserId; 102 103 // Key: userId, Value: whether the user has CACerts installed 104 // Needs to be cached here since the query has to be asynchronous 105 private ArrayMap<Integer, Boolean> mHasCACerts = new ArrayMap<Integer, Boolean>(); 106 107 private final UserTracker.Callback mUserChangedCallback = 108 new UserTracker.Callback() { 109 @Override 110 public void onUserChanged(int newUser, @NonNull Context userContext) { 111 onUserSwitched(newUser); 112 } 113 }; 114 115 /** 116 */ 117 @Inject SecurityControllerImpl( Context context, UserTracker userTracker, @Background Handler bgHandler, BroadcastDispatcher broadcastDispatcher, @Main Executor mainExecutor, @Background Executor bgExecutor, DumpManager dumpManager )118 public SecurityControllerImpl( 119 Context context, 120 UserTracker userTracker, 121 @Background Handler bgHandler, 122 BroadcastDispatcher broadcastDispatcher, 123 @Main Executor mainExecutor, 124 @Background Executor bgExecutor, 125 DumpManager dumpManager 126 ) { 127 mContext = context; 128 mUserTracker = userTracker; 129 mDevicePolicyManager = (DevicePolicyManager) 130 context.getSystemService(Context.DEVICE_POLICY_SERVICE); 131 mConnectivityManager = (ConnectivityManager) 132 context.getSystemService(Context.CONNECTIVITY_SERVICE); 133 mVpnManager = context.getSystemService(VpnManager.class); 134 mPackageManager = context.getPackageManager(); 135 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 136 mMainExecutor = mainExecutor; 137 mBgExecutor = bgExecutor; 138 139 dumpManager.registerDumpable(getClass().getSimpleName(), this); 140 141 IntentFilter filter = new IntentFilter(); 142 filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); 143 filter.addAction(Intent.ACTION_USER_UNLOCKED); 144 broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, bgHandler, 145 UserHandle.ALL); 146 147 // TODO: re-register network callback on user change. 148 mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback); 149 onUserSwitched(mUserTracker.getUserId()); 150 mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); 151 } 152 dump(PrintWriter pw, String[] args)153 public void dump(PrintWriter pw, String[] args) { 154 pw.println("SecurityController state:"); 155 pw.print(" mCurrentVpns={"); 156 for (int i = 0 ; i < mCurrentVpns.size(); i++) { 157 if (i > 0) { 158 pw.print(", "); 159 } 160 pw.print(mCurrentVpns.keyAt(i)); 161 pw.print('='); 162 pw.print(mCurrentVpns.valueAt(i).user); 163 } 164 pw.println("}"); 165 } 166 167 @Override isDeviceManaged()168 public boolean isDeviceManaged() { 169 return mDevicePolicyManager.isDeviceManaged(); 170 } 171 172 @Override getDeviceOwnerName()173 public String getDeviceOwnerName() { 174 return mDevicePolicyManager.getDeviceOwnerNameOnAnyUser(); 175 } 176 177 @Override hasProfileOwner()178 public boolean hasProfileOwner() { 179 return mDevicePolicyManager.getProfileOwnerAsUser(mCurrentUserId) != null; 180 } 181 182 @Override getProfileOwnerName()183 public String getProfileOwnerName() { 184 for (int profileId : mUserManager.getProfileIdsWithDisabled(mCurrentUserId)) { 185 String name = mDevicePolicyManager.getProfileOwnerNameAsUser(profileId); 186 if (name != null) { 187 return name; 188 } 189 } 190 return null; 191 } 192 193 @Override getDeviceOwnerOrganizationName()194 public CharSequence getDeviceOwnerOrganizationName() { 195 return mDevicePolicyManager.getDeviceOwnerOrganizationName(); 196 } 197 198 @Override getWorkProfileOrganizationName()199 public CharSequence getWorkProfileOrganizationName() { 200 final int profileId = getWorkProfileUserId(mCurrentUserId); 201 if (profileId == UserHandle.USER_NULL) return null; 202 return mDevicePolicyManager.getOrganizationNameForUser(profileId); 203 } 204 205 @Override getPrimaryVpnName()206 public String getPrimaryVpnName() { 207 VpnConfig cfg = mCurrentVpns.get(mVpnUserId); 208 if (cfg != null) { 209 return getNameForVpnConfig(cfg, new UserHandle(mVpnUserId)); 210 } else { 211 return null; 212 } 213 } 214 getWorkProfileUserId(int userId)215 private int getWorkProfileUserId(int userId) { 216 for (final UserInfo userInfo : mUserManager.getProfiles(userId)) { 217 if (userInfo.isManagedProfile()) { 218 return userInfo.id; 219 } 220 } 221 return UserHandle.USER_NULL; 222 } 223 224 @Override hasWorkProfile()225 public boolean hasWorkProfile() { 226 return getWorkProfileUserId(mCurrentUserId) != UserHandle.USER_NULL; 227 } 228 229 @Override isWorkProfileOn()230 public boolean isWorkProfileOn() { 231 final UserHandle userHandle = UserHandle.of(getWorkProfileUserId(mCurrentUserId)); 232 return userHandle != null && !mUserManager.isQuietModeEnabled(userHandle); 233 } 234 235 @Override isProfileOwnerOfOrganizationOwnedDevice()236 public boolean isProfileOwnerOfOrganizationOwnedDevice() { 237 return mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile(); 238 } 239 240 @Override getWorkProfileVpnName()241 public String getWorkProfileVpnName() { 242 final int profileId = getWorkProfileUserId(mVpnUserId); 243 if (profileId == UserHandle.USER_NULL) return null; 244 VpnConfig cfg = mCurrentVpns.get(profileId); 245 if (cfg != null) { 246 return getNameForVpnConfig(cfg, UserHandle.of(profileId)); 247 } 248 return null; 249 } 250 251 @Override 252 @Nullable getDeviceOwnerComponentOnAnyUser()253 public ComponentName getDeviceOwnerComponentOnAnyUser() { 254 return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser(); 255 } 256 257 // TODO(b/259908270): remove 258 @Override 259 @DeviceOwnerType getDeviceOwnerType(@onNull ComponentName admin)260 public int getDeviceOwnerType(@NonNull ComponentName admin) { 261 return mDevicePolicyManager.getDeviceOwnerType(admin); 262 } 263 264 @Override isFinancedDevice()265 public boolean isFinancedDevice() { 266 return mDevicePolicyManager.isFinancedDevice(); 267 } 268 269 @Override isNetworkLoggingEnabled()270 public boolean isNetworkLoggingEnabled() { 271 return mDevicePolicyManager.isNetworkLoggingEnabled(null); 272 } 273 274 @Override isVpnEnabled()275 public boolean isVpnEnabled() { 276 for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) { 277 if (mCurrentVpns.get(profileId) != null) { 278 return true; 279 } 280 } 281 return false; 282 } 283 284 @Override isVpnRestricted()285 public boolean isVpnRestricted() { 286 UserHandle currentUser = new UserHandle(mCurrentUserId); 287 return mUserManager.getUserInfo(mCurrentUserId).isRestricted() 288 || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, currentUser); 289 } 290 291 @Override isVpnBranded()292 public boolean isVpnBranded() { 293 VpnConfig cfg = mCurrentVpns.get(mVpnUserId); 294 if (cfg == null) { 295 return false; 296 } 297 298 String packageName = getPackageNameForVpnConfig(cfg); 299 if (packageName == null) { 300 return false; 301 } 302 303 return isVpnPackageBranded(packageName); 304 } 305 306 @Override hasCACertInCurrentUser()307 public boolean hasCACertInCurrentUser() { 308 Boolean hasCACerts = mHasCACerts.get(mCurrentUserId); 309 return hasCACerts != null && hasCACerts.booleanValue(); 310 } 311 312 @Override hasCACertInWorkProfile()313 public boolean hasCACertInWorkProfile() { 314 int userId = getWorkProfileUserId(mCurrentUserId); 315 if (userId == UserHandle.USER_NULL) return false; 316 Boolean hasCACerts = mHasCACerts.get(userId); 317 return hasCACerts != null && hasCACerts.booleanValue(); 318 } 319 320 @Override removeCallback(@onNull SecurityControllerCallback callback)321 public void removeCallback(@NonNull SecurityControllerCallback callback) { 322 synchronized (mCallbacks) { 323 if (callback == null) return; 324 if (DEBUG) Log.d(TAG, "removeCallback " + callback); 325 mCallbacks.remove(callback); 326 } 327 } 328 329 @Override addCallback(@onNull SecurityControllerCallback callback)330 public void addCallback(@NonNull SecurityControllerCallback callback) { 331 synchronized (mCallbacks) { 332 if (callback == null || mCallbacks.contains(callback)) return; 333 if (DEBUG) Log.d(TAG, "addCallback " + callback); 334 mCallbacks.add(callback); 335 } 336 } 337 338 @Override onUserSwitched(int newUserId)339 public void onUserSwitched(int newUserId) { 340 mCurrentUserId = newUserId; 341 final UserInfo newUserInfo = mUserManager.getUserInfo(newUserId); 342 if (newUserInfo.isRestricted()) { 343 // VPN for a restricted profile is routed through its owner user 344 mVpnUserId = newUserInfo.restrictedProfileParentId; 345 } else { 346 mVpnUserId = mCurrentUserId; 347 } 348 fireCallbacks(); 349 } 350 351 @Override isParentalControlsEnabled()352 public boolean isParentalControlsEnabled() { 353 return getProfileOwnerOrDeviceOwnerSupervisionComponent() != null; 354 } 355 356 @Override getDeviceAdminInfo()357 public DeviceAdminInfo getDeviceAdminInfo() { 358 return getDeviceAdminInfo(getProfileOwnerOrDeviceOwnerComponent()); 359 } 360 361 @Override getIcon(DeviceAdminInfo info)362 public Drawable getIcon(DeviceAdminInfo info) { 363 return (info == null) ? null : info.loadIcon(mPackageManager); 364 } 365 366 @Override getLabel(DeviceAdminInfo info)367 public CharSequence getLabel(DeviceAdminInfo info) { 368 return (info == null) ? null : info.loadLabel(mPackageManager); 369 } 370 getProfileOwnerOrDeviceOwnerSupervisionComponent()371 private ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent() { 372 UserHandle currentUser = new UserHandle(mCurrentUserId); 373 return mDevicePolicyManager 374 .getProfileOwnerOrDeviceOwnerSupervisionComponent(currentUser); 375 } 376 377 // Returns the ComponentName of the current DO/PO. Right now it only checks the supervision 378 // component but can be changed to check for other DO/POs. This change would make getIcon() 379 // and getLabel() work for all admins. getProfileOwnerOrDeviceOwnerComponent()380 private ComponentName getProfileOwnerOrDeviceOwnerComponent() { 381 return getProfileOwnerOrDeviceOwnerSupervisionComponent(); 382 } 383 getDeviceAdminInfo(ComponentName componentName)384 private DeviceAdminInfo getDeviceAdminInfo(ComponentName componentName) { 385 try { 386 ResolveInfo resolveInfo = new ResolveInfo(); 387 resolveInfo.activityInfo = mPackageManager.getReceiverInfo(componentName, 388 PackageManager.GET_META_DATA); 389 return new DeviceAdminInfo(mContext, resolveInfo); 390 } catch (NameNotFoundException | XmlPullParserException | IOException e) { 391 return null; 392 } 393 } 394 refreshCACerts(int userId)395 private void refreshCACerts(int userId) { 396 mBgExecutor.execute(() -> { 397 Pair<Integer, Boolean> idWithCert = null; 398 try (KeyChain.KeyChainConnection conn = KeyChain.bindAsUser(mContext, 399 UserHandle.of(userId))) { 400 boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty()); 401 idWithCert = new Pair<Integer, Boolean>(userId, hasCACerts); 402 } catch (RemoteException | InterruptedException | AssertionError e) { 403 Log.i(TAG, "failed to get CA certs", e); 404 idWithCert = new Pair<Integer, Boolean>(userId, null); 405 } finally { 406 if (DEBUG) Log.d(TAG, "Refreshing CA Certs " + idWithCert); 407 if (idWithCert != null && idWithCert.second != null) { 408 mHasCACerts.put(idWithCert.first, idWithCert.second); 409 fireCallbacks(); 410 } 411 } 412 }); 413 } 414 getNameForVpnConfig(VpnConfig cfg, UserHandle user)415 private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) { 416 if (cfg.legacy) { 417 return mContext.getString(R.string.legacy_vpn_name); 418 } 419 // The package name for an active VPN is stored in the 'user' field of its VpnConfig 420 final String vpnPackage = cfg.user; 421 try { 422 Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 423 0 /* flags */, user); 424 return VpnConfig.getVpnLabel(userContext, vpnPackage).toString(); 425 } catch (NameNotFoundException nnfe) { 426 Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe); 427 return null; 428 } 429 } 430 fireCallbacks()431 private void fireCallbacks() { 432 synchronized (mCallbacks) { 433 for (SecurityControllerCallback callback : mCallbacks) { 434 callback.onStateChanged(); 435 } 436 } 437 } 438 updateState()439 private void updateState() { 440 // Find all users with an active VPN 441 SparseArray<VpnConfig> vpns = new SparseArray<>(); 442 for (UserInfo user : mUserManager.getUsers()) { 443 VpnConfig cfg = mVpnManager.getVpnConfig(user.id); 444 if (cfg == null) { 445 continue; 446 } else if (cfg.legacy) { 447 // Legacy VPNs should do nothing if the network is disconnected. Third-party 448 // VPN warnings need to continue as traffic can still go to the app. 449 LegacyVpnInfo legacyVpn = mVpnManager.getLegacyVpnInfo(user.id); 450 if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) { 451 continue; 452 } 453 } 454 vpns.put(user.id, cfg); 455 } 456 mCurrentVpns = vpns; 457 } 458 getPackageNameForVpnConfig(VpnConfig cfg)459 private String getPackageNameForVpnConfig(VpnConfig cfg) { 460 if (cfg.legacy) { 461 return null; 462 } 463 return cfg.user; 464 } 465 isVpnPackageBranded(String packageName)466 private boolean isVpnPackageBranded(String packageName) { 467 boolean isBranded; 468 try { 469 ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 470 PackageManager.GET_META_DATA); 471 if (info == null || info.metaData == null || !info.isSystemApp()) { 472 return false; 473 } 474 isBranded = info.metaData.getBoolean(VPN_BRANDED_META_DATA, false); 475 } catch (NameNotFoundException e) { 476 return false; 477 } 478 return isBranded; 479 } 480 481 private final NetworkCallback mNetworkCallback = new NetworkCallback() { 482 @Override 483 public void onAvailable(Network network) { 484 if (DEBUG) Log.d(TAG, "onAvailable " + network.getNetId()); 485 updateState(); 486 fireCallbacks(); 487 }; 488 489 // TODO Find another way to receive VPN lost. This may be delayed depending on 490 // how long the VPN connection is held on to. 491 @Override 492 public void onLost(Network network) { 493 if (DEBUG) Log.d(TAG, "onLost " + network.getNetId()); 494 updateState(); 495 fireCallbacks(); 496 }; 497 }; 498 499 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 500 @Override public void onReceive(Context context, Intent intent) { 501 if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { 502 refreshCACerts(getSendingUserId()); 503 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 504 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 505 if (userId != UserHandle.USER_NULL) refreshCACerts(userId); 506 } 507 } 508 }; 509 } 510