Merge changes from topic "vpn-dialog"

* changes:
  Unbreak VPN unit tests.
  Add alert dialog when always-on VPN disconnects.
This commit is contained in:
Charles He
2017-09-19 07:56:44 +00:00
committed by Gerrit Code Review
10 changed files with 257 additions and 31 deletions

View File

@@ -2167,10 +2167,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>

View File

@@ -3445,16 +3445,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 -->

View File

@@ -2003,6 +2003,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" />
<java-symbol type="string" name="reset_retail_demo_mode_title" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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(

View File

@@ -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 =

View File

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