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:
@@ -18,6 +18,7 @@ package android.net.vcn;
|
||||
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.vcn.IVcnStatusCallback;
|
||||
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
||||
import android.net.vcn.VcnConfig;
|
||||
import android.net.vcn.VcnUnderlyingNetworkPolicy;
|
||||
@@ -33,4 +34,7 @@ interface IVcnManagementService {
|
||||
void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
|
||||
void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
|
||||
VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp);
|
||||
|
||||
void registerVcnStatusCallback(in ParcelUuid subscriptionGroup, in IVcnStatusCallback callback, in String opPkgName);
|
||||
void unregisterVcnStatusCallback(in IVcnStatusCallback callback);
|
||||
}
|
||||
|
||||
22
core/java/android/net/vcn/IVcnStatusCallback.aidl
Normal file
22
core/java/android/net/vcn/IVcnStatusCallback.aidl
Normal 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();
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.os.Binder;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.RemoteException;
|
||||
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
|
||||
* Server.
|
||||
@@ -280,7 +375,30 @@ public class VcnManager {
|
||||
|
||||
@Override
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
@@ -29,6 +30,7 @@ import android.net.LinkProperties;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.net.vcn.IVcnManagementService;
|
||||
import android.net.vcn.IVcnStatusCallback;
|
||||
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
||||
import android.net.vcn.VcnConfig;
|
||||
import android.net.vcn.VcnUnderlyingNetworkPolicy;
|
||||
@@ -54,6 +56,7 @@ import android.util.Slog;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
import com.android.internal.util.LocationPermissionChecker;
|
||||
import com.android.server.vcn.TelephonySubscriptionTracker;
|
||||
import com.android.server.vcn.Vcn;
|
||||
import com.android.server.vcn.VcnContext;
|
||||
@@ -124,6 +127,7 @@ import java.util.concurrent.TimeUnit;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
|
||||
public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
@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 VcnContext mVcnContext;
|
||||
|
||||
/** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
|
||||
@Nullable private LocationPermissionChecker mLocationPermissionChecker;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
|
||||
@@ -169,6 +176,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners =
|
||||
new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@NonNull
|
||||
private final Map<IBinder, VcnStatusCallbackInfo> mRegisteredStatusCallbacks = new ArrayMap<>();
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
|
||||
mContext = requireNonNull(context, "Missing context");
|
||||
@@ -293,8 +304,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull VcnConfig config,
|
||||
@NonNull TelephonySubscriptionSnapshot snapshot,
|
||||
@NonNull VcnSafemodeCallback safemodeCallback) {
|
||||
return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
|
||||
@NonNull VcnSafeModeCallback safeModeCallback) {
|
||||
return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback);
|
||||
}
|
||||
|
||||
/** 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
|
||||
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. */
|
||||
@@ -309,6 +325,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
mContext.getSystemService(ConnectivityManager.class)
|
||||
.registerNetworkProvider(mNetworkProvider);
|
||||
mTelephonySubscriptionTracker.register();
|
||||
mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
|
||||
}
|
||||
|
||||
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
|
||||
// VCN.
|
||||
|
||||
final VcnSafemodeCallbackImpl safemodeCallback =
|
||||
new VcnSafemodeCallbackImpl(subscriptionGroup);
|
||||
final VcnSafeModeCallbackImpl safeModeCallback =
|
||||
new VcnSafeModeCallbackImpl(subscriptionGroup);
|
||||
|
||||
final Vcn newInstance =
|
||||
mDeps.newVcn(
|
||||
mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
|
||||
mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback);
|
||||
mVcns.put(subscriptionGroup, newInstance);
|
||||
|
||||
// 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. */
|
||||
private class PolicyListenerBinderDeath implements Binder.DeathRecipient {
|
||||
@NonNull private final IVcnUnderlyingNetworkPolicyListener mListener;
|
||||
@@ -672,22 +697,109 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
|
||||
}
|
||||
|
||||
/** Callback for signalling when a Vcn has entered Safemode. */
|
||||
public interface VcnSafemodeCallback {
|
||||
/** Called by a Vcn to signal that it has entered Safemode. */
|
||||
void onEnteredSafemode();
|
||||
/** Binder death recipient used to remove registered VcnStatusCallbacks. */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
class VcnStatusCallbackInfo implements Binder.DeathRecipient {
|
||||
@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. */
|
||||
private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
|
||||
/** Registers the provided callback for receiving VCN status updates. */
|
||||
@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;
|
||||
|
||||
private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
|
||||
private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) {
|
||||
mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnteredSafemode() {
|
||||
public void onEnteredSafeMode() {
|
||||
synchronized (mLock) {
|
||||
// Ignore if this subscription group doesn't exist anymore
|
||||
if (!mVcns.containsKey(mSubGroup)) {
|
||||
@@ -695,6 +807,27 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
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 java.util.Collections;
|
||||
@@ -86,18 +86,18 @@ public class Vcn extends Handler {
|
||||
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
|
||||
* VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
|
||||
* <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its
|
||||
* 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 ParcelUuid mSubscriptionGroup;
|
||||
@NonNull private final Dependencies mDeps;
|
||||
@NonNull private final VcnNetworkRequestListener mRequestListener;
|
||||
@NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
|
||||
@NonNull private final VcnSafeModeCallback mVcnSafeModeCallback;
|
||||
|
||||
@NonNull
|
||||
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
|
||||
@@ -125,13 +125,13 @@ public class Vcn extends Handler {
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull VcnConfig config,
|
||||
@NonNull TelephonySubscriptionSnapshot snapshot,
|
||||
@NonNull VcnSafemodeCallback vcnSafemodeCallback) {
|
||||
@NonNull VcnSafeModeCallback vcnSafeModeCallback) {
|
||||
this(
|
||||
vcnContext,
|
||||
subscriptionGroup,
|
||||
config,
|
||||
snapshot,
|
||||
vcnSafemodeCallback,
|
||||
vcnSafeModeCallback,
|
||||
new Dependencies());
|
||||
}
|
||||
|
||||
@@ -141,13 +141,13 @@ public class Vcn extends Handler {
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull VcnConfig config,
|
||||
@NonNull TelephonySubscriptionSnapshot snapshot,
|
||||
@NonNull VcnSafemodeCallback vcnSafemodeCallback,
|
||||
@NonNull VcnSafeModeCallback vcnSafeModeCallback,
|
||||
@NonNull Dependencies deps) {
|
||||
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
|
||||
mVcnContext = vcnContext;
|
||||
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
|
||||
mVcnSafemodeCallback =
|
||||
Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
|
||||
mVcnSafeModeCallback =
|
||||
Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback");
|
||||
mDeps = Objects.requireNonNull(deps, "Missing deps");
|
||||
mRequestListener = new VcnNetworkRequestListener();
|
||||
|
||||
@@ -216,8 +216,8 @@ public class Vcn extends Handler {
|
||||
case MSG_CMD_TEARDOWN:
|
||||
handleTeardown();
|
||||
break;
|
||||
case MSG_CMD_ENTER_SAFEMODE:
|
||||
handleEnterSafemode();
|
||||
case MSG_CMD_ENTER_SAFE_MODE:
|
||||
handleEnterSafeMode();
|
||||
break;
|
||||
default:
|
||||
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
|
||||
@@ -243,10 +243,10 @@ public class Vcn extends Handler {
|
||||
mIsActive.set(false);
|
||||
}
|
||||
|
||||
private void handleEnterSafemode() {
|
||||
private void handleEnterSafeMode() {
|
||||
handleTeardown();
|
||||
|
||||
mVcnSafemodeCallback.onEnteredSafemode();
|
||||
mVcnSafeModeCallback.onEnteredSafeMode();
|
||||
}
|
||||
|
||||
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. */
|
||||
@VisibleForTesting(visibility = Visibility.PACKAGE)
|
||||
public interface VcnGatewayStatusCallback {
|
||||
/** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
|
||||
void onEnteredSafemode();
|
||||
/** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
|
||||
void onEnteredSafeMode();
|
||||
}
|
||||
|
||||
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
|
||||
@Override
|
||||
public void onEnteredSafemode() {
|
||||
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
|
||||
public void onEnteredSafeMode() {
|
||||
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -418,13 +418,13 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
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}.
|
||||
*
|
||||
* <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).
|
||||
*
|
||||
* <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.
|
||||
*/
|
||||
private static final int EVENT_SAFEMODE_TIMEOUT_EXCEEDED = 10;
|
||||
private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10;
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@NonNull
|
||||
@@ -551,7 +551,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
@Nullable private WakeupMessage mTeardownTimeoutAlarm;
|
||||
@Nullable private WakeupMessage mDisconnectRequestAlarm;
|
||||
@Nullable private WakeupMessage mRetryTimeoutAlarm;
|
||||
@Nullable private WakeupMessage mSafemodeTimeoutAlarm;
|
||||
@Nullable private WakeupMessage mSafeModeTimeoutAlarm;
|
||||
|
||||
public VcnGatewayConnection(
|
||||
@NonNull VcnContext vcnContext,
|
||||
@@ -638,7 +638,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
cancelTeardownTimeoutAlarm();
|
||||
cancelDisconnectRequestAlarm();
|
||||
cancelRetryTimeoutAlarm();
|
||||
cancelSafemodeAlarm();
|
||||
cancelSafeModeAlarm();
|
||||
|
||||
mUnderlyingNetworkTracker.teardown();
|
||||
}
|
||||
@@ -928,27 +928,27 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
void setSafemodeAlarm() {
|
||||
void setSafeModeAlarm() {
|
||||
// Only schedule a NEW alarm if none is already set.
|
||||
if (mSafemodeTimeoutAlarm != null) {
|
||||
if (mSafeModeTimeoutAlarm != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Message delayedMessage = obtainMessage(EVENT_SAFEMODE_TIMEOUT_EXCEEDED, TOKEN_ALL);
|
||||
mSafemodeTimeoutAlarm =
|
||||
final Message delayedMessage = obtainMessage(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED, TOKEN_ALL);
|
||||
mSafeModeTimeoutAlarm =
|
||||
createScheduledAlarm(
|
||||
SAFEMODE_TIMEOUT_ALARM,
|
||||
delayedMessage,
|
||||
TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS));
|
||||
}
|
||||
|
||||
private void cancelSafemodeAlarm() {
|
||||
if (mSafemodeTimeoutAlarm != null) {
|
||||
mSafemodeTimeoutAlarm.cancel();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
private void cancelSafeModeAlarm() {
|
||||
if (mSafeModeTimeoutAlarm != null) {
|
||||
mSafeModeTimeoutAlarm.cancel();
|
||||
mSafeModeTimeoutAlarm = null;
|
||||
}
|
||||
|
||||
removeEqualMessages(EVENT_SAFEMODE_TIMEOUT_EXCEEDED);
|
||||
removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
cancelSafemodeAlarm();
|
||||
cancelSafeModeAlarm();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1153,7 +1153,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
@Override
|
||||
protected void exitState() {
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
mSafeModeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
@@ -1331,9 +1331,9 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
mSafeModeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
@@ -1399,7 +1399,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
|
||||
// Validated connection, clear failed attempt counter
|
||||
mFailedAttempts = 0;
|
||||
cancelSafemodeAlarm();
|
||||
cancelSafeModeAlarm();
|
||||
}
|
||||
|
||||
protected void applyTransform(
|
||||
@@ -1517,9 +1517,9 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
mSafeModeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
@@ -1575,7 +1575,7 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
protected void exitState() {
|
||||
// Attempt to set the safe mode alarm - this requires the Vcn Network being validated
|
||||
// while in ConnectedState (which cancels the previous alarm)
|
||||
setSafemodeAlarm();
|
||||
setSafeModeAlarm();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1623,9 +1623,9 @@ public class VcnGatewayConnection extends StateMachine {
|
||||
case EVENT_DISCONNECT_REQUESTED:
|
||||
handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
|
||||
break;
|
||||
case EVENT_SAFEMODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafemode();
|
||||
mSafemodeTimeoutAlarm = null;
|
||||
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
|
||||
mGatewayStatusCallback.onEnteredSafeMode();
|
||||
mSafeModeTimeoutAlarm = null;
|
||||
break;
|
||||
default:
|
||||
logUnhandledMessage(msg);
|
||||
|
||||
@@ -26,24 +26,30 @@ import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.vcn.VcnManager.VcnStatusCallback;
|
||||
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class VcnManagerTest {
|
||||
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
|
||||
private static final Executor INLINE_EXECUTOR = Runnable::run;
|
||||
|
||||
private IVcnManagementService mMockVcnManagementService;
|
||||
private VcnUnderlyingNetworkPolicyListener mMockPolicyListener;
|
||||
private VcnStatusCallback mMockStatusCallback;
|
||||
|
||||
private Context mContext;
|
||||
private VcnManager mVcnManager;
|
||||
@@ -52,6 +58,7 @@ public class VcnManagerTest {
|
||||
public void setUp() {
|
||||
mMockVcnManagementService = mock(IVcnManagementService.class);
|
||||
mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class);
|
||||
mMockStatusCallback = mock(VcnStatusCallback.class);
|
||||
|
||||
mContext = getContext();
|
||||
mVcnManager = new VcnManager(mContext, mMockVcnManagementService);
|
||||
@@ -132,4 +139,60 @@ public class VcnManagerTest {
|
||||
public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
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.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
@@ -52,6 +55,7 @@ import android.net.LinkProperties;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkCapabilities.Transport;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.net.vcn.IVcnStatusCallback;
|
||||
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
||||
import android.net.vcn.VcnConfig;
|
||||
import android.net.vcn.VcnConfigTest;
|
||||
@@ -70,7 +74,9 @@ import android.telephony.TelephonyManager;
|
||||
import androidx.test.filters.SmallTest;
|
||||
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.Vcn;
|
||||
import com.android.server.vcn.VcnContext;
|
||||
@@ -147,14 +153,17 @@ public class VcnManagementServiceTest {
|
||||
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
|
||||
private final TelephonySubscriptionTracker mSubscriptionTracker =
|
||||
mock(TelephonySubscriptionTracker.class);
|
||||
private final LocationPermissionChecker mLocationPermissionChecker =
|
||||
mock(LocationPermissionChecker.class);
|
||||
|
||||
private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
|
||||
ArgumentCaptor.forClass(VcnSafemodeCallback.class);
|
||||
private final ArgumentCaptor<VcnSafeModeCallback> mSafeModeCallbackCaptor =
|
||||
ArgumentCaptor.forClass(VcnSafeModeCallback.class);
|
||||
|
||||
private final VcnManagementService mVcnMgmtSvc;
|
||||
|
||||
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
|
||||
mock(IVcnUnderlyingNetworkPolicyListener.class);
|
||||
private final IVcnStatusCallback mMockStatusCallback = mock(IVcnStatusCallback.class);
|
||||
private final IBinder mMockIBinder = mock(IBinder.class);
|
||||
|
||||
public VcnManagementServiceTest() throws Exception {
|
||||
@@ -171,6 +180,7 @@ public class VcnManagementServiceTest {
|
||||
|
||||
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
|
||||
|
||||
doReturn(mMockContext).when(mVcnContext).getContext();
|
||||
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
|
||||
doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid();
|
||||
doReturn(mVcnContext)
|
||||
@@ -188,6 +198,9 @@ public class VcnManagementServiceTest {
|
||||
doReturn(mConfigReadWriteHelper)
|
||||
.when(mMockDeps)
|
||||
.newPersistableBundleLockingReadWriteHelper(any());
|
||||
doReturn(mLocationPermissionChecker)
|
||||
.when(mMockDeps)
|
||||
.newLocationPermissionChecker(eq(mMockContext));
|
||||
|
||||
// Setup VCN instance generation
|
||||
doAnswer((invocation) -> {
|
||||
@@ -206,6 +219,7 @@ public class VcnManagementServiceTest {
|
||||
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
|
||||
|
||||
doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
|
||||
doReturn(mMockIBinder).when(mMockStatusCallback).asBinder();
|
||||
|
||||
// Make sure the profiles are loaded.
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -707,24 +721,138 @@ public class VcnManagementServiceTest {
|
||||
verify(mMockPolicyListener).onPolicyChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
|
||||
TelephonySubscriptionSnapshot snapshot =
|
||||
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
|
||||
private void verifyVcnSafeModeCallback(
|
||||
@NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
|
||||
throws Exception {
|
||||
verify(mMockDeps)
|
||||
.newVcn(
|
||||
eq(mVcnContext),
|
||||
eq(TEST_UUID_1),
|
||||
eq(subGroup),
|
||||
eq(TEST_VCN_CONFIG),
|
||||
eq(snapshot),
|
||||
mSafemodeCallbackCaptor.capture());
|
||||
mSafeModeCallbackCaptor.capture());
|
||||
|
||||
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
|
||||
|
||||
VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
|
||||
safemodeCallback.onEnteredSafemode();
|
||||
VcnSafeModeCallback safeModeCallback = mSafeModeCallbackCaptor.getValue();
|
||||
safeModeCallback.onEnteredSafeMode();
|
||||
|
||||
assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnterStateDoesNotCancelSafemodeAlarm() {
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
public void testEnterStateDoesNotCancelSafeModeAlarm() {
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -144,7 +144,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
@Test
|
||||
public void testChildOpenedRegistersNetwork() throws Exception {
|
||||
// Verify scheduled but not canceled when entering ConnectedState
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
final VcnChildSessionConfiguration mMockChildSessionConfig =
|
||||
mock(VcnChildSessionConfiguration.class);
|
||||
@@ -188,17 +188,17 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
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
|
||||
mGatewayConnection.mNetworkAgent.onValidationStatus(
|
||||
NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */);
|
||||
verify(mSafemodeTimeoutAlarm).cancel();
|
||||
verify(mSafeModeTimeoutAlarm).cancel();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChildSessionClosedTriggersDisconnect() throws Exception {
|
||||
// Verify scheduled but not canceled when entering ConnectedState
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
getChildSessionCallback().onClosed();
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -206,14 +206,14 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
|
||||
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
// Since network never validated, verify mSafemodeTimeoutAlarm not canceled
|
||||
verifyNoMoreInteractions(mSafemodeTimeoutAlarm);
|
||||
// Since network never validated, verify mSafeModeTimeoutAlarm not canceled
|
||||
verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIkeSessionClosedTriggersDisconnect() throws Exception {
|
||||
// Verify scheduled but not canceled when entering ConnectedState
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
|
||||
getIkeSessionCallback().onClosed();
|
||||
mTestLooper.dispatchAll();
|
||||
@@ -221,7 +221,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
|
||||
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
|
||||
verify(mIkeSession).close();
|
||||
|
||||
// Since network never validated, verify mSafemodeTimeoutAlarm not canceled
|
||||
verifyNoMoreInteractions(mSafemodeTimeoutAlarm);
|
||||
// Since network never validated, verify mSafeModeTimeoutAlarm not canceled
|
||||
verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafemodeTimeoutNotifiesCallback() {
|
||||
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
|
||||
public void testSafeModeTimeoutNotifiesCallback() {
|
||||
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,6 +100,6 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
|
||||
|
||||
assertNull(mGatewayConnection.getCurrentState());
|
||||
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafemodeTimeoutNotifiesCallback() {
|
||||
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
|
||||
public void testSafeModeTimeoutNotifiesCallback() {
|
||||
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSafemodeTimeoutNotifiesCallback() {
|
||||
verifySafemodeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
|
||||
public void testSafeModeTimeoutNotifiesCallback() {
|
||||
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
@NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
|
||||
@NonNull protected final WakeupMessage mDisconnectRequestAlarm;
|
||||
@NonNull protected final WakeupMessage mRetryTimeoutAlarm;
|
||||
@NonNull protected final WakeupMessage mSafemodeTimeoutAlarm;
|
||||
@NonNull protected final WakeupMessage mSafeModeTimeoutAlarm;
|
||||
|
||||
@NonNull protected final IpSecService mIpSecSvc;
|
||||
@NonNull protected final ConnectivityManager mConnMgr;
|
||||
@@ -131,7 +131,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
mTeardownTimeoutAlarm = mock(WakeupMessage.class);
|
||||
mDisconnectRequestAlarm = mock(WakeupMessage.class);
|
||||
mRetryTimeoutAlarm = mock(WakeupMessage.class);
|
||||
mSafemodeTimeoutAlarm = mock(WakeupMessage.class);
|
||||
mSafeModeTimeoutAlarm = mock(WakeupMessage.class);
|
||||
|
||||
mIpSecSvc = mock(IpSecService.class);
|
||||
setupIpSecManager(mContext, mIpSecSvc);
|
||||
@@ -154,7 +154,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
|
||||
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_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();
|
||||
}
|
||||
@@ -259,23 +259,23 @@ public class VcnGatewayConnectionTestBase {
|
||||
expectCanceled);
|
||||
}
|
||||
|
||||
protected Runnable verifySafemodeTimeoutAlarmAndGetCallback(boolean expectCanceled) {
|
||||
protected Runnable verifySafeModeTimeoutAlarmAndGetCallback(boolean expectCanceled) {
|
||||
return verifyWakeupMessageSetUpAndGetCallback(
|
||||
VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM,
|
||||
mSafemodeTimeoutAlarm,
|
||||
mSafeModeTimeoutAlarm,
|
||||
TimeUnit.SECONDS.toMillis(VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS),
|
||||
expectCanceled);
|
||||
}
|
||||
|
||||
protected void verifySafemodeTimeoutNotifiesCallback(@NonNull State expectedState) {
|
||||
// Safemode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
|
||||
protected void verifySafeModeTimeoutNotifiesCallback(@NonNull State expectedState) {
|
||||
// SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
|
||||
// state)
|
||||
final Runnable delayedEvent =
|
||||
verifySafemodeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
|
||||
delayedEvent.run();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mGatewayStatusCallback).onEnteredSafemode();
|
||||
verify(mGatewayStatusCallback).onEnteredSafeMode();
|
||||
assertEquals(expectedState, mGatewayConnection.getCurrentState());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
|
||||
import android.os.ParcelUuid;
|
||||
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.Vcn.VcnGatewayStatusCallback;
|
||||
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
|
||||
@@ -56,7 +56,7 @@ public class VcnTest {
|
||||
private VcnContext mVcnContext;
|
||||
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
|
||||
private VcnNetworkProvider mVcnNetworkProvider;
|
||||
private VcnSafemodeCallback mVcnSafemodeCallback;
|
||||
private VcnSafeModeCallback mVcnSafeModeCallback;
|
||||
private Vcn.Dependencies mDeps;
|
||||
|
||||
private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
|
||||
@@ -72,7 +72,7 @@ public class VcnTest {
|
||||
mVcnContext = mock(VcnContext.class);
|
||||
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
|
||||
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
|
||||
mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
|
||||
mVcnSafeModeCallback = mock(VcnSafeModeCallback.class);
|
||||
mDeps = mock(Vcn.Dependencies.class);
|
||||
|
||||
mTestLooper = new TestLooper();
|
||||
@@ -104,7 +104,7 @@ public class VcnTest {
|
||||
TEST_SUB_GROUP,
|
||||
mConfig,
|
||||
mSubscriptionSnapshot,
|
||||
mVcnSafemodeCallback,
|
||||
mVcnSafeModeCallback,
|
||||
mDeps);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ public class VcnTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGatewayEnteringSafemodeNotifiesVcn() {
|
||||
public void testGatewayEnteringSafeModeNotifiesVcn() {
|
||||
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
|
||||
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
|
||||
startVcnGatewayWithCapabilities(requestListener, capability);
|
||||
@@ -168,16 +168,17 @@ public class VcnTest {
|
||||
any(),
|
||||
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
|
||||
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
|
||||
statusCallback.onEnteredSafemode();
|
||||
statusCallback.onEnteredSafeMode();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertFalse(mVcn.isActive());
|
||||
for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
|
||||
verify(gatewayConnection).teardownAsynchronously();
|
||||
}
|
||||
verify(mVcnNetworkProvider).unregisterListener(requestListener);
|
||||
verify(mVcnSafemodeCallback).onEnteredSafemode();
|
||||
verify(mVcnSafeModeCallback).onEnteredSafeMode();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user