Merge changes I3242ad0e,I5daced20,Iefd284ae am: f1d78f17c2

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1580104

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ibc653e7fbd0969eede95a194e2a47cd7c7d79dad
This commit is contained in:
Cody Kesting
2021-02-17 18:28:43 +00:00
committed by Automerger Merge Worker
15 changed files with 581 additions and 112 deletions

View File

@@ -18,6 +18,7 @@ package android.net.vcn;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfig;
import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.net.vcn.VcnUnderlyingNetworkPolicy;
@@ -33,4 +34,7 @@ interface IVcnManagementService {
void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp); VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp);
void registerVcnStatusCallback(in ParcelUuid subscriptionGroup, in IVcnStatusCallback callback, in String opPkgName);
void unregisterVcnStatusCallback(in IVcnStatusCallback callback);
} }

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2021, 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 android.net.vcn;
/** @hide */
interface IVcnStatusCallback {
void onEnteredSafeMode();
}

View File

@@ -23,6 +23,7 @@ import android.annotation.SystemService;
import android.content.Context; import android.content.Context;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.os.Binder;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceSpecificException; import android.os.ServiceSpecificException;
@@ -261,6 +262,100 @@ public class VcnManager {
} }
} }
// TODO: make VcnStatusCallback @SystemApi
/**
* VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs.
*
* <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a
* subscription group.
*
* @hide
*/
public abstract static class VcnStatusCallback {
private VcnStatusCallbackBinder mCbBinder;
/**
* Invoked when the VCN for this Callback's subscription group enters safe mode.
*
* <p>A VCN will be put into safe mode if any of the gateway connections were unable to
* establish a connection within a system-determined timeout (while underlying networks were
* available).
*
* <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration
* via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
*/
public abstract void onEnteredSafeMode();
}
/**
* Registers the given callback to receive status updates for the specified subscription.
*
* <p>Callbacks can be registered for a subscription before {@link VcnConfig}s are set for it.
*
* <p>A {@link VcnStatusCallback} may only be registered for one subscription at a time. {@link
* VcnStatusCallback}s may be reused once unregistered.
*
* <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier
* privileges for the specified subscription at the time of invocation.
*
* @param subscriptionGroup The subscription group to match for callbacks
* @param executor The {@link Executor} to be used for invoking callbacks
* @param callback The VcnStatusCallback to be registered
* @throws IllegalStateException if callback is currently registered with VcnManager
* @hide
*/
public void registerVcnStatusCallback(
@NonNull ParcelUuid subscriptionGroup,
@NonNull Executor executor,
@NonNull VcnStatusCallback callback) {
requireNonNull(subscriptionGroup, "subscriptionGroup must not be null");
requireNonNull(executor, "executor must not be null");
requireNonNull(callback, "callback must not be null");
synchronized (callback) {
if (callback.mCbBinder != null) {
throw new IllegalStateException("callback is already registered with VcnManager");
}
callback.mCbBinder = new VcnStatusCallbackBinder(executor, callback);
try {
mService.registerVcnStatusCallback(
subscriptionGroup, callback.mCbBinder, mContext.getOpPackageName());
} catch (RemoteException e) {
callback.mCbBinder = null;
throw e.rethrowFromSystemServer();
}
}
}
/**
* Unregisters the given callback.
*
* <p>Once unregistered, the callback will stop receiving status updates for the subscription it
* was registered with.
*
* @param callback The callback to be unregistered
* @hide
*/
public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) {
requireNonNull(callback, "callback must not be null");
synchronized (callback) {
if (callback.mCbBinder == null) {
// no Binder attached to this callback, so it's not currently registered
return;
}
try {
mService.unregisterVcnStatusCallback(callback.mCbBinder);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
callback.mCbBinder = null;
}
}
}
/** /**
* Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
* Server. * Server.
@@ -280,7 +375,30 @@ public class VcnManager {
@Override @Override
public void onPolicyChanged() { public void onPolicyChanged() {
mExecutor.execute(() -> mListener.onPolicyChanged()); Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> mListener.onPolicyChanged()));
}
}
/**
* Binder wrapper for VcnStatusCallbacks to receive signals from VcnManagementService.
*
* @hide
*/
private class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub {
@NonNull private final Executor mExecutor;
@NonNull private final VcnStatusCallback mCallback;
private VcnStatusCallbackBinder(
@NonNull Executor executor, @NonNull VcnStatusCallback callback) {
mExecutor = executor;
mCallback = callback;
}
@Override
public void onEnteredSafeMode() {
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
} }
} }
} }

View File

@@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@@ -29,6 +30,7 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfig;
import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.net.vcn.VcnUnderlyingNetworkPolicy;
@@ -54,6 +56,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.LocationPermissionChecker;
import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn; import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnContext;
@@ -124,6 +127,7 @@ import java.util.concurrent.TimeUnit;
* *
* @hide * @hide
*/ */
// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
public class VcnManagementService extends IVcnManagementService.Stub { public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); @NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
@@ -147,6 +151,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
@NonNull private final VcnContext mVcnContext; @NonNull private final VcnContext mVcnContext;
/** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
@Nullable private LocationPermissionChecker mLocationPermissionChecker;
@GuardedBy("mLock") @GuardedBy("mLock")
@NonNull @NonNull
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>(); private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
@@ -169,6 +176,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners = private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners =
new ArrayMap<>(); new ArrayMap<>();
@GuardedBy("mLock")
@NonNull
private final Map<IBinder, VcnStatusCallbackInfo> mRegisteredStatusCallbacks = new ArrayMap<>();
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
mContext = requireNonNull(context, "Missing context"); mContext = requireNonNull(context, "Missing context");
@@ -293,8 +304,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config, @NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot, @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnSafemodeCallback safemodeCallback) { @NonNull VcnSafeModeCallback safeModeCallback) {
return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback); return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback);
} }
/** Gets the subId indicated by the given {@link WifiInfo}. */ /** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -302,6 +313,11 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
return SubscriptionManager.INVALID_SUBSCRIPTION_ID; return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
} }
/** Creates a new LocationPermissionChecker for the provided Context. */
public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) {
return new LocationPermissionChecker(context);
}
} }
/** Notifies the VcnManagementService that external dependencies can be set up. */ /** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -309,6 +325,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
mContext.getSystemService(ConnectivityManager.class) mContext.getSystemService(ConnectivityManager.class)
.registerNetworkProvider(mNetworkProvider); .registerNetworkProvider(mNetworkProvider);
mTelephonySubscriptionTracker.register(); mTelephonySubscriptionTracker.register();
mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
} }
private void enforcePrimaryUser() { private void enforcePrimaryUser() {
@@ -440,12 +457,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN. // VCN.
final VcnSafemodeCallbackImpl safemodeCallback = final VcnSafeModeCallbackImpl safeModeCallback =
new VcnSafemodeCallbackImpl(subscriptionGroup); new VcnSafeModeCallbackImpl(subscriptionGroup);
final Vcn newInstance = final Vcn newInstance =
mDeps.newVcn( mDeps.newVcn(
mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback); mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback);
mVcns.put(subscriptionGroup, newInstance); mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their // Now that a new VCN has started, notify all registered listeners to refresh their
@@ -551,6 +568,14 @@ public class VcnManagementService extends IVcnManagementService.Stub {
} }
} }
/** Get current VcnStatusCallbacks for testing purposes. */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<IBinder, VcnStatusCallbackInfo> getAllStatusCallbacks() {
synchronized (mLock) {
return Collections.unmodifiableMap(mRegisteredStatusCallbacks);
}
}
/** Binder death recipient used to remove a registered policy listener. */ /** Binder death recipient used to remove a registered policy listener. */
private class PolicyListenerBinderDeath implements Binder.DeathRecipient { private class PolicyListenerBinderDeath implements Binder.DeathRecipient {
@NonNull private final IVcnUnderlyingNetworkPolicyListener mListener; @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener;
@@ -672,22 +697,109 @@ public class VcnManagementService extends IVcnManagementService.Stub {
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
} }
/** Callback for signalling when a Vcn has entered Safemode. */ /** Binder death recipient used to remove registered VcnStatusCallbacks. */
public interface VcnSafemodeCallback { @VisibleForTesting(visibility = Visibility.PRIVATE)
/** Called by a Vcn to signal that it has entered Safemode. */ class VcnStatusCallbackInfo implements Binder.DeathRecipient {
void onEnteredSafemode(); @NonNull final ParcelUuid mSubGroup;
@NonNull final IVcnStatusCallback mCallback;
@NonNull final String mPkgName;
final int mUid;
private VcnStatusCallbackInfo(
@NonNull ParcelUuid subGroup,
@NonNull IVcnStatusCallback callback,
@NonNull String pkgName,
int uid) {
mSubGroup = subGroup;
mCallback = callback;
mPkgName = pkgName;
mUid = uid;
}
@Override
public void binderDied() {
Log.e(TAG, "app died without unregistering VcnStatusCallback");
unregisterVcnStatusCallback(mCallback);
}
} }
/** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */ /** Registers the provided callback for receiving VCN status updates. */
private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback { @Override
public void registerVcnStatusCallback(
@NonNull ParcelUuid subGroup,
@NonNull IVcnStatusCallback callback,
@NonNull String opPkgName) {
final int callingUid = mDeps.getBinderCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
requireNonNull(subGroup, "subGroup must not be null");
requireNonNull(callback, "callback must not be null");
requireNonNull(opPkgName, "opPkgName must not be null");
mContext.getSystemService(AppOpsManager.class).checkPackage(callingUid, opPkgName);
final IBinder cbBinder = callback.asBinder();
final VcnStatusCallbackInfo cbInfo =
new VcnStatusCallbackInfo(
subGroup, callback, opPkgName, mDeps.getBinderCallingUid());
try {
cbBinder.linkToDeath(cbInfo, 0 /* flags */);
} catch (RemoteException e) {
// Remote binder already died - don't add to mRegisteredStatusCallbacks and exit
return;
}
synchronized (mLock) {
if (mRegisteredStatusCallbacks.containsKey(cbBinder)) {
throw new IllegalStateException(
"Attempting to register a callback that is already in use");
}
mRegisteredStatusCallbacks.put(cbBinder, cbInfo);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/** Unregisters the provided callback from receiving future VCN status updates. */
@Override
public void unregisterVcnStatusCallback(@NonNull IVcnStatusCallback callback) {
final long identity = Binder.clearCallingIdentity();
try {
requireNonNull(callback, "callback must not be null");
final IBinder cbBinder = callback.asBinder();
synchronized (mLock) {
VcnStatusCallbackInfo cbInfo = mRegisteredStatusCallbacks.remove(cbBinder);
if (cbInfo != null) {
cbBinder.unlinkToDeath(cbInfo, 0 /* flags */);
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
// TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
/** Callback for signalling when a Vcn has entered safe mode. */
public interface VcnSafeModeCallback {
/** Called by a Vcn to signal that it has entered safe mode. */
void onEnteredSafeMode();
}
/** VcnSafeModeCallback is used by Vcns to notify VcnManagementService on entering safe mode. */
private class VcnSafeModeCallbackImpl implements VcnSafeModeCallback {
@NonNull private final ParcelUuid mSubGroup; @NonNull private final ParcelUuid mSubGroup;
private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) { private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) {
mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup"); mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
} }
@Override @Override
public void onEnteredSafemode() { public void onEnteredSafeMode() {
synchronized (mLock) { synchronized (mLock) {
// Ignore if this subscription group doesn't exist anymore // Ignore if this subscription group doesn't exist anymore
if (!mVcns.containsKey(mSubGroup)) { if (!mVcns.containsKey(mSubGroup)) {
@@ -695,6 +807,27 @@ public class VcnManagementService extends IVcnManagementService.Stub {
} }
notifyAllPolicyListenersLocked(); notifyAllPolicyListenersLocked();
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (!mSubGroup.equals(cbInfo.mSubGroup)) {
continue;
}
if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
mSubGroup, cbInfo.mPkgName)) {
continue;
}
if (!mLocationPermissionChecker.checkLocationPermission(
cbInfo.mPkgName,
"VcnStatusCallback" /* featureId */,
cbInfo.mUid,
null /* message */)) {
continue;
}
Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
}
} }
} }
} }

View File

@@ -30,7 +30,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections; import java.util.Collections;
@@ -86,18 +86,18 @@ public class Vcn extends Handler {
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
/** /**
* Causes this VCN to immediately enter Safemode. * Causes this VCN to immediately enter safe mode.
* *
* <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its
* VcnGatewayConnections, and notify VcnManagementService that it is in Safemode. * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode.
*/ */
private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1; private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1;
@NonNull private final VcnContext mVcnContext; @NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps; @NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener; @NonNull private final VcnNetworkRequestListener mRequestListener;
@NonNull private final VcnSafemodeCallback mVcnSafemodeCallback; @NonNull private final VcnSafeModeCallback mVcnSafeModeCallback;
@NonNull @NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -125,13 +125,13 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config, @NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot, @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnSafemodeCallback vcnSafemodeCallback) { @NonNull VcnSafeModeCallback vcnSafeModeCallback) {
this( this(
vcnContext, vcnContext,
subscriptionGroup, subscriptionGroup,
config, config,
snapshot, snapshot,
vcnSafemodeCallback, vcnSafeModeCallback,
new Dependencies()); new Dependencies());
} }
@@ -141,13 +141,13 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config, @NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot, @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnSafemodeCallback vcnSafemodeCallback, @NonNull VcnSafeModeCallback vcnSafeModeCallback,
@NonNull Dependencies deps) { @NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext; mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mVcnSafemodeCallback = mVcnSafeModeCallback =
Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback"); Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps"); mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener(); mRequestListener = new VcnNetworkRequestListener();
@@ -216,8 +216,8 @@ public class Vcn extends Handler {
case MSG_CMD_TEARDOWN: case MSG_CMD_TEARDOWN:
handleTeardown(); handleTeardown();
break; break;
case MSG_CMD_ENTER_SAFEMODE: case MSG_CMD_ENTER_SAFE_MODE:
handleEnterSafemode(); handleEnterSafeMode();
break; break;
default: default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what); Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
@@ -243,10 +243,10 @@ public class Vcn extends Handler {
mIsActive.set(false); mIsActive.set(false);
} }
private void handleEnterSafemode() { private void handleEnterSafeMode() {
handleTeardown(); handleTeardown();
mVcnSafemodeCallback.onEnteredSafemode(); mVcnSafeModeCallback.onEnteredSafeMode();
} }
private void handleNetworkRequested( private void handleNetworkRequested(
@@ -335,14 +335,14 @@ public class Vcn extends Handler {
/** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
@VisibleForTesting(visibility = Visibility.PACKAGE) @VisibleForTesting(visibility = Visibility.PACKAGE)
public interface VcnGatewayStatusCallback { public interface VcnGatewayStatusCallback {
/** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */ /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
void onEnteredSafemode(); void onEnteredSafeMode();
} }
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback { private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
@Override @Override
public void onEnteredSafemode() { public void onEnteredSafeMode() {
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE)); sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
} }
} }

View File

@@ -418,13 +418,13 @@ public class VcnGatewayConnection extends StateMachine {
private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9; private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
/** /**
* Sent when this VcnGatewayConnection has entered Safemode. * Sent when this VcnGatewayConnection has entered safe mode.
* *
* <p>A VcnGatewayConnection enters Safemode when it takes over {@link * <p>A VcnGatewayConnection enters safe mode when it takes over {@link
* #SAFEMODE_TIMEOUT_SECONDS} to enter {@link ConnectedState}. * #SAFEMODE_TIMEOUT_SECONDS} to enter {@link ConnectedState}.
* *
* <p>When a VcnGatewayConnection enters safe mode, it will fire {@link * <p>When a VcnGatewayConnection enters safe mode, it will fire {@link
* VcnGatewayStatusCallback#onEnteredSafemode()} to notify its Vcn. The Vcn will then shut down * VcnGatewayStatusCallback#onEnteredSafeMode()} to notify its Vcn. The Vcn will then shut down
* its VcnGatewayConnectin(s). * its VcnGatewayConnectin(s).
* *
* <p>Relevant in DisconnectingState, ConnectingState, ConnectedState (if the Vcn Network is not * <p>Relevant in DisconnectingState, ConnectingState, ConnectedState (if the Vcn Network is not
@@ -432,7 +432,7 @@ public class VcnGatewayConnection extends StateMachine {
* *
* @param arg1 The "all" token; this signal is always honored. * @param arg1 The "all" token; this signal is always honored.
*/ */
private static final int EVENT_SAFEMODE_TIMEOUT_EXCEEDED = 10; private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10;
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull @NonNull
@@ -551,7 +551,7 @@ public class VcnGatewayConnection extends StateMachine {
@Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mTeardownTimeoutAlarm;
@Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm;
@Nullable private WakeupMessage mRetryTimeoutAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm;
@Nullable private WakeupMessage mSafemodeTimeoutAlarm; @Nullable private WakeupMessage mSafeModeTimeoutAlarm;
public VcnGatewayConnection( public VcnGatewayConnection(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@@ -638,7 +638,7 @@ public class VcnGatewayConnection extends StateMachine {
cancelTeardownTimeoutAlarm(); cancelTeardownTimeoutAlarm();
cancelDisconnectRequestAlarm(); cancelDisconnectRequestAlarm();
cancelRetryTimeoutAlarm(); cancelRetryTimeoutAlarm();
cancelSafemodeAlarm(); cancelSafeModeAlarm();
mUnderlyingNetworkTracker.teardown(); mUnderlyingNetworkTracker.teardown();
} }
@@ -928,27 +928,27 @@ public class VcnGatewayConnection extends StateMachine {
} }
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
void setSafemodeAlarm() { void setSafeModeAlarm() {
// Only schedule a NEW alarm if none is already set. // Only schedule a NEW alarm if none is already set.
if (mSafemodeTimeoutAlarm != null) { if (mSafeModeTimeoutAlarm != null) {
return; return;
} }
final Message delayedMessage = obtainMessage(EVENT_SAFEMODE_TIMEOUT_EXCEEDED, TOKEN_ALL); final Message delayedMessage = obtainMessage(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED, TOKEN_ALL);
mSafemodeTimeoutAlarm = mSafeModeTimeoutAlarm =
createScheduledAlarm( createScheduledAlarm(
SAFEMODE_TIMEOUT_ALARM, SAFEMODE_TIMEOUT_ALARM,
delayedMessage, delayedMessage,
TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
} }
private void cancelSafemodeAlarm() { private void cancelSafeModeAlarm() {
if (mSafemodeTimeoutAlarm != null) { if (mSafeModeTimeoutAlarm != null) {
mSafemodeTimeoutAlarm.cancel(); mSafeModeTimeoutAlarm.cancel();
mSafemodeTimeoutAlarm = null; mSafeModeTimeoutAlarm = null;
} }
removeEqualMessages(EVENT_SAFEMODE_TIMEOUT_EXCEEDED); removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED);
} }
private void sessionLost(int token, @Nullable Exception exception) { private void sessionLost(int token, @Nullable Exception exception) {
@@ -1125,7 +1125,7 @@ public class VcnGatewayConnection extends StateMachine {
Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
} }
cancelSafemodeAlarm(); cancelSafeModeAlarm();
} }
@Override @Override
@@ -1153,7 +1153,7 @@ public class VcnGatewayConnection extends StateMachine {
@Override @Override
protected void exitState() { protected void exitState() {
// Safe to blindly set up, as it is cancelled and cleared on entering this state // Safe to blindly set up, as it is cancelled and cleared on entering this state
setSafemodeAlarm(); setSafeModeAlarm();
} }
} }
@@ -1245,9 +1245,9 @@ public class VcnGatewayConnection extends StateMachine {
transitionTo(mDisconnectedState); transitionTo(mDisconnectedState);
} }
break; break;
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafemode(); mGatewayStatusCallback.onEnteredSafeMode();
mSafemodeTimeoutAlarm = null; mSafeModeTimeoutAlarm = null;
break; break;
default: default:
logUnhandledMessage(msg); logUnhandledMessage(msg);
@@ -1331,9 +1331,9 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
break; break;
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafemode(); mGatewayStatusCallback.onEnteredSafeMode();
mSafemodeTimeoutAlarm = null; mSafeModeTimeoutAlarm = null;
break; break;
default: default:
logUnhandledMessage(msg); logUnhandledMessage(msg);
@@ -1399,7 +1399,7 @@ public class VcnGatewayConnection extends StateMachine {
// Validated connection, clear failed attempt counter // Validated connection, clear failed attempt counter
mFailedAttempts = 0; mFailedAttempts = 0;
cancelSafemodeAlarm(); cancelSafeModeAlarm();
} }
protected void applyTransform( protected void applyTransform(
@@ -1517,9 +1517,9 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
break; break;
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafemode(); mGatewayStatusCallback.onEnteredSafeMode();
mSafemodeTimeoutAlarm = null; mSafeModeTimeoutAlarm = null;
break; break;
default: default:
logUnhandledMessage(msg); logUnhandledMessage(msg);
@@ -1575,7 +1575,7 @@ public class VcnGatewayConnection extends StateMachine {
protected void exitState() { protected void exitState() {
// Attempt to set the safe mode alarm - this requires the Vcn Network being validated // Attempt to set the safe mode alarm - this requires the Vcn Network being validated
// while in ConnectedState (which cancels the previous alarm) // while in ConnectedState (which cancels the previous alarm)
setSafemodeAlarm(); setSafeModeAlarm();
} }
} }
@@ -1623,9 +1623,9 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_DISCONNECT_REQUESTED: case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
break; break;
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED: case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafemode(); mGatewayStatusCallback.onEnteredSafeMode();
mSafemodeTimeoutAlarm = null; mSafeModeTimeoutAlarm = null;
break; break;
default: default:
logUnhandledMessage(msg); logUnhandledMessage(msg);

View File

@@ -26,24 +26,30 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager.VcnStatusCallback;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
import android.os.ParcelUuid;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.util.UUID;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
public class VcnManagerTest { public class VcnManagerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final Executor INLINE_EXECUTOR = Runnable::run; private static final Executor INLINE_EXECUTOR = Runnable::run;
private IVcnManagementService mMockVcnManagementService; private IVcnManagementService mMockVcnManagementService;
private VcnUnderlyingNetworkPolicyListener mMockPolicyListener; private VcnUnderlyingNetworkPolicyListener mMockPolicyListener;
private VcnStatusCallback mMockStatusCallback;
private Context mContext; private Context mContext;
private VcnManager mVcnManager; private VcnManager mVcnManager;
@@ -52,6 +58,7 @@ public class VcnManagerTest {
public void setUp() { public void setUp() {
mMockVcnManagementService = mock(IVcnManagementService.class); mMockVcnManagementService = mock(IVcnManagementService.class);
mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class); mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class);
mMockStatusCallback = mock(VcnStatusCallback.class);
mContext = getContext(); mContext = getContext();
mVcnManager = new VcnManager(mContext, mMockVcnManagementService); mVcnManager = new VcnManager(mContext, mMockVcnManagementService);
@@ -132,4 +139,60 @@ public class VcnManagerTest {
public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception { public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception {
mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null); mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null);
} }
@Test
public void testRegisterVcnStatusCallback() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
ArgumentCaptor<IVcnStatusCallback> captor =
ArgumentCaptor.forClass(IVcnStatusCallback.class);
verify(mMockVcnManagementService)
.registerVcnStatusCallback(eq(SUB_GROUP), captor.capture(), any());
IVcnStatusCallback callbackWrapper = captor.getValue();
callbackWrapper.onEnteredSafeMode();
verify(mMockStatusCallback).onEnteredSafeMode();
}
@Test(expected = IllegalStateException.class)
public void testRegisterVcnStatusCallbackAlreadyRegistered() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
}
@Test(expected = NullPointerException.class)
public void testRegisterVcnStatusCallbackNullSubscriptionGroup() throws Exception {
mVcnManager.registerVcnStatusCallback(null, INLINE_EXECUTOR, mMockStatusCallback);
}
@Test(expected = NullPointerException.class)
public void testRegisterVcnStatusCallbackNullExecutor() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, null, mMockStatusCallback);
}
@Test(expected = NullPointerException.class)
public void testRegisterVcnStatusCallbackNullCallback() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, null);
}
@Test
public void testUnregisterVcnStatusCallback() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
mVcnManager.unregisterVcnStatusCallback(mMockStatusCallback);
verify(mMockVcnManagementService).unregisterVcnStatusCallback(any());
}
@Test
public void testUnregisterUnknownVcnStatusCallback() throws Exception {
mVcnManager.unregisterVcnStatusCallback(mMockStatusCallback);
verifyNoMoreInteractions(mMockVcnManagementService);
}
@Test(expected = NullPointerException.class)
public void testUnregisterNullVcnStatusCallback() throws Exception {
mVcnManager.unregisterVcnStatusCallback(null);
}
} }

View File

@@ -26,6 +26,7 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@@ -42,9 +43,11 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@@ -52,6 +55,7 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities.Transport; import android.net.NetworkCapabilities.Transport;
import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest; import android.net.vcn.VcnConfigTest;
@@ -70,7 +74,9 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.internal.util.LocationPermissionChecker;
import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn; import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnContext;
@@ -147,14 +153,17 @@ public class VcnManagementServiceTest {
mock(PersistableBundleUtils.LockingReadWriteHelper.class); mock(PersistableBundleUtils.LockingReadWriteHelper.class);
private final TelephonySubscriptionTracker mSubscriptionTracker = private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class); mock(TelephonySubscriptionTracker.class);
private final LocationPermissionChecker mLocationPermissionChecker =
mock(LocationPermissionChecker.class);
private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor = private final ArgumentCaptor<VcnSafeModeCallback> mSafeModeCallbackCaptor =
ArgumentCaptor.forClass(VcnSafemodeCallback.class); ArgumentCaptor.forClass(VcnSafeModeCallback.class);
private final VcnManagementService mVcnMgmtSvc; private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener = private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
mock(IVcnUnderlyingNetworkPolicyListener.class); mock(IVcnUnderlyingNetworkPolicyListener.class);
private final IVcnStatusCallback mMockStatusCallback = mock(IVcnStatusCallback.class);
private final IBinder mMockIBinder = mock(IBinder.class); private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception { public VcnManagementServiceTest() throws Exception {
@@ -171,6 +180,7 @@ public class VcnManagementServiceTest {
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName(); doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
doReturn(mMockContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper(); doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid(); doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid();
doReturn(mVcnContext) doReturn(mVcnContext)
@@ -188,6 +198,9 @@ public class VcnManagementServiceTest {
doReturn(mConfigReadWriteHelper) doReturn(mConfigReadWriteHelper)
.when(mMockDeps) .when(mMockDeps)
.newPersistableBundleLockingReadWriteHelper(any()); .newPersistableBundleLockingReadWriteHelper(any());
doReturn(mLocationPermissionChecker)
.when(mMockDeps)
.newLocationPermissionChecker(eq(mMockContext));
// Setup VCN instance generation // Setup VCN instance generation
doAnswer((invocation) -> { doAnswer((invocation) -> {
@@ -206,6 +219,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
doReturn(mMockIBinder).when(mMockPolicyListener).asBinder(); doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
doReturn(mMockIBinder).when(mMockStatusCallback).asBinder();
// Make sure the profiles are loaded. // Make sure the profiles are loaded.
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
@@ -707,24 +721,138 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged(); verify(mMockPolicyListener).onPolicyChanged();
} }
@Test private void verifyVcnSafeModeCallback(
public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception { @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
TelephonySubscriptionSnapshot snapshot = throws Exception {
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps) verify(mMockDeps)
.newVcn( .newVcn(
eq(mVcnContext), eq(mVcnContext),
eq(TEST_UUID_1), eq(subGroup),
eq(TEST_VCN_CONFIG), eq(TEST_VCN_CONFIG),
eq(snapshot), eq(snapshot),
mSafemodeCallbackCaptor.capture()); mSafeModeCallbackCaptor.capture());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue(); VcnSafeModeCallback safeModeCallback = mSafeModeCallbackCaptor.getValue();
safemodeCallback.onEnteredSafemode(); safeModeCallback.onEnteredSafeMode();
assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
verify(mMockPolicyListener).onPolicyChanged(); verify(mMockPolicyListener).onPolicyChanged();
} }
@Test
public void testVcnSafeModeCallbackOnEnteredSafeMode() throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verifyVcnSafeModeCallback(TEST_UUID_1, snapshot);
}
private void triggerVcnStatusCallbackOnEnteredSafeMode(
@NonNull ParcelUuid subGroup,
@NonNull String pkgName,
int uid,
boolean hasPermissionsforSubGroup,
boolean hasLocationPermission)
throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));
doReturn(hasPermissionsforSubGroup)
.when(snapshot)
.packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));
doReturn(hasLocationPermission)
.when(mLocationPermissionChecker)
.checkLocationPermission(eq(pkgName), any(), eq(uid), any());
mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
// Trigger systemReady() to set up LocationPermissionChecker
mVcnMgmtSvc.systemReady();
verifyVcnSafeModeCallback(subGroup, snapshot);
}
@Test
public void testVcnStatusCallbackOnEnteredSafeModeWithCarrierPrivileges() throws Exception {
triggerVcnStatusCallbackOnEnteredSafeMode(
TEST_UUID_1,
TEST_PACKAGE_NAME,
TEST_UID,
true /* hasPermissionsforSubGroup */,
true /* hasLocationPermission */);
verify(mMockStatusCallback, times(1)).onEnteredSafeMode();
}
@Test
public void testVcnStatusCallbackOnEnteredSafeModeWithoutCarrierPrivileges() throws Exception {
triggerVcnStatusCallbackOnEnteredSafeMode(
TEST_UUID_1,
TEST_PACKAGE_NAME,
TEST_UID,
false /* hasPermissionsforSubGroup */,
true /* hasLocationPermission */);
verify(mMockStatusCallback, never()).onEnteredSafeMode();
}
@Test
public void testVcnStatusCallbackOnEnteredSafeModeWithoutLocationPermission() throws Exception {
triggerVcnStatusCallbackOnEnteredSafeMode(
TEST_UUID_1,
TEST_PACKAGE_NAME,
TEST_UID,
true /* hasPermissionsforSubGroup */,
false /* hasLocationPermission */);
verify(mMockStatusCallback, never()).onEnteredSafeMode();
}
@Test
public void testRegisterVcnStatusCallback() throws Exception {
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
Map<IBinder, VcnStatusCallbackInfo> callbacks = mVcnMgmtSvc.getAllStatusCallbacks();
VcnStatusCallbackInfo cbInfo = callbacks.get(mMockIBinder);
assertNotNull(cbInfo);
assertEquals(TEST_UUID_1, cbInfo.mSubGroup);
assertEquals(mMockStatusCallback, cbInfo.mCallback);
assertEquals(TEST_PACKAGE_NAME, cbInfo.mPkgName);
assertEquals(TEST_UID, cbInfo.mUid);
verify(mMockIBinder).linkToDeath(eq(cbInfo), anyInt());
}
@Test(expected = IllegalStateException.class)
public void testRegisterVcnStatusCallbackDuplicate() {
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
}
@Test
public void testUnregisterVcnStatusCallback() {
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
Map<IBinder, VcnStatusCallbackInfo> callbacks = mVcnMgmtSvc.getAllStatusCallbacks();
VcnStatusCallbackInfo cbInfo = callbacks.get(mMockIBinder);
mVcnMgmtSvc.unregisterVcnStatusCallback(mMockStatusCallback);
assertTrue(mVcnMgmtSvc.getAllStatusCallbacks().isEmpty());
verify(mMockIBinder).unlinkToDeath(eq(cbInfo), anyInt());
}
@Test(expected = SecurityException.class)
public void testRegisterVcnStatusCallbackInvalidPackage() {
doThrow(new SecurityException()).when(mAppOpsMgr).checkPackage(TEST_UID, TEST_PACKAGE_NAME);
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_1, mMockStatusCallback, TEST_PACKAGE_NAME);
}
@Test
public void testUnregisterVcnStatusCallbackNeverRegistered() {
mVcnMgmtSvc.unregisterVcnStatusCallback(mMockStatusCallback);
assertTrue(mVcnMgmtSvc.getAllStatusCallbacks().isEmpty());
}
} }

View File

@@ -75,8 +75,8 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
} }
@Test @Test
public void testEnterStateDoesNotCancelSafemodeAlarm() { public void testEnterStateDoesNotCancelSafeModeAlarm() {
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
} }
@Test @Test
@@ -144,7 +144,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
@Test @Test
public void testChildOpenedRegistersNetwork() throws Exception { public void testChildOpenedRegistersNetwork() throws Exception {
// Verify scheduled but not canceled when entering ConnectedState // Verify scheduled but not canceled when entering ConnectedState
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
final VcnChildSessionConfiguration mMockChildSessionConfig = final VcnChildSessionConfiguration mMockChildSessionConfig =
mock(VcnChildSessionConfiguration.class); mock(VcnChildSessionConfiguration.class);
@@ -188,17 +188,17 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
assertTrue(nc.hasCapability(cap)); assertTrue(nc.hasCapability(cap));
} }
// Now that Vcn Network is up, notify it as validated and verify the Safemode alarm is // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is
// canceled // canceled
mGatewayConnection.mNetworkAgent.onValidationStatus( mGatewayConnection.mNetworkAgent.onValidationStatus(
NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */); NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */);
verify(mSafemodeTimeoutAlarm).cancel(); verify(mSafeModeTimeoutAlarm).cancel();
} }
@Test @Test
public void testChildSessionClosedTriggersDisconnect() throws Exception { public void testChildSessionClosedTriggersDisconnect() throws Exception {
// Verify scheduled but not canceled when entering ConnectedState // Verify scheduled but not canceled when entering ConnectedState
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
getChildSessionCallback().onClosed(); getChildSessionCallback().onClosed();
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
@@ -206,14 +206,14 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
// Since network never validated, verify mSafemodeTimeoutAlarm not canceled // Since network never validated, verify mSafeModeTimeoutAlarm not canceled
verifyNoMoreInteractions(mSafemodeTimeoutAlarm); verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
} }
@Test @Test
public void testIkeSessionClosedTriggersDisconnect() throws Exception { public void testIkeSessionClosedTriggersDisconnect() throws Exception {
// Verify scheduled but not canceled when entering ConnectedState // Verify scheduled but not canceled when entering ConnectedState
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
getIkeSessionCallback().onClosed(); getIkeSessionCallback().onClosed();
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
@@ -221,7 +221,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
verify(mIkeSession).close(); verify(mIkeSession).close();
// Since network never validated, verify mSafemodeTimeoutAlarm not canceled // Since network never validated, verify mSafeModeTimeoutAlarm not canceled
verifyNoMoreInteractions(mSafemodeTimeoutAlarm); verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
} }
} }

View File

@@ -108,7 +108,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
} }
@Test @Test
public void testSafemodeTimeoutNotifiesCallback() { public void testSafeModeTimeoutNotifiesCallback() {
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState); verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
} }
} }

View File

@@ -79,7 +79,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
} }
@Test @Test
@@ -100,6 +100,6 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
assertNull(mGatewayConnection.getCurrentState()); assertNull(mGatewayConnection.getCurrentState());
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
verifySafemodeTimeoutAlarmAndGetCallback(true /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
} }
} }

View File

@@ -82,7 +82,7 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
} }
@Test @Test
public void testSafemodeTimeoutNotifiesCallback() { public void testSafeModeTimeoutNotifiesCallback() {
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState); verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
} }
} }

View File

@@ -93,7 +93,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
} }
@Test @Test
public void testSafemodeTimeoutNotifiesCallback() { public void testSafeModeTimeoutNotifiesCallback() {
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState); verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
} }
} }

View File

@@ -110,7 +110,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final WakeupMessage mTeardownTimeoutAlarm; @NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
@NonNull protected final WakeupMessage mDisconnectRequestAlarm; @NonNull protected final WakeupMessage mDisconnectRequestAlarm;
@NonNull protected final WakeupMessage mRetryTimeoutAlarm; @NonNull protected final WakeupMessage mRetryTimeoutAlarm;
@NonNull protected final WakeupMessage mSafemodeTimeoutAlarm; @NonNull protected final WakeupMessage mSafeModeTimeoutAlarm;
@NonNull protected final IpSecService mIpSecSvc; @NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr; @NonNull protected final ConnectivityManager mConnMgr;
@@ -131,7 +131,7 @@ public class VcnGatewayConnectionTestBase {
mTeardownTimeoutAlarm = mock(WakeupMessage.class); mTeardownTimeoutAlarm = mock(WakeupMessage.class);
mDisconnectRequestAlarm = mock(WakeupMessage.class); mDisconnectRequestAlarm = mock(WakeupMessage.class);
mRetryTimeoutAlarm = mock(WakeupMessage.class); mRetryTimeoutAlarm = mock(WakeupMessage.class);
mSafemodeTimeoutAlarm = mock(WakeupMessage.class); mSafeModeTimeoutAlarm = mock(WakeupMessage.class);
mIpSecSvc = mock(IpSecService.class); mIpSecSvc = mock(IpSecService.class);
setupIpSecManager(mContext, mIpSecSvc); setupIpSecManager(mContext, mIpSecSvc);
@@ -154,7 +154,7 @@ public class VcnGatewayConnectionTestBase {
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM); setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM); setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
setUpWakeupMessage(mRetryTimeoutAlarm, VcnGatewayConnection.RETRY_TIMEOUT_ALARM); setUpWakeupMessage(mRetryTimeoutAlarm, VcnGatewayConnection.RETRY_TIMEOUT_ALARM);
setUpWakeupMessage(mSafemodeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM); setUpWakeupMessage(mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM);
doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime(); doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime();
} }
@@ -259,23 +259,23 @@ public class VcnGatewayConnectionTestBase {
expectCanceled); expectCanceled);
} }
protected Runnable verifySafemodeTimeoutAlarmAndGetCallback(boolean expectCanceled) { protected Runnable verifySafeModeTimeoutAlarmAndGetCallback(boolean expectCanceled) {
return verifyWakeupMessageSetUpAndGetCallback( return verifyWakeupMessageSetUpAndGetCallback(
VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM,
mSafemodeTimeoutAlarm, mSafeModeTimeoutAlarm,
TimeUnit.SECONDS.toMillis(VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS), TimeUnit.SECONDS.toMillis(VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS),
expectCanceled); expectCanceled);
} }
protected void verifySafemodeTimeoutNotifiesCallback(@NonNull State expectedState) { protected void verifySafeModeTimeoutNotifiesCallback(@NonNull State expectedState) {
// Safemode timer starts when VcnGatewayConnection exits DisconnectedState (the initial // SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
// state) // state)
final Runnable delayedEvent = final Runnable delayedEvent =
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */); verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
delayedEvent.run(); delayedEvent.run();
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
verify(mGatewayStatusCallback).onEnteredSafemode(); verify(mGatewayStatusCallback).onEnteredSafeMode();
assertEquals(expectedState, mGatewayConnection.getCurrentState()); assertEquals(expectedState, mGatewayConnection.getCurrentState());
} }
} }

View File

@@ -34,7 +34,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.test.TestLooper; import android.os.test.TestLooper;
import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.VcnManagementService.VcnSafeModeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
@@ -56,7 +56,7 @@ public class VcnTest {
private VcnContext mVcnContext; private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot; private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider; private VcnNetworkProvider mVcnNetworkProvider;
private VcnSafemodeCallback mVcnSafemodeCallback; private VcnSafeModeCallback mVcnSafeModeCallback;
private Vcn.Dependencies mDeps; private Vcn.Dependencies mDeps;
private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor; private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
@@ -72,7 +72,7 @@ public class VcnTest {
mVcnContext = mock(VcnContext.class); mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class); mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class); mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnSafemodeCallback = mock(VcnSafemodeCallback.class); mVcnSafeModeCallback = mock(VcnSafeModeCallback.class);
mDeps = mock(Vcn.Dependencies.class); mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper(); mTestLooper = new TestLooper();
@@ -104,7 +104,7 @@ public class VcnTest {
TEST_SUB_GROUP, TEST_SUB_GROUP,
mConfig, mConfig,
mSubscriptionSnapshot, mSubscriptionSnapshot,
mVcnSafemodeCallback, mVcnSafeModeCallback,
mDeps); mDeps);
} }
@@ -148,7 +148,7 @@ public class VcnTest {
} }
@Test @Test
public void testGatewayEnteringSafemodeNotifiesVcn() { public void testGatewayEnteringSafeModeNotifiesVcn() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener(); final NetworkRequestListener requestListener = verifyAndGetRequestListener();
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
startVcnGatewayWithCapabilities(requestListener, capability); startVcnGatewayWithCapabilities(requestListener, capability);
@@ -168,16 +168,17 @@ public class VcnTest {
any(), any(),
mGatewayStatusCallbackCaptor.capture()); mGatewayStatusCallbackCaptor.capture());
// Doesn't matter which callback this gets - any Gateway entering Safemode should shut down // Doesn't matter which callback this gets - any Gateway entering safe mode should shut down
// all Gateways // all Gateways
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue(); final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
statusCallback.onEnteredSafemode(); statusCallback.onEnteredSafeMode();
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
assertFalse(mVcn.isActive());
for (final VcnGatewayConnection gatewayConnection : gatewayConnections) { for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
verify(gatewayConnection).teardownAsynchronously(); verify(gatewayConnection).teardownAsynchronously();
} }
verify(mVcnNetworkProvider).unregisterListener(requestListener); verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnSafemodeCallback).onEnteredSafemode(); verify(mVcnSafeModeCallback).onEnteredSafeMode();
} }
} }