Loading services/core/java/com/android/server/vcn/VcnGatewayConnection.java +160 −33 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.os.Message; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.util.ArraySet; import android.util.Slog; Loading @@ -68,6 +69,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; Loading Loading @@ -129,8 +131,8 @@ import java.util.concurrent.TimeUnit; * lack of WakeLocks). * * <p>Any attempt to remove messages from the Handler should be done using {@link * #removeEqualMessages(int, Object)}. This is necessary to ensure that the WakeLock is correctly * released when no messages remain in the Handler queue. * #removeEqualMessages}. This is necessary to ensure that the WakeLock is correctly released when * no messages remain in the Handler queue. * * @hide */ Loading @@ -140,6 +142,15 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0"); @VisibleForTesting(visibility = Visibility.PRIVATE) static final String TEARDOWN_TIMEOUT_ALARM = TAG + "_TEARDOWN_TIMEOUT_ALARM"; @VisibleForTesting(visibility = Visibility.PRIVATE) static final String DISCONNECT_REQUEST_ALARM = TAG + "_DISCONNECT_REQUEST_ALARM"; @VisibleForTesting(visibility = Visibility.PRIVATE) static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; private static final int[] MERGED_CAPABILITIES = new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; Loading @@ -150,7 +161,8 @@ public class VcnGatewayConnection extends StateMachine { private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel"; private static final int TOKEN_ALL = Integer.MIN_VALUE; private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30; @VisibleForTesting(visibility = Visibility.PRIVATE) static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30; @VisibleForTesting(visibility = Visibility.PRIVATE) static final int TEARDOWN_TIMEOUT_SECONDS = 5; Loading Loading @@ -510,6 +522,10 @@ public class VcnGatewayConnection extends StateMachine { */ private NetworkAgent mNetworkAgent; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm; public VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, Loading Loading @@ -592,6 +608,10 @@ public class VcnGatewayConnection extends StateMachine { releaseWakeLock(); cancelTeardownTimeoutAlarm(); cancelDisconnectRequestAlarm(); cancelRetryTimeoutAlarm(); mUnderlyingNetworkTracker.teardown(); } Loading Loading @@ -623,17 +643,11 @@ public class VcnGatewayConnection extends StateMachine { // If underlying is null, all underlying networks have been lost. Disconnect VCN after a // timeout. if (underlying == null) { sendMessageDelayed( EVENT_DISCONNECT_REQUESTED, TOKEN_ALL, new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST), TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); setDisconnectRequestAlarm(); } else { // Cancel any existing disconnect due to previous loss of underlying network removeEqualMessages( EVENT_DISCONNECT_REQUESTED, new EventDisconnectRequestedInfo( DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); // Received a new Network so any previous alarm is irrelevant - cancel + clear it, // and cancel any queued EVENT_DISCONNECT_REQUEST messages cancelDisconnectRequestAlarm(); } sendMessageAndAcquireWakeLock( Loading Loading @@ -724,6 +738,10 @@ public class VcnGatewayConnection extends StateMachine { super.sendMessage(msg); } // TODO(b/180146061): also override and Log.wtf() other Message handling methods // In mind are sendMessageDelayed(), sendMessageAtFrontOfQueue, removeMessages, and // removeDeferredMessages /** * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not * go to sleep before processing the sent message. Loading Loading @@ -751,14 +769,13 @@ public class VcnGatewayConnection extends StateMachine { super.sendMessage(what, token, arg2, data); } // TODO: remove this method once WakupMessage is used instead private void sendMessageDelayed(int what, int token, EventInfo data, long timeout) { super.sendMessageDelayed(what, token, ARG_NOT_PRESENT, data, timeout); } // TODO: remove this method once WakupMessage is used instead private void sendMessageDelayed(int what, int token, int arg2, EventInfo data, long timeout) { super.sendMessageDelayed(what, token, arg2, data, timeout); /** * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not * go to sleep before processing the sent message. */ private void sendMessageAndAcquireWakeLock(Message msg) { acquireWakeLock(); super.sendMessage(msg); } /** Loading Loading @@ -788,6 +805,100 @@ public class VcnGatewayConnection extends StateMachine { maybeReleaseWakeLock(); } private WakeupMessage createScheduledAlarm( @NonNull String cmdName, Message delayedMessage, long delay) { // WakeupMessage uses Handler#dispatchMessage() to immediately handle the specified Runnable // at the scheduled time. dispatchMessage() immediately executes and there may be queued // events that resolve the scheduled alarm pending in the queue. So, use the Runnable to // place the alarm event at the end of the queue with sendMessageAndAcquireWakeLock (which // guarantees the device will stay awake). final WakeupMessage alarm = mDeps.newWakeupMessage( mVcnContext, getHandler(), cmdName, () -> sendMessageAndAcquireWakeLock(delayedMessage)); alarm.schedule(mDeps.getElapsedRealTime() + delay); return alarm; } private void setTeardownTimeoutAlarm() { // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In // either case, there is nothing to cancel. if (mTeardownTimeoutAlarm != null) { Slog.wtf(TAG, "mTeardownTimeoutAlarm should be null before being set"); } final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken); mTeardownTimeoutAlarm = createScheduledAlarm( TEARDOWN_TIMEOUT_ALARM, delayedMessage, TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); } private void cancelTeardownTimeoutAlarm() { if (mTeardownTimeoutAlarm != null) { mTeardownTimeoutAlarm.cancel(); mTeardownTimeoutAlarm = null; } // Cancel any existing teardown timeouts removeEqualMessages(EVENT_TEARDOWN_TIMEOUT_EXPIRED); } private void setDisconnectRequestAlarm() { // Only schedule a NEW alarm if none is already set. if (mDisconnectRequestAlarm != null) { return; } final Message delayedMessage = obtainMessage( EVENT_DISCONNECT_REQUESTED, TOKEN_ALL, 0 /* arg2 */, new EventDisconnectRequestedInfo( DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); mDisconnectRequestAlarm = createScheduledAlarm( DISCONNECT_REQUEST_ALARM, delayedMessage, TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); } private void cancelDisconnectRequestAlarm() { if (mDisconnectRequestAlarm != null) { mDisconnectRequestAlarm.cancel(); mDisconnectRequestAlarm = null; } // Cancel any existing disconnect due to previous loss of underlying network removeEqualMessages( EVENT_DISCONNECT_REQUESTED, new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); } private void setRetryTimeoutAlarm(long delay) { // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In // either case, there is nothing to cancel. if (mRetryTimeoutAlarm != null) { Slog.wtf(TAG, "mRetryTimeoutAlarm should be null before being set"); } final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken); mRetryTimeoutAlarm = createScheduledAlarm(RETRY_TIMEOUT_ALARM, delayedMessage, delay); } private void cancelRetryTimeoutAlarm() { if (mRetryTimeoutAlarm != null) { mRetryTimeoutAlarm.cancel(); mRetryTimeoutAlarm = null; } removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); } private void sessionLost(int token, @Nullable Exception exception) { sendMessageAndAcquireWakeLock( EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); Loading Loading @@ -847,8 +958,8 @@ public class VcnGatewayConnection extends StateMachine { * builds. * * <p>Here be dragons: processMessage() is final to ensure that mWakeLock is released once * the Handler queue is empty. Future changes to processMessage() (or overrides to * processMessage) MUST ensure that mWakeLock is correctly released. * the Handler queue is empty. Future changes (or overrides) to processMessage() to MUST * ensure that mWakeLock is correctly released. */ @Override public final boolean processMessage(Message msg) { Loading Loading @@ -1030,10 +1141,9 @@ public class VcnGatewayConnection extends StateMachine { } mIkeSession.close(); sendMessageDelayed( EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken, TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); // Safe to blindly set up, as it is cancelled and cleared on exiting this state setTeardownTimeoutAlarm(); } @Override Loading Loading @@ -1084,6 +1194,8 @@ public class VcnGatewayConnection extends StateMachine { @Override protected void exitState() throws Exception { mSkipRetryTimeout = false; cancelTeardownTimeoutAlarm(); } } Loading Loading @@ -1385,8 +1497,8 @@ public class VcnGatewayConnection extends StateMachine { Slog.wtf(TAG, "Underlying network was null in retry state"); transitionTo(mDisconnectedState); } else { sendMessageDelayed( EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs()); // Safe to blindly set up, as it is cancelled and cleared on exiting this state setRetryTimeoutAlarm(getNextRetryIntervalsMs()); } } Loading @@ -1399,8 +1511,6 @@ public class VcnGatewayConnection extends StateMachine { // If new underlying is null, all networks were lost; go back to disconnected. if (mUnderlying == null) { removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); transitionTo(mDisconnectedState); return; } else if (oldUnderlying != null Loading @@ -1411,8 +1521,6 @@ public class VcnGatewayConnection extends StateMachine { // Fallthrough case EVENT_RETRY_TIMEOUT_EXPIRED: removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); transitionTo(mConnectingState); break; case EVENT_DISCONNECT_REQUESTED: Loading @@ -1424,6 +1532,11 @@ public class VcnGatewayConnection extends StateMachine { } } @Override public void exitState() { cancelRetryTimeoutAlarm(); } private long getNextRetryIntervalsMs() { final int retryDelayIndex = mFailedAttempts - 1; final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs(); Loading Loading @@ -1702,6 +1815,20 @@ public class VcnGatewayConnection extends StateMachine { @NonNull Context context, int wakeLockFlag, @NonNull String wakeLockTag) { return new VcnWakeLock(context, wakeLockFlag, wakeLockTag); } /** Builds a new WakeupMessage. */ public WakeupMessage newWakeupMessage( @NonNull VcnContext vcnContext, @NonNull Handler handler, @NonNull String tag, @NonNull Runnable runnable) { return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable); } /** Gets the elapsed real time since boot, in millis. */ public long getElapsedRealTime() { return SystemClock.elapsedRealtime(); } } /** Loading tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +3 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); verify(mIkeSession, never()).close(); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading Loading @@ -170,6 +171,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -179,5 +181,6 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } } tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +4 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).kill(); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -73,6 +74,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verify(mIkeSession, never()).kill(); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -92,6 +94,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -101,5 +104,6 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } } tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +17 −5 Original line number Diff line number Diff line Loading @@ -16,9 +16,9 @@ package com.android.server.vcn; import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import androidx.test.filters.SmallTest; Loading @@ -28,8 +28,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.TimeUnit; /** Tests for VcnGatewayConnection.DisconnectedState */ @RunWith(AndroidJUnit4.class) @SmallTest Loading @@ -40,6 +38,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession()); // ensure that mGatewayConnection has an underlying Network before entering // DisconnectingState mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_2); mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState); mTestLooper.dispatchAll(); } Loading @@ -49,12 +50,22 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec getIkeSessionCallback().onClosed(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mMockIkeSession).close(); verify(mMockIkeSession, never()).kill(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } @Test public void testTimeoutExpired() throws Exception { mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); Runnable delayedEvent = verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); // Can't use mTestLooper to advance the time since VcnGatewayConnection uses WakeupMessages // (which are mocked here). Directly invoke the runnable instead. This is still sufficient, // since verifyTeardownTimeoutAlarmAndGetCallback() verifies the WakeupMessage was scheduled // with the correct delay. delayedEvent.run(); mTestLooper.dispatchAll(); verify(mMockIkeSession).kill(); Loading @@ -67,5 +78,6 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec // Should do nothing; already tearing down. assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } } Loading
services/core/java/com/android/server/vcn/VcnGatewayConnection.java +160 −33 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.os.Message; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.util.ArraySet; import android.util.Slog; Loading @@ -68,6 +69,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; Loading Loading @@ -129,8 +131,8 @@ import java.util.concurrent.TimeUnit; * lack of WakeLocks). * * <p>Any attempt to remove messages from the Handler should be done using {@link * #removeEqualMessages(int, Object)}. This is necessary to ensure that the WakeLock is correctly * released when no messages remain in the Handler queue. * #removeEqualMessages}. This is necessary to ensure that the WakeLock is correctly released when * no messages remain in the Handler queue. * * @hide */ Loading @@ -140,6 +142,15 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0"); @VisibleForTesting(visibility = Visibility.PRIVATE) static final String TEARDOWN_TIMEOUT_ALARM = TAG + "_TEARDOWN_TIMEOUT_ALARM"; @VisibleForTesting(visibility = Visibility.PRIVATE) static final String DISCONNECT_REQUEST_ALARM = TAG + "_DISCONNECT_REQUEST_ALARM"; @VisibleForTesting(visibility = Visibility.PRIVATE) static final String RETRY_TIMEOUT_ALARM = TAG + "_RETRY_TIMEOUT_ALARM"; private static final int[] MERGED_CAPABILITIES = new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING}; private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; Loading @@ -150,7 +161,8 @@ public class VcnGatewayConnection extends StateMachine { private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel"; private static final int TOKEN_ALL = Integer.MIN_VALUE; private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30; @VisibleForTesting(visibility = Visibility.PRIVATE) static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30; @VisibleForTesting(visibility = Visibility.PRIVATE) static final int TEARDOWN_TIMEOUT_SECONDS = 5; Loading Loading @@ -510,6 +522,10 @@ public class VcnGatewayConnection extends StateMachine { */ private NetworkAgent mNetworkAgent; @Nullable private WakeupMessage mTeardownTimeoutAlarm; @Nullable private WakeupMessage mDisconnectRequestAlarm; @Nullable private WakeupMessage mRetryTimeoutAlarm; public VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, Loading Loading @@ -592,6 +608,10 @@ public class VcnGatewayConnection extends StateMachine { releaseWakeLock(); cancelTeardownTimeoutAlarm(); cancelDisconnectRequestAlarm(); cancelRetryTimeoutAlarm(); mUnderlyingNetworkTracker.teardown(); } Loading Loading @@ -623,17 +643,11 @@ public class VcnGatewayConnection extends StateMachine { // If underlying is null, all underlying networks have been lost. Disconnect VCN after a // timeout. if (underlying == null) { sendMessageDelayed( EVENT_DISCONNECT_REQUESTED, TOKEN_ALL, new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST), TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); setDisconnectRequestAlarm(); } else { // Cancel any existing disconnect due to previous loss of underlying network removeEqualMessages( EVENT_DISCONNECT_REQUESTED, new EventDisconnectRequestedInfo( DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); // Received a new Network so any previous alarm is irrelevant - cancel + clear it, // and cancel any queued EVENT_DISCONNECT_REQUEST messages cancelDisconnectRequestAlarm(); } sendMessageAndAcquireWakeLock( Loading Loading @@ -724,6 +738,10 @@ public class VcnGatewayConnection extends StateMachine { super.sendMessage(msg); } // TODO(b/180146061): also override and Log.wtf() other Message handling methods // In mind are sendMessageDelayed(), sendMessageAtFrontOfQueue, removeMessages, and // removeDeferredMessages /** * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not * go to sleep before processing the sent message. Loading Loading @@ -751,14 +769,13 @@ public class VcnGatewayConnection extends StateMachine { super.sendMessage(what, token, arg2, data); } // TODO: remove this method once WakupMessage is used instead private void sendMessageDelayed(int what, int token, EventInfo data, long timeout) { super.sendMessageDelayed(what, token, ARG_NOT_PRESENT, data, timeout); } // TODO: remove this method once WakupMessage is used instead private void sendMessageDelayed(int what, int token, int arg2, EventInfo data, long timeout) { super.sendMessageDelayed(what, token, arg2, data, timeout); /** * WakeLock-based alternative to {@link #sendMessage}. Use to guarantee that the device will not * go to sleep before processing the sent message. */ private void sendMessageAndAcquireWakeLock(Message msg) { acquireWakeLock(); super.sendMessage(msg); } /** Loading Loading @@ -788,6 +805,100 @@ public class VcnGatewayConnection extends StateMachine { maybeReleaseWakeLock(); } private WakeupMessage createScheduledAlarm( @NonNull String cmdName, Message delayedMessage, long delay) { // WakeupMessage uses Handler#dispatchMessage() to immediately handle the specified Runnable // at the scheduled time. dispatchMessage() immediately executes and there may be queued // events that resolve the scheduled alarm pending in the queue. So, use the Runnable to // place the alarm event at the end of the queue with sendMessageAndAcquireWakeLock (which // guarantees the device will stay awake). final WakeupMessage alarm = mDeps.newWakeupMessage( mVcnContext, getHandler(), cmdName, () -> sendMessageAndAcquireWakeLock(delayedMessage)); alarm.schedule(mDeps.getElapsedRealTime() + delay); return alarm; } private void setTeardownTimeoutAlarm() { // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In // either case, there is nothing to cancel. if (mTeardownTimeoutAlarm != null) { Slog.wtf(TAG, "mTeardownTimeoutAlarm should be null before being set"); } final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken); mTeardownTimeoutAlarm = createScheduledAlarm( TEARDOWN_TIMEOUT_ALARM, delayedMessage, TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); } private void cancelTeardownTimeoutAlarm() { if (mTeardownTimeoutAlarm != null) { mTeardownTimeoutAlarm.cancel(); mTeardownTimeoutAlarm = null; } // Cancel any existing teardown timeouts removeEqualMessages(EVENT_TEARDOWN_TIMEOUT_EXPIRED); } private void setDisconnectRequestAlarm() { // Only schedule a NEW alarm if none is already set. if (mDisconnectRequestAlarm != null) { return; } final Message delayedMessage = obtainMessage( EVENT_DISCONNECT_REQUESTED, TOKEN_ALL, 0 /* arg2 */, new EventDisconnectRequestedInfo( DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); mDisconnectRequestAlarm = createScheduledAlarm( DISCONNECT_REQUEST_ALARM, delayedMessage, TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); } private void cancelDisconnectRequestAlarm() { if (mDisconnectRequestAlarm != null) { mDisconnectRequestAlarm.cancel(); mDisconnectRequestAlarm = null; } // Cancel any existing disconnect due to previous loss of underlying network removeEqualMessages( EVENT_DISCONNECT_REQUESTED, new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); } private void setRetryTimeoutAlarm(long delay) { // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In // either case, there is nothing to cancel. if (mRetryTimeoutAlarm != null) { Slog.wtf(TAG, "mRetryTimeoutAlarm should be null before being set"); } final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken); mRetryTimeoutAlarm = createScheduledAlarm(RETRY_TIMEOUT_ALARM, delayedMessage, delay); } private void cancelRetryTimeoutAlarm() { if (mRetryTimeoutAlarm != null) { mRetryTimeoutAlarm.cancel(); mRetryTimeoutAlarm = null; } removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); } private void sessionLost(int token, @Nullable Exception exception) { sendMessageAndAcquireWakeLock( EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); Loading Loading @@ -847,8 +958,8 @@ public class VcnGatewayConnection extends StateMachine { * builds. * * <p>Here be dragons: processMessage() is final to ensure that mWakeLock is released once * the Handler queue is empty. Future changes to processMessage() (or overrides to * processMessage) MUST ensure that mWakeLock is correctly released. * the Handler queue is empty. Future changes (or overrides) to processMessage() to MUST * ensure that mWakeLock is correctly released. */ @Override public final boolean processMessage(Message msg) { Loading Loading @@ -1030,10 +1141,9 @@ public class VcnGatewayConnection extends StateMachine { } mIkeSession.close(); sendMessageDelayed( EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken, TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); // Safe to blindly set up, as it is cancelled and cleared on exiting this state setTeardownTimeoutAlarm(); } @Override Loading Loading @@ -1084,6 +1194,8 @@ public class VcnGatewayConnection extends StateMachine { @Override protected void exitState() throws Exception { mSkipRetryTimeout = false; cancelTeardownTimeoutAlarm(); } } Loading Loading @@ -1385,8 +1497,8 @@ public class VcnGatewayConnection extends StateMachine { Slog.wtf(TAG, "Underlying network was null in retry state"); transitionTo(mDisconnectedState); } else { sendMessageDelayed( EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs()); // Safe to blindly set up, as it is cancelled and cleared on exiting this state setRetryTimeoutAlarm(getNextRetryIntervalsMs()); } } Loading @@ -1399,8 +1511,6 @@ public class VcnGatewayConnection extends StateMachine { // If new underlying is null, all networks were lost; go back to disconnected. if (mUnderlying == null) { removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); transitionTo(mDisconnectedState); return; } else if (oldUnderlying != null Loading @@ -1411,8 +1521,6 @@ public class VcnGatewayConnection extends StateMachine { // Fallthrough case EVENT_RETRY_TIMEOUT_EXPIRED: removeEqualMessages(EVENT_RETRY_TIMEOUT_EXPIRED); transitionTo(mConnectingState); break; case EVENT_DISCONNECT_REQUESTED: Loading @@ -1424,6 +1532,11 @@ public class VcnGatewayConnection extends StateMachine { } } @Override public void exitState() { cancelRetryTimeoutAlarm(); } private long getNextRetryIntervalsMs() { final int retryDelayIndex = mFailedAttempts - 1; final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs(); Loading Loading @@ -1702,6 +1815,20 @@ public class VcnGatewayConnection extends StateMachine { @NonNull Context context, int wakeLockFlag, @NonNull String wakeLockTag) { return new VcnWakeLock(context, wakeLockFlag, wakeLockTag); } /** Builds a new WakeupMessage. */ public WakeupMessage newWakeupMessage( @NonNull VcnContext vcnContext, @NonNull Handler handler, @NonNull String tag, @NonNull Runnable runnable) { return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable); } /** Gets the elapsed real time since boot, in millis. */ public long getElapsedRealTime() { return SystemClock.elapsedRealtime(); } } /** Loading
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +3 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); verify(mIkeSession, never()).close(); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading Loading @@ -170,6 +171,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -179,5 +181,6 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } }
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +4 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).kill(); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -73,6 +74,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verify(mIkeSession, never()).kill(); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -92,6 +94,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading @@ -101,5 +104,6 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mIkeSession).close(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } }
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); } @Test Loading
tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +17 −5 Original line number Diff line number Diff line Loading @@ -16,9 +16,9 @@ package com.android.server.vcn; import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import androidx.test.filters.SmallTest; Loading @@ -28,8 +28,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.TimeUnit; /** Tests for VcnGatewayConnection.DisconnectedState */ @RunWith(AndroidJUnit4.class) @SmallTest Loading @@ -40,6 +38,9 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession()); // ensure that mGatewayConnection has an underlying Network before entering // DisconnectingState mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_2); mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState); mTestLooper.dispatchAll(); } Loading @@ -49,12 +50,22 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec getIkeSessionCallback().onClosed(); mTestLooper.dispatchAll(); assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); verify(mMockIkeSession).close(); verify(mMockIkeSession, never()).kill(); verifyTeardownTimeoutAlarmAndGetCallback(true /* expectCanceled */); } @Test public void testTimeoutExpired() throws Exception { mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); Runnable delayedEvent = verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); // Can't use mTestLooper to advance the time since VcnGatewayConnection uses WakeupMessages // (which are mocked here). Directly invoke the runnable instead. This is still sufficient, // since verifyTeardownTimeoutAlarmAndGetCallback() verifies the WakeupMessage was scheduled // with the correct delay. delayedEvent.run(); mTestLooper.dispatchAll(); verify(mMockIkeSession).kill(); Loading @@ -67,5 +78,6 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec // Should do nothing; already tearing down. assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); } }