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();