Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Less intrusive VPN dialog and other UX tweaks.
Browse files Browse the repository at this point in the history
-The ability to launch VPNs is now sticky; once approved by the user,
further approvals are not needed UNLESS the connection is revoked in
Quick Settings.

-The old persistent notification has been removed in favor of the new
Quick Settings UI.

-The name of the VPN app is now pulled from the label of the VPN
service rather than the app itself, if one is set.

Bug: 12878887
Bug: 16578022
Change-Id: I102a14c05db26ee3aef030cda971e5165f078a91
  • Loading branch information
jpd236 committed Aug 20, 2014
1 parent 23ad279 commit 0554260
Show file tree
Hide file tree
Showing 22 changed files with 170 additions and 513 deletions.
31 changes: 22 additions & 9 deletions core/java/android/app/AppOpsManager.java
Expand Up @@ -16,26 +16,26 @@

package android.app;

import android.Manifest;
import android.annotation.SystemApi;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.media.AudioAttributes.AttributeUsage;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.ArrayMap;

import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;

/**
* API for interacting with "application operation" tracking.
*
Expand Down Expand Up @@ -203,8 +203,10 @@ public class AppOpsManager {
public static final int OP_TOAST_WINDOW = 45;
/** @hide Capture the device's display contents and/or audio */
public static final int OP_PROJECT_MEDIA = 46;
/** @hide Activate a VPN connection without user intervention. */
public static final int OP_ACTIVATE_VPN = 47;
/** @hide */
public static final int _NUM_OP = 47;
public static final int _NUM_OP = 48;

/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
Expand All @@ -218,6 +220,9 @@ public class AppOpsManager {
/** Continually monitoring location data with a relatively high power request. */
public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION
= "android:monitor_location_high_power";
/** Activate a VPN connection without user intervention. @hide */
@SystemApi
public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";

/**
* This maps each operation to the operation that serves as the
Expand Down Expand Up @@ -275,6 +280,7 @@ public class AppOpsManager {
OP_MUTE_MICROPHONE,
OP_TOAST_WINDOW,
OP_PROJECT_MEDIA,
OP_ACTIVATE_VPN,
};

/**
Expand Down Expand Up @@ -329,6 +335,7 @@ public class AppOpsManager {
null,
null,
null,
OPSTR_ACTIVATE_VPN,
};

/**
Expand Down Expand Up @@ -383,6 +390,7 @@ public class AppOpsManager {
"MUTE_MICROPHONE",
"TOAST_WINDOW",
"PROJECT_MEDIA",
"ACTIVATE_VPN",
};

/**
Expand Down Expand Up @@ -437,6 +445,7 @@ public class AppOpsManager {
null, // no permission for muting/unmuting microphone
null, // no permission for displaying toasts
null, // no permission for projecting media
null, // no permission for activating vpn
};

/**
Expand Down Expand Up @@ -492,6 +501,7 @@ public class AppOpsManager {
UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
null, //PROJECT_MEDIA
UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN
};

/**
Expand Down Expand Up @@ -546,6 +556,7 @@ public class AppOpsManager {
false, //MUTE_MICROPHONE
true, //TOAST_WINDOW
false, //PROJECT_MEDIA
false, //ACTIVATE_VPN
};

/**
Expand Down Expand Up @@ -599,6 +610,7 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
};

/**
Expand Down Expand Up @@ -656,6 +668,7 @@ public class AppOpsManager {
false,
false,
false,
false,
};

private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
Expand Down
2 changes: 2 additions & 0 deletions core/java/android/net/IConnectivityManager.aidl
Expand Up @@ -108,6 +108,8 @@ interface IConnectivityManager

boolean prepareVpn(String oldPackage, String newPackage);

void setVpnPackageAuthorization(boolean authorized);

ParcelFileDescriptor establishVpn(in VpnConfig config);

VpnConfig getVpnConfig();
Expand Down
3 changes: 0 additions & 3 deletions core/java/com/android/internal/net/LegacyVpnInfo.java
Expand Up @@ -40,7 +40,6 @@ public class LegacyVpnInfo implements Parcelable {

public String key;
public int state = -1;
public PendingIntent intent;

@Override
public int describeContents() {
Expand All @@ -51,7 +50,6 @@ public int describeContents() {
public void writeToParcel(Parcel out, int flags) {
out.writeString(key);
out.writeInt(state);
out.writeParcelable(intent, flags);
}

public static final Parcelable.Creator<LegacyVpnInfo> CREATOR =
Expand All @@ -61,7 +59,6 @@ public LegacyVpnInfo createFromParcel(Parcel in) {
LegacyVpnInfo info = new LegacyVpnInfo();
info.key = in.readString();
info.state = in.readInt();
info.intent = in.readParcelable(null);
return info;
}

Expand Down
29 changes: 19 additions & 10 deletions core/java/com/android/internal/net/VpnConfig.java
Expand Up @@ -20,17 +20,19 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.LinkAddress;
import android.net.RouteInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.net.RouteInfo;
import android.net.LinkAddress;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.List;
import java.util.ArrayList;
import java.util.List;

/**
* A simple container used to carry information in VpnBuilder, VpnDialogs,
Expand All @@ -55,12 +57,19 @@ public static Intent getIntentForConfirmation() {
return intent;
}

public static PendingIntent getIntentForStatusPanel(Context context) {
Intent intent = new Intent();
intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ManageDialog");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
return PendingIntent.getActivityAsUser(context, 0, intent, 0, null, UserHandle.CURRENT);
public static CharSequence getVpnLabel(Context context, String packageName)
throws NameNotFoundException {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(SERVICE_INTERFACE);
intent.setPackage(packageName);
List<ResolveInfo> services = pm.queryIntentServices(intent, 0 /* flags */);
if (services != null && services.size() == 1) {
// This app contains exactly one VPN service. Call loadLabel, which will attempt to
// load the service's label, and fall back to the app label if none is present.
return services.get(0).loadLabel(pm);
} else {
return pm.getApplicationInfo(packageName, 0).loadLabel(pm);
}
}

public String user;
Expand Down
4 changes: 2 additions & 2 deletions packages/SystemUI/res/values/strings.xml
Expand Up @@ -834,8 +834,8 @@
<!-- Monitoring dialog title for normal devices [CHAR LIMIT=35]-->
<string name="monitoring_title">Network monitoring</string>

<!-- Monitoring dialog open app button [CHAR LIMIT=30] -->
<string name="open_app">Open app</string>
<!-- Monitoring dialog disable vpn button [CHAR LIMIT=30] -->
<string name="disable_vpn">Disable VPN</string>

<!-- Monitoring dialog disconnect vpn button [CHAR LIMIT=30] -->
<string name="disconnect_vpn">Disconnect VPN</string>
Expand Down
8 changes: 2 additions & 6 deletions packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
Expand Up @@ -119,11 +119,7 @@ private void handleRefreshState() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
if (mSecurityController.isLegacyVpn()) {
mSecurityController.disconnectFromLegacyVpn();
} else {
mSecurityController.openVpnApp();
}
mSecurityController.disconnectFromVpn();
}
}

Expand All @@ -142,7 +138,7 @@ private String getNegativeButton() {
if (mSecurityController.isLegacyVpn()) {
return mContext.getString(R.string.disconnect_vpn);
} else {
return mContext.getString(R.string.open_app);
return mContext.getString(R.string.disable_vpn);
}
}

Expand Down
Expand Up @@ -23,8 +23,7 @@ public interface SecurityController {
String getVpnApp();
boolean isLegacyVpn();
String getLegacyVpnName();
void openVpnApp();
void disconnectFromLegacyVpn();
void disconnectFromVpn();

void addCallback(VpnCallback callback);
void removeCallback(VpnCallback callback);
Expand Down
Expand Up @@ -18,7 +18,6 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
Expand Down Expand Up @@ -109,18 +108,17 @@ public String getLegacyVpnName() {
}

@Override
public void openVpnApp() {
Intent i = mContext.getPackageManager().getLaunchIntentForPackage(mVpnConfig.user);
if (i != null) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(i);
}
}

@Override
public void disconnectFromLegacyVpn() {
public void disconnectFromVpn() {
try {
mConnectivityService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
if (isLegacyVpn()) {
mConnectivityService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
} else {
// Prevent this app from initiating VPN connections in the future without user
// intervention.
mConnectivityService.setVpnPackageAuthorization(false);

mConnectivityService.prepareVpn(mVpnConfig.user, VpnConfig.LEGACY_VPN);
}
} catch (Exception e) {
Log.e(TAG, "Unable to disconnect from VPN", e);
}
Expand Down Expand Up @@ -154,9 +152,7 @@ private void updateState() {
mIsVpnEnabled = mVpnConfig != null;

if (mVpnConfig != null && !mVpnConfig.legacy) {
ApplicationInfo info =
mContext.getPackageManager().getApplicationInfo(mVpnConfig.user, 0);
mVpnName = mContext.getPackageManager().getApplicationLabel(info).toString();
mVpnName = VpnConfig.getVpnLabel(mContext, mVpnConfig.user).toString();
}
} catch (RemoteException | NameNotFoundException e) {
Log.w(TAG, "Unable to get current VPN", e);
Expand Down
9 changes: 0 additions & 9 deletions packages/VpnDialogs/AndroidManifest.xml
Expand Up @@ -28,14 +28,5 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

<activity android:name=".ManageDialog"
android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 7 additions & 36 deletions packages/VpnDialogs/res/layout/confirm.xml
Expand Up @@ -18,41 +18,12 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout android:layout_width="match_parent"
<TextView android:id="@+id/warning"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="3mm">

<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">

<ImageView android:id="@+id/icon"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:paddingRight="1mm"/>

<TextView android:id="@+id/prompt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="18sp"/>
</LinearLayout>

<TextView android:id="@+id/warning"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="1mm"
android:paddingBottom="1mm"
android:text="@string/warning"
android:textSize="18sp"/>

<CheckBox android:id="@+id/check"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/accept"
android:textSize="20sp"
android:filterTouchesWhenObscured="true"
android:checked="false"/>
</LinearLayout>
android:textSize="18sp"
android:paddingTop="4mm"
android:paddingLeft="3mm"
android:paddingRight="3mm"
android:paddingBottom="4mm"/>
</ScrollView>

0 comments on commit 0554260

Please sign in to comment.