diff options
author | 2017-07-12 15:30:00 +0100 | |
---|---|---|
committer | 2017-08-15 10:20:05 +0100 | |
commit | 7376f6c16873e4c8f7c3f7fa27d4be6ea7894014 (patch) | |
tree | e291b6340b5084f6e352bc6d279562829e99889e | |
parent | 0dd82c49f1ea174d2638317cc45510741eaed8d7 (diff) |
Add alert dialog when always-on VPN disconnects.
As part of the improvement to always-on VPN, we're adding this dialog
which is shown when the user taps the "Always-on VPN disconnected"
notification. This dialog shows a relatively detailed explanation of the
situation and offers two actions: 1) to attempt to reconnect, and 2) to
open the VpnSettings page in Settings. As a result, we expect the users
to be more aware of the consequences of a disconnected VPN, and offer
them more actionable options.
Bug: 36650087
Test: manual
Change-Id: I5ae3ff5d25740ea52357012b75d7eb1776dfdc5e
-rw-r--r-- | core/res/res/values/config.xml | 8 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 19 | ||||
-rw-r--r-- | core/res/res/values/symbols.xml | 1 | ||||
-rw-r--r-- | packages/VpnDialogs/AndroidManifest.xml | 26 | ||||
-rw-r--r-- | packages/VpnDialogs/res/layout/always_on_disconnected.xml | 25 | ||||
-rw-r--r-- | packages/VpnDialogs/res/values/strings.xml | 53 | ||||
-rw-r--r-- | packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java | 139 | ||||
-rw-r--r-- | packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java | 6 | ||||
-rw-r--r-- | services/core/java/com/android/server/connectivity/Vpn.java | 6 |
9 files changed, 252 insertions, 31 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bc02ea021632..337c4b518453 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2191,10 +2191,14 @@ <string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" >com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string> - <!-- Name of the CustomDialog that is used for VPN --> - <string name="config_customVpnConfirmDialogComponent" + <!-- Name of the dialog that is used to request the user's consent to VPN connection --> + <string name="config_customVpnConfirmDialogComponent" translatable="false" >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string> + <!-- Name of the dialog that is used to inform the user that always-on VPN is disconnected --> + <string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false" + >com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string> + <!-- Apps that are authorized to access shared accounts, overridden by product overlays --> <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index c97702069210..974e2179061b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3440,16 +3440,21 @@ <!-- The text of the notification when VPN is active with a session name. --> <string name="vpn_text_long">Connected to <xliff:g id="session" example="office">%s</xliff:g>. Tap to manage the network.</string> - <!-- Notification title when connecting to lockdown VPN. --> + <!-- Notification title when connecting to always-on VPN, a VPN that's set to always stay + connected. --> <string name="vpn_lockdown_connecting">Always-on VPN connecting\u2026</string> - <!-- Notification title when connected to lockdown VPN. --> + <!-- Notification title when connected to always-on VPN, a VPN that's set to always stay + connected. --> <string name="vpn_lockdown_connected">Always-on VPN connected</string> - <!-- Notification title when not connected to lockdown VPN. --> - <string name="vpn_lockdown_disconnected">Always-on VPN disconnected</string> - <!-- Notification title when error connecting to lockdown VPN. --> + <!-- Notification title when not connected to always-on VPN, a VPN that's set to always stay + connected. --> + <string name="vpn_lockdown_disconnected">Disconnected from always-on VPN</string> + <!-- Notification title when error connecting to always-on VPN, a VPN that's set to always stay + connected. --> <string name="vpn_lockdown_error">Always-on VPN error</string> - <!-- Notification body that indicates user can touch to configure lockdown VPN connection. --> - <string name="vpn_lockdown_config">Tap to set up</string> + <!-- Notification body that indicates user can touch to configure always-on VPN, a VPN that's + set to always stay connected. --> + <string name="vpn_lockdown_config">Change network or VPN settings</string> <!-- Localized strings for WebView --> <!-- Label for button in a WebView that will open a chooser to choose a file to upload --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 71cb8fef275e..8ad425ae7eca 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1999,6 +1999,7 @@ <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" /> <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" /> <java-symbol type="string" name="config_customVpnConfirmDialogComponent" /> + <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" /> <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> <java-symbol type="string" name="config_persistentDataPackageName" /> diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml index a3d27ce8a3da..8172e717850b 100644 --- a/packages/VpnDialogs/AndroidManifest.xml +++ b/packages/VpnDialogs/AndroidManifest.xml @@ -23,9 +23,10 @@ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <application android:label="VpnDialogs" - android:allowBackup="false" > + android:allowBackup="false"> + <activity android:name=".ConfirmDialog" - android:theme="@android:style/Theme.Material.Light.Dialog.Alert"> + android:theme="@android:style/Theme.Material.Light.Dialog.Alert"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT"/> @@ -33,12 +34,21 @@ </activity> <activity android:name=".ManageDialog" - android:theme="@android:style/Theme.Material.Light.Dialog.Alert" - android:noHistory="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.DEFAULT"/> - </intent-filter> + android:theme="@android:style/Theme.Material.Light.Dialog.Alert" + android:noHistory="true" + android:excludeFromRecents="true" + android:permission="android.permission.NETWORK_SETTINGS" + android:exported="true"> + </activity> + + <activity android:name=".AlwaysOnDisconnectedDialog" + android:label="@string/always_on_disconnected_title" + android:theme="@android:style/Theme.Material.Light.Dialog.Alert" + android:noHistory="true" + android:excludeFromRecents="true" + android:permission="android.permission.NETWORK_SETTINGS" + android:exported="true"> </activity> + </application> </manifest> diff --git a/packages/VpnDialogs/res/layout/always_on_disconnected.xml b/packages/VpnDialogs/res/layout/always_on_disconnected.xml new file mode 100644 index 000000000000..0f4a46d07a9a --- /dev/null +++ b/packages/VpnDialogs/res/layout/always_on_disconnected.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="24dp"> + <TextView android:id="@+id/message" + android:layout_width="fill_parent" + android:layout_height="wrap_content"/> +</ScrollView> diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml index 406bcc34a101..443a9bc33b90 100644 --- a/packages/VpnDialogs/res/values/strings.xml +++ b/packages/VpnDialogs/res/values/strings.xml @@ -18,7 +18,6 @@ <!-- Dialog title to identify the request from a VPN application. [CHAR LIMIT=60] --> <string name="prompt">Connection request</string> - <!-- Dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] --> <string name="warning"><xliff:g id="app">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. @@ -31,11 +30,6 @@ <!-- Dialog title for built-in VPN. [CHAR LIMIT=40] --> <string name="legacy_title">VPN is connected</string> - <!-- Button label to configure the current VPN session. [CHAR LIMIT=20] --> - <string name="configure">Configure</string> - <!-- Button label to disconnect the current VPN session. [CHAR LIMIT=20] --> - <string name="disconnect">Disconnect</string> - <!-- Label for the name of the current VPN session. [CHAR LIMIT=20] --> <string name="session">Session:</string> <!-- Label for the duration of the current VPN session. [CHAR LIMIT=20] --> @@ -44,10 +38,55 @@ <string name="data_transmitted">Sent:</string> <!-- Label for the network usage of data received over VPN. [CHAR LIMIT=20] --> <string name="data_received">Received:</string> - <!-- Formatted string for the network usage over VPN. [CHAR LIMIT=40] --> <string name="data_value_format"> <xliff:g id="number">%1$s</xliff:g> bytes / <xliff:g id="number">%2$s</xliff:g> packets </string> + + <!-- This string is the title of a dialog. The dialog shows up for Android users when always-on + VPN, a VPN that's set to always stay connected, loses its connection. [CHAR LIMIT=60] --> + <string name="always_on_disconnected_title">Can\'t connect to always-on VPN</string> + <!-- This message is part of a dialog. The dialog shows up for users when always-on VPN, a VPN + that's set to always stay connected, loses its connection. Until the phone can reconnect to + the VPN, it'll automatically connect to a public network if possible. This text is followed + by a clickable link that leads to VPN settings. [CHAR LIMIT=NONE] --> + <string name="always_on_disconnected_message"> + <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> is set up to stay connected all + the time, but it can\'t connect right now. Your phone will use a public network until it can + reconnect to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. + </string> + <!-- This message is part of a dialog. The dialog shows up for users when always-on VPN, a VPN + that's set to always stay connected, loses its connection while in the lockdown mode. + Until the phone can reconnect to the VPN, it won't be able to connect to the Internet. This + text is followed by a clickable link that leads to VPN settings. [CHAR LIMIT=NONE] --> + <string name="always_on_disconnected_message_lockdown"> + <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> is set up to stay connected all + the time, but it can\'t connect right now. You won\'t have a connection until the VPN can + reconnect. + </string> + <!-- This is a space separating the body text and the "Change VPN settings" link that follows + it. [CHAR LIMIT=5] --> + <string name="always_on_disconnected_message_separator">" "</string> + <!-- This is a clickable link appended at the end of the body text of a dialog. The dialog shows + up for users when always-on VPN, a VPN that's set to always stay connected, loses its + connection. This link takes the user to the VPN page in Settings. --> + <string name="always_on_disconnected_message_settings_link">Change VPN settings</string> + + <!-- This is the label of a button in a dialog. The button takes the user to the VPN settings + screen. [CHAR LIMIT=20] --> + <string name="configure">Configure</string> + <!-- This is the label of a button in a dialog. The button lets the user disconnect from the + current VPN connection. [CHAR LIMIT=20] --> + <string name="disconnect">Disconnect</string> + <!-- This button is part of a dialog, and it opens the user's VPN app. The dialog shows up for + users when always-on VPN, a VPN that's set to always stay connected, loses its connection. + Until the phone can reconnect to the VPN, it may automatically connect to a public network. + If it doesn't, the user won't have a connection until the VPN reconnects. [CHAR LIMIT=20] + --> + <string name="open_app">Open app</string> + <!-- This is the label of a button in a dialog. The button lets the user dismiss the dialog + without any consequences. [CHAR LIMIT=20] --> + <string name="dismiss">Dismiss</string> + </resources> diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java new file mode 100644 index 000000000000..846fcf867e32 --- /dev/null +++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 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. + */ + +package com.android.vpndialogs; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.IConnectivityManager; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.SpannableStringBuilder; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; + +import com.android.internal.app.AlertActivity; +import com.android.internal.net.VpnConfig; + +public class AlwaysOnDisconnectedDialog extends AlertActivity + implements DialogInterface.OnClickListener{ + + private static final String TAG = "VpnDisconnected"; + + private IConnectivityManager mService; + private int mUserId; + private String mVpnPackage; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mService = IConnectivityManager.Stub.asInterface( + ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + mUserId = UserHandle.myUserId(); + mVpnPackage = getAlwaysOnVpnPackage(); + if (mVpnPackage == null) { + finish(); + return; + } + + View view = View.inflate(this, R.layout.always_on_disconnected, null); + TextView messageView = view.findViewById(R.id.message); + messageView.setText(getMessage(getIntent().getBooleanExtra("lockdown", false))); + messageView.setMovementMethod(LinkMovementMethod.getInstance()); + + mAlertParams.mTitle = getString(R.string.always_on_disconnected_title); + mAlertParams.mPositiveButtonText = getString(R.string.open_app); + mAlertParams.mPositiveButtonListener = this; + mAlertParams.mNegativeButtonText = getString(R.string.dismiss); + mAlertParams.mNegativeButtonListener = this; + mAlertParams.mCancelable = false; + mAlertParams.mView = view; + setupAlert(); + + getWindow().setCloseOnTouchOutside(false); + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case BUTTON_POSITIVE: + PackageManager pm = getPackageManager(); + final Intent intent = pm.getLaunchIntentForPackage(mVpnPackage); + if (intent != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + } + finish(); + break; + case BUTTON_NEGATIVE: + finish(); + break; + default: + break; + } + } + + private String getAlwaysOnVpnPackage() { + try { + return mService.getAlwaysOnVpnPackage(mUserId); + } catch (RemoteException e) { + Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e); + return null; + } + } + + private CharSequence getVpnLabel() { + try { + return VpnConfig.getVpnLabel(this, mVpnPackage); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Can't getVpnLabel() for " + mVpnPackage, e); + return mVpnPackage; + } + } + + private CharSequence getMessage(boolean isLockdown) { + final SpannableStringBuilder message = new SpannableStringBuilder(); + final int baseMessageResId = isLockdown + ? R.string.always_on_disconnected_message_lockdown + : R.string.always_on_disconnected_message; + message.append(getString(baseMessageResId, getVpnLabel())); + message.append(getString(R.string.always_on_disconnected_message_separator)); + message.append(getString(R.string.always_on_disconnected_message_settings_link), + new VpnSpan(), 0 /*flags*/); + return message; + } + + private class VpnSpan extends ClickableSpan { + @Override + public void onClick(View unused) { + final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + } + } +} diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index 2fe6648d82da..01dca7e30e64 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -54,12 +54,6 @@ public class ManageDialog extends AlertActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getCallingPackage() != null) { - Log.e(TAG, getCallingPackage() + " cannot start this activity"); - finish(); - return; - } - try { mService = IConnectivityManager.Stub.asInterface( diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index e82eabfe5ad9..225d7083748f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1416,7 +1416,11 @@ public class Vpn { notificationManager.cancelAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, user); return; } - final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS); + final Intent intent = new Intent(); + intent.setComponent(ComponentName.unflattenFromString(mContext.getString( + R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))); + intent.putExtra("lockdown", mLockdown); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); final PendingIntent configIntent = mSystemServices.pendingIntentGetActivityAsUser( intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, user); final Notification.Builder builder = |