diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6648d1f625af2..617afcd42f5de 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2173,10 +2173,14 @@
com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity
-
-
+ com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog
+
+ com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog
+
;com.android.settings;
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 61d2108780d27..23290565172ef 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3463,16 +3463,21 @@
Connected to %s. Tap to manage the network.
-
+
Always-on VPN connecting\u2026
-
+
Always-on VPN connected
-
- Always-on VPN disconnected
-
+
+ Disconnected from always-on VPN
+
Always-on VPN error
-
- Tap to set up
+
+ Change network or VPN settings
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c1390b0c39bb9..a6993ea32d57a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2009,6 +2009,7 @@
+
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index a3d27ce8a3dab..8172e717850bd 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -23,9 +23,10 @@
+ android:allowBackup="false">
+
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
@@ -33,12 +34,21 @@
-
-
-
-
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:permission="android.permission.NETWORK_SETTINGS"
+ android:exported="true">
+
+
+
+
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 0000000000000..0f4a46d07a9a6
--- /dev/null
+++ b/packages/VpnDialogs/res/layout/always_on_disconnected.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
index 406bcc34a1015..443a9bc33b90f 100644
--- a/packages/VpnDialogs/res/values/strings.xml
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -18,7 +18,6 @@
Connection request
-
%s 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 @@
VPN is connected
-
- Configure
-
- Disconnect
-
Session:
@@ -44,10 +38,55 @@
Sent:
Received:
-
%1$s bytes /
%2$s packets
+
+
+ Can\'t connect to always-on VPN
+
+
+ %1$s 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 %1$s.
+
+
+
+ %1$s 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.
+
+
+ " "
+
+ Change VPN settings
+
+
+ Configure
+
+ Disconnect
+
+ Open app
+
+ Dismiss
+
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 0000000000000..846fcf867e328
--- /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 2fe6648d82dac..01dca7e30e64b 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 56cff7c715d63..a44b18dc2d8c3 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 =
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 296cb76560af4..4f18da7056efa 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.net.NetworkInfo.DetailedState;
import android.net.UidRange;
import android.net.VpnService;
@@ -46,6 +47,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.R;
import com.android.internal.net.VpnConfig;
import org.mockito.Answers;
@@ -116,6 +118,9 @@ public class VpnTest extends AndroidTestCase {
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
.thenReturn(mNotificationManager);
+ when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
+ .thenReturn(Resources.getSystem().getString(
+ R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
// Used by {@link Notification.Builder}
ApplicationInfo applicationInfo = new ApplicationInfo();