summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenedict Wong <benedictwong@google.com>2019-11-06 00:20:15 -0800
committerBenedict Wong <benedictwong@google.com>2020-02-03 19:05:23 -0800
commit418017e5f9a320b845cf9722fb4aa03d9710b091 (patch)
treeaf7fd2381ed933bf08c6469385cf91948dfc6da2
parent2d814e8ba88ee64e6969a478a640e0490606e370 (diff)
Add separate user consent for Platform VPNs
This change adds a new VPN user consent flow (using the same text) for granting the lesser OP_ACTIVATE_PLATFORM_VPN. A new PlatformVpnConfirmDialog is created as a subclass to preserve all logic, but ensure the right appop is granted for the relevant dialog. Intent extras were considered, but are inherently unsafe, since the caller may add any extras that they would want. Bug: 144246835 Test: FrameworksNetTests passing Change-Id: Ia6f36207d43c3748f938430c2780dcf29e5623f3
-rw-r--r--core/java/android/net/IConnectivityManager.aidl2
-rw-r--r--core/java/android/net/VpnManager.java17
-rw-r--r--core/java/android/net/VpnService.java2
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--packages/VpnDialogs/AndroidManifest.xml7
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java13
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java29
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java27
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java77
-rw-r--r--tests/net/java/android/net/VpnManagerTest.java11
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java50
12 files changed, 200 insertions, 42 deletions
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 6baa3832403f..1089a197ff59 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -117,7 +117,7 @@ interface IConnectivityManager
boolean prepareVpn(String oldPackage, String newPackage, int userId);
- void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
+ void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
ParcelFileDescriptor establishVpn(in VpnConfig config);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index e60cc81bf9d2..f19ba0f5ef51 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -18,6 +18,7 @@ package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -30,6 +31,8 @@ import android.os.RemoteException;
import com.android.internal.net.VpnProfile;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.GeneralSecurityException;
/**
@@ -47,6 +50,18 @@ import java.security.GeneralSecurityException;
* @see Ikev2VpnProfile
*/
public class VpnManager {
+ /** Type representing a lack of VPN @hide */
+ public static final int TYPE_VPN_NONE = -1;
+ /** VPN service type code @hide */
+ public static final int TYPE_VPN_SERVICE = 1;
+ /** Platform VPN type code @hide */
+ public static final int TYPE_VPN_PLATFORM = 2;
+
+ /** @hide */
+ @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VpnType {}
+
@NonNull private final Context mContext;
@NonNull private final IConnectivityManager mService;
@@ -54,7 +69,7 @@ public class VpnManager {
final Intent intent = new Intent();
final ComponentName componentName = ComponentName.unflattenFromString(
Resources.getSystem().getString(
- com.android.internal.R.string.config_customVpnConfirmDialogComponent));
+ com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
intent.setComponent(componentName);
return intent;
}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 4b804b097d59..63e510733907 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -234,7 +234,7 @@ public class VpnService extends Service {
if (!cm.prepareVpn(packageName, null, userId)) {
cm.prepareVpn(null, packageName, userId);
}
- cm.setVpnPackageAuthorization(packageName, userId, true);
+ cm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE);
} catch (RemoteException e) {
// ignore
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 52b92d2660da..1c9cccb6a1dd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2565,7 +2565,11 @@
<string name="config_usbResolverActivity" translatable="false"
>com.android.systemui/com.android.systemui.usb.UsbResolverActivity</string>
- <!-- Name of the dialog that is used to request the user's consent to VPN connection -->
+ <!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
+ <string name="config_platformVpnConfirmDialogComponent" translatable="false"
+ >com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
+
+ <!-- Name of the dialog that is used to request the user's consent for a VpnService VPN -->
<string name="config_customVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 21d1d3cf9c89..8f56164de688 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2143,6 +2143,7 @@
<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_platformVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
<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 e4de6259e07d..693ca52b4ed1 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -34,6 +34,13 @@
</intent-filter>
</activity>
+ <activity android:name=".PlatformVpnConfirmDialog"
+ android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ </activity>
+
<activity android:name=".ManageDialog"
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
android:noHistory="true"
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 48adb9ba3f63..e66f2cc17a7f 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -23,6 +23,7 @@ import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.IConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -43,10 +44,20 @@ public class ConfirmDialog extends AlertActivity
implements DialogInterface.OnClickListener, ImageGetter {
private static final String TAG = "VpnConfirm";
+ @VpnManager.VpnType private final int mVpnType;
+
private String mPackage;
private IConnectivityManager mService;
+ public ConfirmDialog() {
+ this(VpnManager.TYPE_VPN_SERVICE);
+ }
+
+ public ConfirmDialog(@VpnManager.VpnType int vpnType) {
+ mVpnType = vpnType;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -138,7 +149,7 @@ public class ConfirmDialog extends AlertActivity
if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true);
+ mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java
new file mode 100644
index 000000000000..e2e297caad14
--- /dev/null
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 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.net.VpnManager;
+
+/**
+ * PlatformVpnConfirmDialog is a minimal subclass for requesting user consent for platform VPN
+ * profiles.
+ */
+public class PlatformVpnConfirmDialog extends ConfirmDialog {
+ public PlatformVpnConfirmDialog() {
+ super(VpnManager.TYPE_VPN_PLATFORM);
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8c8a4e22d48a..a1a953f86505 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -109,6 +109,7 @@ import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.UidRange;
import android.net.Uri;
+import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
@@ -4317,7 +4318,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
- return vpn.prepare(oldPackage, newPackage, false);
+ return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
} else {
return false;
}
@@ -4325,26 +4326,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Set whether the VPN package has the ability to launch VPNs without user intervention.
- * This method is used by system-privileged apps.
- * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
- * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param packageName The package for which authorization state should change.
* @param userId User for whom {@code packageName} is installed.
* @param authorized {@code true} if this app should be able to start a VPN connection without
- * explicit user approval, {@code false} if not.
- *
+ * explicit user approval, {@code false} if not.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
* @hide
*/
@Override
- public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
- vpn.setPackageAuthorization(packageName, authorized);
+ vpn.setPackageAuthorization(packageName, vpnType);
}
}
}
@@ -7217,7 +7221,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) {
setAlwaysOnVpnPackage(userId, null, false, null);
- setVpnPackageAuthorization(alwaysOnPackage, userId, false);
+ setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
}
// Turn Always-on VPN off
@@ -7240,7 +7244,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
// Prevent this app (packagename = vpnConfig.user) from initiating
// VPN connections in the future without user intervention.
- setVpnPackageAuthorization(vpnConfig.user, userId, false);
+ setVpnPackageAuthorization(
+ vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 03795597f47a..89af5b58ff72 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -62,6 +62,7 @@ import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.VpnManager;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
@@ -519,8 +520,11 @@ public class Vpn {
}
if (packageName != null) {
- // Pre-authorize new always-on VPN package.
- if (!setPackageAuthorization(packageName, true)) {
+ // TODO: Give the minimum permission possible; if there is a Platform VPN profile, only
+ // grant ACTIVATE_PLATFORM_VPN.
+ // Pre-authorize new always-on VPN package. Grant the full ACTIVATE_VPN appop, allowing
+ // both VpnService and Platform VPNs.
+ if (!setPackageAuthorization(packageName, VpnManager.TYPE_VPN_SERVICE)) {
return false;
}
mAlwaysOn = true;
@@ -691,13 +695,12 @@ public class Vpn {
*
* @param oldPackage The package name of the old VPN application
* @param newPackage The package name of the new VPN application
- * @param isPlatformVpn Whether the package being prepared is using a platform VPN profile.
- * Preparing a platform VPN profile requires only the lesser ACTIVATE_PLATFORM_VPN appop.
+ * @param vpnType The type of VPN being prepared. One of {@link VpnManager.VpnType} Preparing a
+ * platform VPN profile requires only the lesser ACTIVATE_PLATFORM_VPN appop.
* @return true if the operation succeeded.
*/
- // TODO: Use an Intdef'd type to represent what kind of VPN the caller is preparing.
public synchronized boolean prepare(
- String oldPackage, String newPackage, boolean isPlatformVpn) {
+ String oldPackage, String newPackage, @VpnManager.VpnType int vpnType) {
if (oldPackage != null) {
// Stop an existing always-on VPN from being dethroned by other apps.
if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
@@ -709,13 +712,13 @@ public class Vpn {
// The package doesn't match. We return false (to obtain user consent) unless the
// user has already consented to that VPN package.
if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
- && isVpnPreConsented(mContext, oldPackage, isPlatformVpn)) {
+ && isVpnPreConsented(mContext, oldPackage, vpnType)) {
prepareInternal(oldPackage);
return true;
}
return false;
} else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
- && !isVpnPreConsented(mContext, oldPackage, isPlatformVpn)) {
+ && !isVpnPreConsented(mContext, oldPackage, vpnType)) {
// Currently prepared VPN is revoked, so unprepare it and return false.
prepareInternal(VpnConfig.LEGACY_VPN);
return false;
@@ -798,25 +801,49 @@ public class Vpn {
}
}
- /**
- * Set whether a package has the ability to launch VPNs without user intervention.
- */
- public boolean setPackageAuthorization(String packageName, boolean authorized) {
+ /** Set whether a package has the ability to launch VPNs without user intervention. */
+ public boolean setPackageAuthorization(String packageName, @VpnManager.VpnType int vpnType) {
// Check if the caller is authorized.
enforceControlPermissionOrInternalCaller();
- int uid = getAppUid(packageName, mUserHandle);
+ final int uid = getAppUid(packageName, mUserHandle);
if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
// Authorization for nonexistent packages (or fake ones) can't be updated.
return false;
}
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
- AppOpsManager appOps =
+ final int[] toChange;
+
+ // Clear all AppOps if the app is being unauthorized.
+ switch (vpnType) {
+ case VpnManager.TYPE_VPN_NONE:
+ toChange = new int[] {
+ AppOpsManager.OP_ACTIVATE_VPN, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN
+ };
+ break;
+ case VpnManager.TYPE_VPN_PLATFORM:
+ toChange = new int[] {AppOpsManager.OP_ACTIVATE_PLATFORM_VPN};
+ break;
+ case VpnManager.TYPE_VPN_SERVICE:
+ toChange = new int[] {AppOpsManager.OP_ACTIVATE_VPN};
+ break;
+ default:
+ Log.wtf(TAG, "Unrecognized VPN type while granting authorization");
+ return false;
+ }
+
+ final AppOpsManager appOpMgr =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
- authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+ for (final int appOp : toChange) {
+ appOpMgr.setMode(
+ appOp,
+ uid,
+ packageName,
+ vpnType == VpnManager.TYPE_VPN_NONE
+ ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED);
+ }
return true;
} catch (Exception e) {
Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
@@ -826,11 +853,15 @@ public class Vpn {
return false;
}
- private static boolean isVpnPreConsented(
- Context context, String packageName, boolean isPlatformVpn) {
- return isPlatformVpn
- ? isVpnProfilePreConsented(context, packageName)
- : isVpnServicePreConsented(context, packageName);
+ private static boolean isVpnPreConsented(Context context, String packageName, int vpnType) {
+ switch (vpnType) {
+ case VpnManager.TYPE_VPN_SERVICE:
+ return isVpnServicePreConsented(context, packageName);
+ case VpnManager.TYPE_VPN_PLATFORM:
+ return isVpnProfilePreConsented(context, packageName);
+ default:
+ return false;
+ }
}
private static boolean doesPackageHaveAppop(Context context, String packageName, int appop) {
@@ -2377,7 +2408,7 @@ public class Vpn {
checkNotNull(keyStore, "KeyStore missing");
// Prepare VPN for startup
- if (!prepare(packageName, null /* newPackage */, true /* isPlatformVpn */)) {
+ if (!prepare(packageName, null /* newPackage */, VpnManager.TYPE_VPN_PLATFORM)) {
throw new SecurityException("User consent not granted for package " + packageName);
}
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
index 97551c94e2ae..95a794235a2e 100644
--- a/tests/net/java/android/net/VpnManagerTest.java
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -16,6 +16,7 @@
package android.net;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
@@ -24,6 +25,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.Intent;
import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
@@ -78,7 +81,13 @@ public class VpnManagerTest {
when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
// Expect intent to be returned, as consent has not already been granted.
- assertNotNull(mVpnManager.provisionVpnProfile(profile));
+ final Intent intent = mVpnManager.provisionVpnProfile(profile);
+ assertNotNull(intent);
+
+ final ComponentName expectedComponentName =
+ ComponentName.unflattenFromString(
+ "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
+ assertEquals(expectedComponentName, intent.getComponent());
verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 084ec7330236..155c61f3f8c7 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -63,6 +63,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
import android.net.UidRange;
+import android.net.VpnManager;
import android.net.VpnService;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -471,12 +472,12 @@ public class VpnTest {
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
// When a new VPN package is set the rules should change to cover that package.
- vpn.prepare(null, PKGS[0], false /* isPlatformVpn */);
+ vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse.
- vpn.prepare(null, VpnConfig.LEGACY_VPN, false /* isPlatformVpn */);
+ vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
}
@@ -817,6 +818,51 @@ public class VpnTest {
eq(TEST_VPN_PKG));
}
+ @Test
+ public void testSetPackageAuthorizationVpnService() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationPlatformVpn() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationRevokeAuthorization() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ }
+
/**
* Mock some methods of vpn object.
*/