1 /* 2 * Copyright (C) 2011 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 17 package com.android.settings.vpn2; 18 19 import static android.app.AppOpsManager.OP_ACTIVATE_PLATFORM_VPN; 20 import static android.app.AppOpsManager.OP_ACTIVATE_VPN; 21 22 import android.annotation.UiThread; 23 import android.annotation.WorkerThread; 24 import android.app.Activity; 25 import android.app.AppOpsManager; 26 import android.app.settings.SettingsEnums; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageInfo; 31 import android.content.pm.PackageManager; 32 import android.net.ConnectivityManager; 33 import android.net.ConnectivityManager.NetworkCallback; 34 import android.net.Network; 35 import android.net.NetworkCapabilities; 36 import android.net.NetworkRequest; 37 import android.net.VpnManager; 38 import android.os.Bundle; 39 import android.os.Handler; 40 import android.os.HandlerThread; 41 import android.os.Message; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.security.Credentials; 45 import android.security.LegacyVpnProfileStore; 46 import android.text.TextUtils; 47 import android.util.ArrayMap; 48 import android.util.ArraySet; 49 import android.util.Log; 50 import android.view.Menu; 51 import android.view.MenuInflater; 52 import android.view.MenuItem; 53 54 import androidx.annotation.VisibleForTesting; 55 import androidx.preference.Preference; 56 import androidx.preference.PreferenceGroup; 57 import androidx.preference.PreferenceScreen; 58 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.internal.net.LegacyVpnInfo; 61 import com.android.internal.net.VpnConfig; 62 import com.android.internal.net.VpnProfile; 63 import com.android.settings.R; 64 import com.android.settings.Utils; 65 import com.android.settings.dashboard.RestrictedDashboardFragment; 66 import com.android.settings.overlay.FeatureFactory; 67 import com.android.settings.widget.GearPreference; 68 import com.android.settings.widget.GearPreference.OnGearClickListener; 69 import com.android.settingslib.RestrictedLockUtilsInternal; 70 71 import com.google.android.collect.Lists; 72 73 import java.util.ArrayList; 74 import java.util.Collection; 75 import java.util.Collections; 76 import java.util.List; 77 import java.util.Map; 78 import java.util.Set; 79 80 /** 81 * Settings screen listing VPNs. Configured VPNs and networks managed by apps 82 * are shown in the same list. 83 */ 84 public class VpnSettings extends RestrictedDashboardFragment implements 85 Handler.Callback, Preference.OnPreferenceClickListener { 86 private static final String LOG_TAG = "VpnSettings"; 87 private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); 88 89 private static final int RESCAN_MESSAGE = 0; 90 private static final int RESCAN_INTERVAL_MS = 1000; 91 private static final String ADVANCED_VPN_GROUP_KEY = "advanced_vpn_group"; 92 private static final String VPN_GROUP_KEY = "vpn_group"; 93 94 private static final NetworkRequest VPN_REQUEST = new NetworkRequest.Builder() 95 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 96 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 97 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) 98 .build(); 99 100 private ConnectivityManager mConnectivityManager; 101 private UserManager mUserManager; 102 private VpnManager mVpnManager; 103 104 private Map<String, LegacyVpnPreference> mLegacyVpnPreferences = new ArrayMap<>(); 105 private Map<AppVpnInfo, AppPreference> mAppPreferences = new ArrayMap<>(); 106 107 @GuardedBy("this") 108 private Handler mUpdater; 109 private HandlerThread mUpdaterThread; 110 private LegacyVpnInfo mConnectedLegacyVpn; 111 112 private boolean mUnavailable; 113 private AdvancedVpnFeatureProvider mFeatureProvider; 114 private PreferenceScreen mPreferenceScreen; 115 private boolean mIsAdvancedVpnSupported; 116 VpnSettings()117 public VpnSettings() { 118 super(UserManager.DISALLOW_CONFIG_VPN); 119 } 120 121 @Override getMetricsCategory()122 public int getMetricsCategory() { 123 return SettingsEnums.VPN; 124 } 125 126 @Override onActivityCreated(Bundle savedInstanceState)127 public void onActivityCreated(Bundle savedInstanceState) { 128 super.onActivityCreated(savedInstanceState); 129 130 mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); 131 mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 132 mVpnManager = (VpnManager) getSystemService(Context.VPN_MANAGEMENT_SERVICE); 133 mFeatureProvider = FeatureFactory.getFeatureFactory().getAdvancedVpnFeatureProvider(); 134 mIsAdvancedVpnSupported = mFeatureProvider.isAdvancedVpnSupported(getContext()); 135 136 mUnavailable = isUiRestricted(); 137 setHasOptionsMenu(!mUnavailable); 138 139 mPreferenceScreen = getPreferenceScreen(); 140 } 141 142 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)143 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 144 super.onCreateOptionsMenu(menu, inflater); 145 146 if (!getContext().getResources().getBoolean(R.bool.config_show_vpn_options)) { 147 return; 148 } 149 150 // Although FEATURE_IPSEC_TUNNELS should always be present in android S and beyond, 151 // keep this check here just to be safe. 152 if (!getContext().getPackageManager().hasSystemFeature( 153 PackageManager.FEATURE_IPSEC_TUNNELS)) { 154 Log.wtf(LOG_TAG, "FEATURE_IPSEC_TUNNELS missing from system, cannot create new VPNs"); 155 return; 156 } else { 157 // By default, we should inflate this menu. 158 inflater.inflate(R.menu.vpn, menu); 159 } 160 } 161 162 @Override onPrepareOptionsMenu(Menu menu)163 public void onPrepareOptionsMenu(Menu menu) { 164 super.onPrepareOptionsMenu(menu); 165 166 // Disable all actions if VPN configuration has been disallowed 167 for (int i = 0; i < menu.size(); i++) { 168 if (isUiRestrictedByOnlyAdmin()) { 169 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getPrefContext(), 170 menu.getItem(i), getRestrictionEnforcedAdmin()); 171 } else { 172 menu.getItem(i).setEnabled(!mUnavailable); 173 } 174 } 175 } 176 177 @Override onOptionsItemSelected(MenuItem item)178 public boolean onOptionsItemSelected(MenuItem item) { 179 // Generate a new key. Here we just use the current time. 180 if (item.getItemId() == R.id.vpn_create) { 181 long millis = System.currentTimeMillis(); 182 while (mLegacyVpnPreferences.containsKey(Long.toHexString(millis))) { 183 ++millis; 184 } 185 VpnProfile profile = new VpnProfile(Long.toHexString(millis)); 186 ConfigDialogFragment.show(this, profile, true /* editing */, false /* exists */); 187 return true; 188 } 189 return super.onOptionsItemSelected(item); 190 } 191 192 @Override onResume()193 public void onResume() { 194 super.onResume(); 195 196 mUnavailable = mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN); 197 if (mUnavailable) { 198 // Show a message to explain that VPN settings have been disabled 199 if (!isUiRestrictedByOnlyAdmin()) { 200 getEmptyTextView() 201 .setText(com.android.settingslib.R.string.vpn_settings_not_available); 202 } 203 getPreferenceScreen().removeAll(); 204 return; 205 } else { 206 setEmptyView(getEmptyTextView()); 207 getEmptyTextView().setText(R.string.vpn_no_vpns_added); 208 } 209 210 // Start monitoring 211 mConnectivityManager.registerNetworkCallback(VPN_REQUEST, mNetworkCallback); 212 213 // Trigger a refresh 214 mUpdaterThread = new HandlerThread("Refresh VPN list in background"); 215 mUpdaterThread.start(); 216 mUpdater = new Handler(mUpdaterThread.getLooper(), this); 217 mUpdater.sendEmptyMessage(RESCAN_MESSAGE); 218 } 219 220 @Override getPreferenceScreenResId()221 protected int getPreferenceScreenResId() { 222 return R.xml.vpn_settings2; 223 } 224 225 @Override getLogTag()226 protected String getLogTag() { 227 return LOG_TAG; 228 } 229 230 @Override onPause()231 public void onPause() { 232 if (mUnavailable) { 233 super.onPause(); 234 return; 235 } 236 237 // Stop monitoring 238 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 239 240 synchronized (this) { 241 mUpdater.removeCallbacksAndMessages(null); 242 mUpdater = null; 243 mUpdaterThread.quit(); 244 mUpdaterThread = null; 245 } 246 247 super.onPause(); 248 } 249 250 @Override @WorkerThread handleMessage(Message message)251 public boolean handleMessage(Message message) { 252 //Return if activity has been recycled 253 final Activity activity = getActivity(); 254 if (activity == null) { 255 return true; 256 } 257 final Context context = activity.getApplicationContext(); 258 259 // Run heavy RPCs before switching to UI thread 260 final List<VpnProfile> vpnProfiles = loadVpnProfiles(); 261 final List<AppVpnInfo> vpnApps = getVpnApps(context, /* includeProfiles */ true, 262 mFeatureProvider); 263 264 final Map<String, LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns(); 265 final Set<AppVpnInfo> connectedAppVpns = getConnectedAppVpns(); 266 267 final Set<AppVpnInfo> alwaysOnAppVpnInfos = getAlwaysOnAppVpnInfos(); 268 final String lockdownVpnKey = VpnUtils.getLockdownVpn(); 269 270 // Refresh list of VPNs 271 activity.runOnUiThread(new UpdatePreferences(this) 272 .legacyVpns(vpnProfiles, connectedLegacyVpns, lockdownVpnKey) 273 .appVpns(vpnApps, connectedAppVpns, alwaysOnAppVpnInfos)); 274 275 synchronized (this) { 276 if (mUpdater != null) { 277 mUpdater.removeMessages(RESCAN_MESSAGE); 278 mUpdater.sendEmptyMessageDelayed(RESCAN_MESSAGE, RESCAN_INTERVAL_MS); 279 } 280 } 281 return true; 282 } 283 284 @VisibleForTesting 285 static class UpdatePreferences implements Runnable { 286 private List<VpnProfile> vpnProfiles = Collections.<VpnProfile>emptyList(); 287 private List<AppVpnInfo> vpnApps = Collections.<AppVpnInfo>emptyList(); 288 289 private Map<String, LegacyVpnInfo> connectedLegacyVpns = 290 Collections.<String, LegacyVpnInfo>emptyMap(); 291 private Set<AppVpnInfo> connectedAppVpns = Collections.<AppVpnInfo>emptySet(); 292 293 private Set<AppVpnInfo> alwaysOnAppVpnInfos = Collections.<AppVpnInfo>emptySet(); 294 private String lockdownVpnKey = null; 295 296 private final VpnSettings mSettings; 297 UpdatePreferences(VpnSettings settings)298 UpdatePreferences(VpnSettings settings) { 299 mSettings = settings; 300 } 301 legacyVpns(List<VpnProfile> vpnProfiles, Map<String, LegacyVpnInfo> connectedLegacyVpns, String lockdownVpnKey)302 public final UpdatePreferences legacyVpns(List<VpnProfile> vpnProfiles, 303 Map<String, LegacyVpnInfo> connectedLegacyVpns, String lockdownVpnKey) { 304 this.vpnProfiles = vpnProfiles; 305 this.connectedLegacyVpns = connectedLegacyVpns; 306 this.lockdownVpnKey = lockdownVpnKey; 307 return this; 308 } 309 appVpns(List<AppVpnInfo> vpnApps, Set<AppVpnInfo> connectedAppVpns, Set<AppVpnInfo> alwaysOnAppVpnInfos)310 public final UpdatePreferences appVpns(List<AppVpnInfo> vpnApps, 311 Set<AppVpnInfo> connectedAppVpns, Set<AppVpnInfo> alwaysOnAppVpnInfos) { 312 this.vpnApps = vpnApps; 313 this.connectedAppVpns = connectedAppVpns; 314 this.alwaysOnAppVpnInfos = alwaysOnAppVpnInfos; 315 return this; 316 } 317 318 @Override @UiThread run()319 public void run() { 320 if (!mSettings.canAddPreferences()) { 321 return; 322 } 323 324 // Find new VPNs by subtracting existing ones from the full set 325 final Set<Preference> updates = new ArraySet<>(); 326 327 // Add legacy VPNs 328 for (VpnProfile profile : vpnProfiles) { 329 LegacyVpnPreference p = mSettings.findOrCreatePreference(profile, true); 330 if (connectedLegacyVpns.containsKey(profile.key)) { 331 p.setState(connectedLegacyVpns.get(profile.key).state); 332 } else { 333 p.setState(LegacyVpnPreference.STATE_NONE); 334 } 335 p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(profile.key)); 336 p.setInsecureVpn(VpnProfile.isLegacyType(profile.type)); 337 updates.add(p); 338 } 339 340 // Show connected VPNs even if the original entry in keystore is gone 341 for (LegacyVpnInfo vpn : connectedLegacyVpns.values()) { 342 final VpnProfile stubProfile = new VpnProfile(vpn.key); 343 LegacyVpnPreference p = mSettings.findOrCreatePreference(stubProfile, false); 344 p.setState(vpn.state); 345 p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(vpn.key)); 346 // (b/184921649) do not call setInsecureVpn() for connectedLegacyVpns, since the 347 // LegacyVpnInfo does not contain VPN type information, and the profile already 348 // exists within vpnProfiles. 349 updates.add(p); 350 } 351 352 // Add VpnService VPNs 353 for (AppVpnInfo app : vpnApps) { 354 AppPreference p = mSettings.findOrCreatePreference(app); 355 if (connectedAppVpns.contains(app)) { 356 p.setState(AppPreference.STATE_CONNECTED); 357 } else { 358 p.setState(AppPreference.STATE_DISCONNECTED); 359 } 360 p.setAlwaysOn(alwaysOnAppVpnInfos.contains(app)); 361 updates.add(p); 362 } 363 364 // Trim out deleted VPN preferences 365 if (DEBUG) { 366 Log.d(LOG_TAG, "isAdvancedVpnSupported() : " + mSettings.mIsAdvancedVpnSupported); 367 } 368 if (mSettings.mIsAdvancedVpnSupported) { 369 mSettings.setShownAdvancedPreferences(updates); 370 } else { 371 mSettings.setShownPreferences(updates); 372 } 373 } 374 } 375 376 @VisibleForTesting canAddPreferences()377 public boolean canAddPreferences() { 378 return isAdded(); 379 } 380 381 @VisibleForTesting @UiThread setShownPreferences(final Collection<Preference> updates)382 public void setShownPreferences(final Collection<Preference> updates) { 383 retainAllPreference(updates); 384 385 final PreferenceGroup vpnGroup = mPreferenceScreen; 386 updatePreferenceGroup(vpnGroup, updates); 387 388 // Show all new preferences on the screen 389 for (Preference pref : updates) { 390 vpnGroup.addPreference(pref); 391 } 392 } 393 394 @VisibleForTesting @UiThread setShownAdvancedPreferences(final Collection<Preference> updates)395 void setShownAdvancedPreferences(final Collection<Preference> updates) { 396 retainAllPreference(updates); 397 398 PreferenceGroup advancedVpnGroup = mPreferenceScreen.findPreference(ADVANCED_VPN_GROUP_KEY); 399 PreferenceGroup vpnGroup = mPreferenceScreen.findPreference(VPN_GROUP_KEY); 400 advancedVpnGroup.setTitle( 401 mFeatureProvider.getAdvancedVpnPreferenceGroupTitle(getContext())); 402 vpnGroup.setTitle(mFeatureProvider.getVpnPreferenceGroupTitle(getContext())); 403 updatePreferenceGroup(advancedVpnGroup, updates); 404 updatePreferenceGroup(vpnGroup, updates); 405 406 // Show all new preferences on the screen 407 for (Preference pref : updates) { 408 String packageName = ""; 409 if (pref instanceof LegacyVpnPreference) { 410 LegacyVpnPreference legacyPref = (LegacyVpnPreference) pref; 411 packageName = legacyPref.getPackageName(); 412 } else if (pref instanceof AppPreference) { 413 AppPreference appPref = (AppPreference) pref; 414 packageName = appPref.getPackageName(); 415 } 416 if (DEBUG) { 417 Log.d(LOG_TAG, "setShownAdvancedPreferences() package name : " + packageName); 418 } 419 if (TextUtils.equals(packageName, mFeatureProvider.getAdvancedVpnPackageName())) { 420 advancedVpnGroup.addPreference(pref); 421 } else { 422 vpnGroup.addPreference(pref); 423 } 424 } 425 426 advancedVpnGroup.setVisible(advancedVpnGroup.getPreferenceCount() > 0); 427 vpnGroup.setVisible(vpnGroup.getPreferenceCount() > 0); 428 } 429 retainAllPreference(Collection<Preference> updates)430 private void retainAllPreference(Collection<Preference> updates) { 431 mLegacyVpnPreferences.values().retainAll(updates); 432 mAppPreferences.values().retainAll(updates); 433 } 434 updatePreferenceGroup(PreferenceGroup vpnGroup, Collection<Preference> updates)435 private void updatePreferenceGroup(PreferenceGroup vpnGroup, Collection<Preference> updates) { 436 // Change {@param updates} in-place to only contain new preferences that were not already 437 // added to the preference screen. 438 for (int i = vpnGroup.getPreferenceCount() - 1; i >= 0; i--) { 439 Preference p = vpnGroup.getPreference(i); 440 if (updates.contains(p)) { 441 updates.remove(p); 442 } else { 443 vpnGroup.removePreference(p); 444 } 445 } 446 } 447 448 @Override onPreferenceClick(Preference preference)449 public boolean onPreferenceClick(Preference preference) { 450 if (preference instanceof LegacyVpnPreference) { 451 LegacyVpnPreference pref = (LegacyVpnPreference) preference; 452 VpnProfile profile = pref.getProfile(); 453 if (mConnectedLegacyVpn != null && profile.key.equals(mConnectedLegacyVpn.key) && 454 mConnectedLegacyVpn.state == LegacyVpnInfo.STATE_CONNECTED) { 455 try { 456 mConnectedLegacyVpn.intent.send(); 457 return true; 458 } catch (Exception e) { 459 Log.w(LOG_TAG, "Starting config intent failed", e); 460 } 461 } 462 ConfigDialogFragment.show(this, profile, false /* editing */, true /* exists */); 463 return true; 464 } else if (preference instanceof AppPreference) { 465 AppPreference pref = (AppPreference) preference; 466 boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED); 467 String vpnPackageName = pref.getPackageName(); 468 469 if ((!connected) || (isAdvancedVpn(mFeatureProvider, vpnPackageName, getContext()) 470 && !mFeatureProvider.isDisconnectDialogEnabled())) { 471 try { 472 UserHandle user = UserHandle.of(pref.getUserId()); 473 Context userContext = getContext().createPackageContextAsUser( 474 getContext().getPackageName(), 0 /* flags */, user); 475 PackageManager pm = userContext.getPackageManager(); 476 Intent appIntent = pm.getLaunchIntentForPackage(vpnPackageName); 477 if (appIntent != null) { 478 userContext.startActivityAsUser(appIntent, user); 479 return true; 480 } 481 } catch (PackageManager.NameNotFoundException nnfe) { 482 Log.w(LOG_TAG, "VPN provider does not exist: " + pref.getPackageName(), nnfe); 483 } 484 } 485 486 // Already connected or no launch intent available - show an info dialog 487 PackageInfo pkgInfo = pref.getPackageInfo(); 488 AppDialogFragment.show(this, pkgInfo, pref.getLabel(), false /* editing */, connected); 489 return true; 490 } 491 return false; 492 } 493 494 @Override getHelpResource()495 public int getHelpResource() { 496 return R.string.help_url_vpn; 497 } 498 499 private OnGearClickListener mGearListener = new OnGearClickListener() { 500 @Override 501 public void onGearClick(GearPreference p) { 502 if (p instanceof LegacyVpnPreference) { 503 LegacyVpnPreference pref = (LegacyVpnPreference) p; 504 ConfigDialogFragment.show(VpnSettings.this, pref.getProfile(), true /* editing */, 505 true /* exists */); 506 } else if (p instanceof AppPreference) { 507 AppPreference pref = (AppPreference) p; 508 AppManagementFragment.show(getPrefContext(), pref, getMetricsCategory()); 509 } 510 } 511 }; 512 513 private NetworkCallback mNetworkCallback = new NetworkCallback() { 514 @Override 515 public void onAvailable(Network network) { 516 if (mUpdater != null) { 517 mUpdater.sendEmptyMessage(RESCAN_MESSAGE); 518 } 519 } 520 521 @Override 522 public void onLost(Network network) { 523 if (mUpdater != null) { 524 mUpdater.sendEmptyMessage(RESCAN_MESSAGE); 525 } 526 } 527 }; 528 529 @VisibleForTesting @UiThread findOrCreatePreference(VpnProfile profile, boolean update)530 public LegacyVpnPreference findOrCreatePreference(VpnProfile profile, boolean update) { 531 LegacyVpnPreference pref = mLegacyVpnPreferences.get(profile.key); 532 boolean created = false; 533 if (pref == null ) { 534 pref = new LegacyVpnPreference(getPrefContext()); 535 pref.setOnGearClickListener(mGearListener); 536 pref.setOnPreferenceClickListener(this); 537 mLegacyVpnPreferences.put(profile.key, pref); 538 created = true; 539 } 540 if (created || update) { 541 // This can change call-to-call because the profile can update and keep the same key. 542 pref.setProfile(profile); 543 } 544 return pref; 545 } 546 547 @VisibleForTesting @UiThread findOrCreatePreference(AppVpnInfo app)548 public AppPreference findOrCreatePreference(AppVpnInfo app) { 549 AppPreference pref = mAppPreferences.get(app); 550 if (pref == null) { 551 pref = new AppPreference(getPrefContext(), app.userId, app.packageName); 552 pref.setOnGearClickListener(mGearListener); 553 pref.setOnPreferenceClickListener(this); 554 mAppPreferences.put(app, pref); 555 } 556 enableAdvancedVpnGearIconIfNecessary(pref); 557 return pref; 558 } 559 enableAdvancedVpnGearIconIfNecessary(AppPreference pref)560 private void enableAdvancedVpnGearIconIfNecessary(AppPreference pref) { 561 Context context = getContext(); 562 if (!isAdvancedVpn(mFeatureProvider, pref.getPackageName(), context)) { 563 return; 564 } 565 566 boolean isEnabled = false; 567 AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); 568 List<AppOpsManager.PackageOps> apps = 569 appOpsManager.getPackagesForOps( 570 new int[] {OP_ACTIVATE_VPN, OP_ACTIVATE_PLATFORM_VPN}); 571 if (apps != null) { 572 for (AppOpsManager.PackageOps pkg : apps) { 573 if (isAdvancedVpn(mFeatureProvider, pkg.getPackageName(), context)) { 574 isEnabled = true; 575 break; 576 } 577 } 578 } 579 pref.setOnGearClickListener(isEnabled ? mGearListener : null); 580 } 581 582 @WorkerThread getConnectedLegacyVpns()583 private Map<String, LegacyVpnInfo> getConnectedLegacyVpns() { 584 mConnectedLegacyVpn = mVpnManager.getLegacyVpnInfo(UserHandle.myUserId()); 585 if (mConnectedLegacyVpn != null) { 586 return Collections.singletonMap(mConnectedLegacyVpn.key, mConnectedLegacyVpn); 587 } 588 return Collections.emptyMap(); 589 } 590 591 @WorkerThread getConnectedAppVpns()592 private Set<AppVpnInfo> getConnectedAppVpns() { 593 // Mark connected third-party services 594 Set<AppVpnInfo> connections = new ArraySet<>(); 595 for (UserHandle profile : mUserManager.getUserProfiles()) { 596 if (Utils.shouldHideUser(profile, mUserManager)) { 597 continue; 598 } 599 VpnConfig config = mVpnManager.getVpnConfig(profile.getIdentifier()); 600 if (config != null && !config.legacy) { 601 connections.add(new AppVpnInfo(profile.getIdentifier(), config.user)); 602 } 603 } 604 return connections; 605 } 606 607 @WorkerThread getAlwaysOnAppVpnInfos()608 private Set<AppVpnInfo> getAlwaysOnAppVpnInfos() { 609 Set<AppVpnInfo> result = new ArraySet<>(); 610 for (UserHandle profile : mUserManager.getUserProfiles()) { 611 if (Utils.shouldHideUser(profile, mUserManager)) { 612 continue; 613 } 614 final int profileId = profile.getIdentifier(); 615 final String packageName = mVpnManager.getAlwaysOnVpnPackageForUser(profileId); 616 if (packageName != null) { 617 result.add(new AppVpnInfo(profileId, packageName)); 618 } 619 } 620 return result; 621 } 622 getVpnApps(Context context, boolean includeProfiles, AdvancedVpnFeatureProvider featureProvider)623 static List<AppVpnInfo> getVpnApps(Context context, boolean includeProfiles, 624 AdvancedVpnFeatureProvider featureProvider) { 625 return getVpnApps(context, includeProfiles, featureProvider, 626 context.getSystemService(AppOpsManager.class)); 627 } 628 629 @VisibleForTesting getVpnApps(Context context, boolean includeProfiles, AdvancedVpnFeatureProvider featureProvider, AppOpsManager aom)630 static List<AppVpnInfo> getVpnApps(Context context, boolean includeProfiles, 631 AdvancedVpnFeatureProvider featureProvider, AppOpsManager aom) { 632 List<AppVpnInfo> result = Lists.newArrayList(); 633 634 final Set<Integer> profileIds; 635 if (includeProfiles) { 636 profileIds = new ArraySet<>(); 637 UserManager userManager = UserManager.get(context); 638 for (UserHandle profile : userManager.getUserProfiles()) { 639 if (Utils.shouldHideUser(profile, userManager)) { 640 continue; 641 } 642 profileIds.add(profile.getIdentifier()); 643 } 644 } else { 645 profileIds = Collections.singleton(UserHandle.myUserId()); 646 } 647 648 if (featureProvider.isAdvancedVpnSupported(context)) { 649 PackageManager pm = context.getPackageManager(); 650 try { 651 ApplicationInfo appInfo = 652 pm.getApplicationInfo( 653 featureProvider.getAdvancedVpnPackageName(), /* flags= */ 0); 654 int userId = UserHandle.getUserId(appInfo.uid); 655 result.add(new AppVpnInfo(userId, featureProvider.getAdvancedVpnPackageName())); 656 } catch (PackageManager.NameNotFoundException e) { 657 Log.e(LOG_TAG, "Advanced VPN package name not found.", e); 658 } 659 } 660 661 List<AppOpsManager.PackageOps> apps = 662 aom.getPackagesForOps(new int[] {OP_ACTIVATE_VPN, OP_ACTIVATE_PLATFORM_VPN}); 663 if (apps != null) { 664 for (AppOpsManager.PackageOps pkg : apps) { 665 int userId = UserHandle.getUserId(pkg.getUid()); 666 if (!profileIds.contains(userId)) { 667 // Skip packages for users outside of our profile group. 668 continue; 669 } 670 if (isAdvancedVpn(featureProvider, pkg.getPackageName(), context)) { 671 continue; 672 } 673 // Look for a MODE_ALLOWED permission to activate VPN. 674 boolean allowed = false; 675 for (AppOpsManager.OpEntry op : pkg.getOps()) { 676 if ((op.getOp() == OP_ACTIVATE_VPN || op.getOp() == OP_ACTIVATE_PLATFORM_VPN) 677 && op.getMode() == AppOpsManager.MODE_ALLOWED) { 678 allowed = true; 679 } 680 } 681 if (allowed) { 682 result.add(new AppVpnInfo(userId, pkg.getPackageName())); 683 } 684 } 685 } 686 687 Collections.sort(result); 688 return result; 689 } 690 isAdvancedVpn(AdvancedVpnFeatureProvider featureProvider, String packageName, Context context)691 private static boolean isAdvancedVpn(AdvancedVpnFeatureProvider featureProvider, 692 String packageName, Context context) { 693 return featureProvider.isAdvancedVpnSupported(context) 694 && TextUtils.equals(packageName, featureProvider.getAdvancedVpnPackageName()); 695 } 696 loadVpnProfiles()697 private static List<VpnProfile> loadVpnProfiles() { 698 final ArrayList<VpnProfile> result = Lists.newArrayList(); 699 700 for (String key : LegacyVpnProfileStore.list(Credentials.VPN)) { 701 final VpnProfile profile = VpnProfile.decode(key, 702 LegacyVpnProfileStore.get(Credentials.VPN + key)); 703 if (profile != null) { 704 result.add(profile); 705 } 706 } 707 return result; 708 } 709 710 @VisibleForTesting init(PreferenceScreen preferenceScreen, AdvancedVpnFeatureProvider featureProvider)711 void init(PreferenceScreen preferenceScreen, AdvancedVpnFeatureProvider featureProvider) { 712 mPreferenceScreen = preferenceScreen; 713 mFeatureProvider = featureProvider; 714 } 715 } 716