diff options
author | Danny Baumann <dannybaumann@web.de> | 2019-07-03 15:12:22 +0200 |
---|---|---|
committer | Rashed Abdel-Tawab <rashedabdeltawab@gmail.com> | 2019-11-22 06:30:10 +0100 |
commit | 1a2b508355478b19d1f4ca07eb0037bc12766217 (patch) | |
tree | 4770c26f9990fd2e87dc5c424b0d695c066810f5 | |
parent | da3d7d8d88610092a866229c1eca4b2247740136 (diff) | |
download | android_frameworks_base-1a2b508355478b19d1f4ca07eb0037bc12766217.tar.gz android_frameworks_base-1a2b508355478b19d1f4ca07eb0037bc12766217.tar.bz2 android_frameworks_base-1a2b508355478b19d1f4ca07eb0037bc12766217.zip |
SystemUI: Add VPN tile
Allows quickly connecting/disconnecting to/from VPNs.
Change-Id: Iaa9413bf1071805be8764d92bc5e2dd3131fdaed
10 files changed, 487 insertions, 3 deletions
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 61648dc7f1d..7baff8b1e19 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -130,6 +130,8 @@ interface IConnectivityManager VpnConfig getVpnConfig(int userId); + VpnProfile[] getAllLegacyVpns(); + @UnsupportedAppUsage void startLegacyVpn(in VpnProfile profile); diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_vpn.xml new file mode 100644 index 00000000000..d06a270681f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_vpn.xml @@ -0,0 +1,27 @@ +<!-- +Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> +</vector> diff --git a/packages/SystemUI/res/layout/vpn_credentials_dialog.xml b/packages/SystemUI/res/layout/vpn_credentials_dialog.xml new file mode 100644 index 00000000000..71c96ecb18f --- /dev/null +++ b/packages/SystemUI/res/layout/vpn_credentials_dialog.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The LineageOS Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <TextView android:id="@+id/hint" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/vpn_credentials_username" /> + + <EditText android:id="@+id/username" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/vpn_credentials_password" /> + + <EditText android:id="@+id/password" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:password="true" + android:singleLine="true" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml index 8f3fd84653c..7af19af99c0 100644 --- a/packages/SystemUI/res/values/cm_strings.xml +++ b/packages/SystemUI/res/values/cm_strings.xml @@ -88,6 +88,14 @@ <!-- Volume panel QS tile --> <string name="quick_settings_volume_panel_label">Volume panel</string> + <!-- VPN QS tile --> + <string name="quick_settings_vpn_label">VPN</string> + <string name="quick_settings_vpn_connect_dialog_title">Connect to\u2026</string> + <string name="vpn_credentials_hint">Please enter your credentials for connecting to <xliff:g id="name">%s</xliff:g></string> + <string name="vpn_credentials_username">Username</string> + <string name="vpn_credentials_password">Password</string> + <string name="vpn_credentials_dialog_connect">Connect</string> + <!-- Content description of the location tile in quick settings when on, battery saving mode (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_location_battery_saving">Location reporting: battery saving mode.</string> <!-- Content description of the location tile in quick settings when on, sensors only mode (not shown on the screen). [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index af11e7d1cdf..ba7d8a48c81 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -117,7 +117,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,nfc,location,hotspot,inversion,saver,dark,work,cast,night,adb_network,ambient_display,caffeine,heads_up,livedisplay,reading_mode,sync,usb_tether,volume_panel + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,nfc,location,hotspot,inversion,saver,dark,work,cast,night,adb_network,ambient_display,caffeine,heads_up,livedisplay,reading_mode,sync,usb_tether,volume_panel,vpn </string> <!-- The tiles to display in QuickSettings --> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 00a79d0d700..e8a48fd1bf3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -53,6 +53,7 @@ import com.android.systemui.qs.tiles.UiModeNightTile; import com.android.systemui.qs.tiles.UsbTetherTile; import com.android.systemui.qs.tiles.UserTile; import com.android.systemui.qs.tiles.VolumeTile; +import com.android.systemui.qs.tiles.VpnTile; import com.android.systemui.qs.tiles.WifiTile; import com.android.systemui.qs.tiles.WorkModeTile; import com.android.systemui.util.leak.GarbageMonitor; @@ -94,6 +95,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider<SyncTile> mSyncTileProvider; private final Provider<UsbTetherTile> mUsbTetherTileProvider; private final Provider<VolumeTile> mVolumeTileProvider; + private final Provider<VpnTile> mVpnTileProvider; private QSTileHost mHost; @@ -125,7 +127,8 @@ public class QSFactoryImpl implements QSFactory { Provider<ReadingModeTile> readingModeTileProvider, Provider<SyncTile> syncTileProvider, Provider<UsbTetherTile> usbTetherTileProvider, - Provider<VolumeTile> volumeTileProvider) { + Provider<VolumeTile> volumeTileProvider, + Provider<VpnTile> vpnTileProvider) { mWifiTileProvider = wifiTileProvider; mBluetoothTileProvider = bluetoothTileProvider; mCellularTileProvider = cellularTileProvider; @@ -154,6 +157,7 @@ public class QSFactoryImpl implements QSFactory { mSyncTileProvider = syncTileProvider; mUsbTetherTileProvider = usbTetherTileProvider; mVolumeTileProvider = volumeTileProvider; + mVpnTileProvider = vpnTileProvider; } public void setHost(QSTileHost host) { @@ -226,6 +230,8 @@ public class QSFactoryImpl implements QSFactory { return mUsbTetherTileProvider.get(); case "volume_panel": return mVolumeTileProvider.get(); + case "vpn": + return mVpnTileProvider.get(); } // Intent tiles. diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/VpnTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/VpnTile.java new file mode 100644 index 00000000000..a8d5889c084 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/VpnTile.java @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.phone.UnlockMethodCache; +import com.android.systemui.statusbar.policy.SecurityController; +import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback; +import com.android.systemui.statusbar.policy.KeyguardMonitor; + +import java.util.List; +import java.util.Set; +import javax.inject.Inject; + +/** Quick settings tile: VPN **/ +public class VpnTile extends QSTileImpl<BooleanState> { + private final SecurityController mController; + private final KeyguardMonitor mKeyguard; + private final UnlockMethodCache mUnlockMethodCache; + private final Callback mCallback = new Callback(); + private final ActivityStarter mActivityStarter; + + @Inject + public VpnTile(QSHost host) { + super(host); + mController = Dependency.get(SecurityController.class); + mKeyguard = Dependency.get(KeyguardMonitor.class); + mUnlockMethodCache = UnlockMethodCache.getInstance(mHost.getContext()); + mActivityStarter = Dependency.get(ActivityStarter.class); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleSetListening(boolean listening) { + if (DEBUG) Log.d(TAG, "handleSetListening " + listening); + if (listening) { + mController.addCallback(mCallback); + mKeyguard.addCallback(mCallback); + } else { + mController.removeCallback(mCallback); + mKeyguard.removeCallback(mCallback); + } + } + + @Override + protected void handleUserSwitch(int newUserId) { + super.handleUserSwitch(newUserId); + mController.onUserSwitched(newUserId); + } + + @Override + public Intent getLongClickIntent() { + return new Intent(Settings.ACTION_VPN_SETTINGS); + } + + @Override + protected void handleSecondaryClick() { + handleClick(); + } + + @Override + protected void handleClick() { + if (mKeyguard.isSecure() && !mUnlockMethodCache.canSkipBouncer()) { + mActivityStarter.postQSRunnableDismissingKeyguard(() -> { + showConnectDialogOrDisconnect(); + }); + } else { + showConnectDialogOrDisconnect(); + } + } + + private void showConnectDialogOrDisconnect() { + if (mController.isVpnRestricted()) { + return; + } + if (mController.isVpnEnabled()) { + mController.disconnectPrimaryVpn(); + return; + } + final List<VpnProfile> profiles = mController.getConfiguredLegacyVpns(); + final List<String> vpnApps = mController.getVpnAppPackageNames(); + if (profiles.isEmpty() && vpnApps.isEmpty()) { + return; + } else if (profiles.isEmpty() && vpnApps.size() == 1) { + mController.launchVpnApp(vpnApps.get(0)); + return; + } else if (vpnApps.isEmpty() && profiles.size() == 1) { + connectVpnOrAskForCredentials(profiles.get(0)); + return; + } + + mUiHandler.post(() -> { + CharSequence[] labels = new CharSequence[profiles.size() + vpnApps.size()]; + int profileCount = profiles.size(); + for (int i = 0; i < profileCount; i++) { + labels[i] = profiles.get(i).name; + } + for (int i = 0; i < vpnApps.size(); i++) { + try { + labels[profileCount + i] = VpnConfig.getVpnLabel(mContext, vpnApps.get(i)); + } catch (PackageManager.NameNotFoundException e) { + labels[profileCount + i] = vpnApps.get(i); + } + } + + Dialog dialog = new AlertDialog.Builder(mContext) + .setTitle(R.string.quick_settings_vpn_connect_dialog_title) + .setItems(labels, (dlg, which) -> { + if (which < profileCount) { + connectVpnOrAskForCredentials(profiles.get(which)); + } else { + mController.launchVpnApp(vpnApps.get(which - profileCount)); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + prepareAndShowDialog(dialog); + mHost.collapsePanels(); + }); + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_vpn_label); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + state.label = mContext.getString(R.string.quick_settings_vpn_label); + state.value = mController.isVpnEnabled(); + state.secondaryLabel = mController.getPrimaryVpnName(); + state.contentDescription = state.label; + state.icon = ResourceIcon.get(R.drawable.ic_qs_vpn); + boolean hasAnyVpn = mController.getConfiguredLegacyVpns().size() > 0 + || mController.getVpnAppPackageNames().size() > 0; + if (mController.isVpnRestricted() || !hasAnyVpn) { + state.state = Tile.STATE_UNAVAILABLE; + } else if (state.value) { + state.state = Tile.STATE_ACTIVE; + } else { + state.state = Tile.STATE_INACTIVE; + } + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.VPN; + } + + private void connectVpnOrAskForCredentials(VpnProfile profile) { + if (profile.saveLogin) { + mController.connectLegacyVpn(profile); + return; + } + + final LayoutInflater inflater = LayoutInflater.from(mContext); + final View dialogView = inflater.inflate(R.layout.vpn_credentials_dialog, null); + final EditText userNameEditor = dialogView.findViewById(R.id.username); + final EditText passwordEditor = dialogView.findViewById(R.id.password); + final TextView hint = dialogView.findViewById(R.id.hint); + + hint.setText(mContext.getString(R.string.vpn_credentials_hint, profile.name)); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setView(dialogView) + .setPositiveButton(R.string.vpn_credentials_dialog_connect, (dlg, which) -> { + profile.username = userNameEditor.getText().toString(); + profile.password = passwordEditor.getText().toString(); + mController.connectLegacyVpn(profile); + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + prepareAndShowDialog(dialog); + mHost.collapsePanels(); + mUiHandler.post(() -> { + TextWatcher watcher = new UsernameAndPasswordWatcher(userNameEditor, passwordEditor, + dialog.getButton(AlertDialog.BUTTON_POSITIVE)); + userNameEditor.addTextChangedListener(watcher); + passwordEditor.addTextChangedListener(watcher); + }); + } + + private void prepareAndShowDialog(Dialog dialog) { + dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG); + SystemUIDialog.setShowForAllUsers(dialog, true); + SystemUIDialog.registerDismissListener(dialog); + SystemUIDialog.setWindowOnTop(dialog); + mUiHandler.post(() -> dialog.show()); + } + + private static final class UsernameAndPasswordWatcher implements TextWatcher { + private final Button mOkButton; + private final EditText mUserNameEditor; + private final EditText mPasswordEditor; + + public UsernameAndPasswordWatcher(EditText userName, EditText password, Button okButton) { + mUserNameEditor = userName; + mPasswordEditor = password; + mOkButton = okButton; + updateOkButtonState(); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + updateOkButtonState(); + } + + private void updateOkButtonState() { + mOkButton.setEnabled(mUserNameEditor.getText().length() > 0 + && mPasswordEditor.getText().length() > 0); + } + } + + private final class Callback implements SecurityControllerCallback, KeyguardMonitor.Callback { + @Override + public void onStateChanged() { + refreshState(); + } + + @Override + public void onKeyguardShowingChanged() { + refreshState(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index 1fb9b691d52..18faa93887c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -15,9 +15,12 @@ */ package com.android.systemui.statusbar.policy; +import com.android.internal.net.VpnProfile; import com.android.systemui.Dumpable; import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback; +import java.util.List; + public interface SecurityController extends CallbackController<SecurityControllerCallback>, Dumpable { /** Whether the device has device owner, even if not on this user. */ @@ -39,6 +42,12 @@ public interface SecurityController extends CallbackController<SecurityControlle boolean hasCACertInWorkProfile(); void onUserSwitched(int newUserId); + List<VpnProfile> getConfiguredLegacyVpns(); + List<String> getVpnAppPackageNames(); + void connectLegacyVpn(VpnProfile profile); + void launchVpnApp(String packageName); + void disconnectPrimaryVpn(); + public interface SecurityControllerCallback { void onStateChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index d88ae78c5af..90b4c2a1452 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import static com.android.systemui.Dependency.BG_HANDLER_NAME; import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -49,12 +50,15 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; import com.android.systemui.R; import com.android.systemui.settings.CurrentUserTracker; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.inject.Inject; import javax.inject.Named; @@ -81,6 +85,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private static final int CA_CERT_LOADING_RETRY_TIME_IN_MS = 30_000; private final Context mContext; + private final AppOpsManager mAppOpsManager; private final ConnectivityManager mConnectivityManager; private final IConnectivityManager mConnectivityManagerService; private final DevicePolicyManager mDevicePolicyManager; @@ -111,6 +116,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi super(context); mContext = context; mBgHandler = bgHandler; + mAppOpsManager = (AppOpsManager) + context.getSystemService(Context.APP_OPS_SERVICE); mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); mConnectivityManager = (ConnectivityManager) @@ -196,6 +203,82 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } } + @Override + public List<VpnProfile> getConfiguredLegacyVpns() { + try { + return Arrays.asList(mConnectivityManagerService.getAllLegacyVpns()); + } catch (RemoteException e) { + Log.e(TAG, "Failed to connect", e); + return new ArrayList<VpnProfile>(); + } + } + + @Override + public List<String> getVpnAppPackageNames() { + List<String> result = new ArrayList<>(); + List<AppOpsManager.PackageOps> apps = mAppOpsManager.getPackagesForOps( + new int[] {AppOpsManager.OP_ACTIVATE_VPN}); + if (apps != null) { + for (AppOpsManager.PackageOps pkg : apps) { + if (mVpnUserId != UserHandle.getUserId(pkg.getUid())) { + continue; + } + // Look for a MODE_ALLOWED permission to activate VPN. + boolean allowed = false; + for (AppOpsManager.OpEntry op : pkg.getOps()) { + if (op.getOp() == AppOpsManager.OP_ACTIVATE_VPN && + op.getMode() == AppOpsManager.MODE_ALLOWED) { + allowed = true; + break; + } + } + if (allowed) { + result.add(pkg.getPackageName()); + } + } + } + + return result; + } + + @Override + public void connectLegacyVpn(VpnProfile profile) { + try { + mConnectivityManagerService.startLegacyVpn(profile); + } catch (IllegalStateException | RemoteException e) { + Log.e(TAG, "Failed to connect", e); + } + } + + @Override + public void launchVpnApp(String packageName) { + try { + UserHandle user = UserHandle.of(mCurrentUserId); + Context userContext = mContext.createPackageContextAsUser( + mContext.getPackageName(), 0 /* flags */, user); + PackageManager pm = userContext.getPackageManager(); + Intent appIntent = pm.getLaunchIntentForPackage(packageName); + if (appIntent != null) { + userContext.startActivityAsUser(appIntent, user); + } + } catch (NameNotFoundException nnfe) { + Log.w(TAG, "VPN provider does not exist: " + packageName, nnfe); + } + } + + @Override + public void disconnectPrimaryVpn() { + VpnConfig cfg = mCurrentVpns.get(mVpnUserId); + if (cfg != null) { + final String user = cfg.legacy ? VpnConfig.LEGACY_VPN : cfg.user; + try { + mConnectivityManagerService.prepareVpn(user, VpnConfig.LEGACY_VPN, mVpnUserId); + } catch (RemoteException e) { + Log.e(TAG, "Failed to disconnect", e); + } + } + } + private int getWorkProfileUserId(int userId) { for (final UserInfo userInfo : mUserManager.getProfiles(userId)) { if (userInfo.isManagedProfile()) { @@ -312,7 +395,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) { if (cfg.legacy) { - return mContext.getString(R.string.legacy_vpn_name); + return cfg.session != null + ? cfg.session : mContext.getString(R.string.legacy_vpn_name); } // The package name for an active VPN is stored in the 'user' field of its VpnConfig final String vpnPackage = cfg.user; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index fc67c38560f..21aaeb9597f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4331,6 +4331,26 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + @Override + public VpnProfile[] getAllLegacyVpns() { + enforceConnectivityInternalPermission(); + + final ArrayList<VpnProfile> result = new ArrayList<>(); + final long token = Binder.clearCallingIdentity(); + try { + for (String key : mKeyStore.list(Credentials.VPN)) { + final VpnProfile profile = VpnProfile.decode(key, + mKeyStore.get(Credentials.VPN + key)); + if (profile != null) { + result.add(profile); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + return result.toArray(new VpnProfile[result.size()]); + } + /** * Start legacy VPN, controlling native daemons as needed. Creates a * secondary thread to perform connection work, returning quickly. |