Merge "Add satellite DemoSimulator." into 24D1-dev
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index dbb586d..2f18796 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -29,6 +29,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Build;
import android.os.Looper;
import android.os.SystemProperties;
@@ -69,6 +70,8 @@
public static final int TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE = 3;
/** This type is used by CTS to override the time to datagram delay in demo mode */
public static final int TIMEOUT_TYPE_DATAGRAM_DELAY_IN_DEMO_MODE = 4;
+ /** This type is used by CTS to override wait for device alignment in demo datagram boolean */
+ public static final int BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM = 1;
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
private static final boolean DEBUG = !"user".equals(Build.TYPE);
@@ -101,6 +104,7 @@
private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
private long mDatagramWaitTimeForConnectedState;
private long mModemImageSwitchingDuration;
+ private boolean mWaitForDeviceAlignmentInDemoDatagram;
@GuardedBy("mLock")
@SatelliteManager.SatelliteModemState
private int mSatelltieModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
@@ -155,6 +159,8 @@
mDatagramWaitTimeForConnectedState = getDatagramWaitForConnectedStateTimeoutMillis();
mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis();
+ mWaitForDeviceAlignmentInDemoDatagram =
+ getWaitForDeviceAlignmentInDemoDatagramFromResources();
mDemoModeDatagramList = new ArrayList<>();
}
@@ -492,6 +498,36 @@
return true;
}
+ /**
+ * This API can be used by only CTS to override the boolean configs used by the
+ * DatagramController module.
+ *
+ * @param enable Whether to enable or disable boolean config.
+ * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+ */
+ boolean setDatagramControllerBooleanConfig(
+ boolean reset, int booleanType, boolean enable) {
+ if (!isMockModemAllowed()) {
+ loge("Updating boolean config is not allowed");
+ return false;
+ }
+
+ logd("setDatagramControllerTimeoutDuration: booleanType=" + booleanType
+ + ", reset=" + reset + ", enable=" + enable);
+ if (booleanType == BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM) {
+ if (reset) {
+ mWaitForDeviceAlignmentInDemoDatagram =
+ getWaitForDeviceAlignmentInDemoDatagramFromResources();
+ } else {
+ mWaitForDeviceAlignmentInDemoDatagram = enable;
+ }
+ } else {
+ loge("Invalid boolean type " + booleanType);
+ return false;
+ }
+ return true;
+ }
+
private boolean isMockModemAllowed() {
return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
}
@@ -546,6 +582,38 @@
}
}
+ /**
+ * Get whether to wait for device alignment with satellite before sending datagrams.
+ *
+ * @param isAligned if the device is aligned with satellite or not
+ * @return {@code true} if device is not aligned to satellite,
+ * and it is required to wait for alignment else {@code false}
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean waitForAligningToSatellite(boolean isAligned) {
+ if (isAligned) {
+ return false;
+ }
+
+ return getWaitForDeviceAlignmentInDemoDatagram();
+ }
+
+ private boolean getWaitForDeviceAlignmentInDemoDatagram() {
+ return mWaitForDeviceAlignmentInDemoDatagram;
+ }
+
+ private boolean getWaitForDeviceAlignmentInDemoDatagramFromResources() {
+ boolean waitForDeviceAlignmentInDemoDatagram = false;
+ try {
+ waitForDeviceAlignmentInDemoDatagram = mContext.getResources().getBoolean(
+ R.bool.config_wait_for_device_alignment_in_demo_datagram);
+ } catch (Resources.NotFoundException ex) {
+ loge("getWaitForDeviceAlignmentInDemoDatagram: ex=" + ex);
+ }
+
+ return waitForDeviceAlignmentInDemoDatagram;
+ }
+
private static void logd(@NonNull String log) {
Rlog.d(TAG, log);
}
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 4fc8d55..f764b2b 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -239,7 +239,7 @@
if (mIsDemoMode && (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)) {
if (argument.skipCheckingSatelliteAligned) {
logd("Satellite was already aligned. No need to check alignment again");
- } else if (!mIsAligned) {
+ } else if (mDatagramController.waitForAligningToSatellite(mIsAligned)) {
logd("Satellite is not aligned in demo mode, wait for the alignment.");
startSatelliteAlignedTimer(request);
break;
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index 145b017..f1f0fde 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -620,7 +620,7 @@
DatagramReceiverHandlerRequest request = new DatagramReceiverHandlerRequest(
callback, phone, subId);
synchronized (mLock) {
- if (mIsAligned) {
+ if (!mDatagramController.waitForAligningToSatellite(mIsAligned)) {
Message msg = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
request);
AsyncResult.forMessage(msg, null, null);
diff --git a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
new file mode 100644
index 0000000..3c31ae8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.IIntegerConsumer;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NtnSignalStrength;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.telephony.satellite.stub.SatelliteResult;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+public class DemoSimulator extends StateMachine {
+ private static final String TAG = "DemoSimulator";
+ private static final boolean DBG = true;
+
+ private static final int EVENT_SATELLITE_MODE_ON = 1;
+ private static final int EVENT_SATELLITE_MODE_OFF = 2;
+ private static final int EVENT_DEVICE_ALIGNED_WITH_SATELLITE = 3;
+ protected static final int EVENT_DEVICE_ALIGNED = 4;
+ protected static final int EVENT_DEVICE_NOT_ALIGNED = 5;
+
+ @NonNull private static DemoSimulator sInstance;
+
+ @NonNull private final Context mContext;
+ @NonNull private final SatelliteController mSatelliteController;
+ @NonNull private final PowerOffState mPowerOffState = new PowerOffState();
+ @NonNull private final NotConnectedState mNotConnectedState = new NotConnectedState();
+ @NonNull private final ConnectedState mConnectedState = new ConnectedState();
+ @NonNull private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mIsAligned = false;
+ private ISatelliteListener mISatelliteListener;
+
+ /**
+ * @return The singleton instance of DemoSimulator.
+ */
+ public static DemoSimulator getInstance() {
+ if (sInstance == null) {
+ Log.e(TAG, "DemoSimulator was not yet initialized.");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create the DemoSimulator singleton instance.
+ *
+ * @param context The Context for the DemoSimulator.
+ * @return The singleton instance of DemoSimulator.
+ */
+ public static DemoSimulator make(@NonNull Context context,
+ @NonNull SatelliteController satelliteController) {
+ if (sInstance == null) {
+ sInstance = new DemoSimulator(context, Looper.getMainLooper(), satelliteController);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a DemoSimulator.
+ *
+ * @param context The Context for the DemoSimulator.
+ * @param looper The looper associated with the handler of this class.
+ */
+ protected DemoSimulator(@NonNull Context context, @NonNull Looper looper,
+ @NonNull SatelliteController satelliteController) {
+ super(TAG, looper);
+
+ mContext = context;
+ mSatelliteController = satelliteController;
+ addState(mPowerOffState);
+ addState(mNotConnectedState);
+ addState(mConnectedState);
+ setInitialState(mPowerOffState);
+ start();
+ }
+
+ private class PowerOffState extends State {
+ @Override
+ public void enter() {
+ logd("Entering PowerOffState");
+ }
+
+ @Override
+ public void exit() {
+ logd("Exiting PowerOffState");
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("PowerOffState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_SATELLITE_MODE_ON:
+ transitionTo(mNotConnectedState);
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+ }
+
+ private class NotConnectedState extends State {
+ @Override
+ public void enter() {
+ logd("Entering NotConnectedState");
+
+ try {
+ NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
+ ntnSignalStrength.signalStrengthLevel = 0;
+ mISatelliteListener.onSatelliteModemStateChanged(
+ SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+ mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
+
+ synchronized (mLock) {
+ if (mIsAligned) {
+ handleEventDeviceAlignedWithSatellite(true);
+ }
+ }
+ } catch (RemoteException e) {
+ loge("NotConnectedState: RemoteException " + e);
+ }
+ }
+
+ @Override
+ public void exit() {
+ logd("Exiting NotConnectedState");
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("NotConnectedState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_SATELLITE_MODE_OFF:
+ transitionTo(mPowerOffState);
+ break;
+ case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+ handleEventDeviceAlignedWithSatellite((boolean) msg.obj);
+ break;
+ case EVENT_DEVICE_ALIGNED:
+ transitionTo(mConnectedState);
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+
+ private void handleEventDeviceAlignedWithSatellite(boolean isAligned) {
+ if (isAligned && !hasMessages(EVENT_DEVICE_ALIGNED)) {
+ long durationMillis = mSatelliteController.getDemoPointingAlignedDurationMillis();
+ logd("NotConnectedState: handleEventAlignedWithSatellite isAligned=true."
+ + " Send delayed EVENT_DEVICE_ALIGNED message in"
+ + " durationMillis=" + durationMillis);
+ sendMessageDelayed(EVENT_DEVICE_ALIGNED, durationMillis);
+ } else if (!isAligned && hasMessages(EVENT_DEVICE_ALIGNED)) {
+ logd("NotConnectedState: handleEventAlignedWithSatellite isAligned=false."
+ + " Remove EVENT_DEVICE_ALIGNED message.");
+ removeMessages(EVENT_DEVICE_ALIGNED);
+ }
+ }
+ }
+
+ private class ConnectedState extends State {
+ @Override
+ public void enter() {
+ logd("Entering ConnectedState");
+
+ try {
+ NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
+ ntnSignalStrength.signalStrengthLevel = 2;
+ mISatelliteListener.onSatelliteModemStateChanged(
+ SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+ mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
+
+ synchronized (mLock) {
+ if (!mIsAligned) {
+ handleEventDeviceAlignedWithSatellite(false);
+ }
+ }
+ } catch (RemoteException e) {
+ loge("ConnectedState: RemoteException " + e);
+ }
+ }
+
+ @Override
+ public void exit() {
+ logd("Exiting ConnectedState");
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ if (DBG) log("ConnectedState: processing " + getWhatToString(msg.what));
+ switch (msg.what) {
+ case EVENT_SATELLITE_MODE_OFF:
+ transitionTo(mPowerOffState);
+ break;
+ case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+ handleEventDeviceAlignedWithSatellite((boolean) msg.obj);
+ break;
+ case EVENT_DEVICE_NOT_ALIGNED:
+ transitionTo(mNotConnectedState);
+ break;
+ }
+ // Ignore all unexpected events.
+ return HANDLED;
+ }
+
+ private void handleEventDeviceAlignedWithSatellite(boolean isAligned) {
+ if (!isAligned && !hasMessages(EVENT_DEVICE_NOT_ALIGNED)) {
+ long durationMillis =
+ mSatelliteController.getDemoPointingNotAlignedDurationMillis();
+ logd("ConnectedState: handleEventAlignedWithSatellite isAligned=false."
+ + " Send delayed EVENT_DEVICE_NOT_ALIGNED message"
+ + " in durationMillis=" + durationMillis);
+ sendMessageDelayed(EVENT_DEVICE_NOT_ALIGNED, durationMillis);
+ } else if (isAligned && hasMessages(EVENT_DEVICE_NOT_ALIGNED)) {
+ logd("ConnectedState: handleEventAlignedWithSatellite isAligned=true."
+ + " Remove EVENT_DEVICE_NOT_ALIGNED message.");
+ removeMessages(EVENT_DEVICE_NOT_ALIGNED);
+ }
+ }
+ }
+
+ /**
+ * @return the string for msg.what
+ */
+ @Override
+ protected String getWhatToString(int what) {
+ String whatString;
+ switch (what) {
+ case EVENT_SATELLITE_MODE_ON:
+ whatString = "EVENT_SATELLITE_MODE_ON";
+ break;
+ case EVENT_SATELLITE_MODE_OFF:
+ whatString = "EVENT_SATELLITE_MODE_OFF";
+ break;
+ case EVENT_DEVICE_ALIGNED_WITH_SATELLITE:
+ whatString = "EVENT_DEVICE_ALIGNED_WITH_SATELLITE";
+ break;
+ case EVENT_DEVICE_ALIGNED:
+ whatString = "EVENT_DEVICE_ALIGNED";
+ break;
+ case EVENT_DEVICE_NOT_ALIGNED:
+ whatString = "EVENT_DEVICE_NOT_ALIGNED";
+ break;
+ default:
+ whatString = "UNKNOWN EVENT " + what;
+ }
+ return whatString;
+ }
+
+ /**
+ * Register the callback interface with satellite service.
+ *
+ * @param listener The callback interface to handle satellite service indications.
+ */
+ public void setSatelliteListener(@NonNull ISatelliteListener listener) {
+ mISatelliteListener = listener;
+ }
+
+ /**
+ * Allow cellular modem scanning while satellite mode is on.
+ *
+ * @param enabled {@code true} to enable cellular modem while satellite mode is on
+ * and {@code false} to disable
+ * @param errorCallback The callback to receive the error code result of the operation.
+ */
+ public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+ @NonNull IIntegerConsumer errorCallback) {
+ try {
+ errorCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS);
+ } catch (RemoteException e) {
+ loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
+ }
+ }
+
+ /**
+ * This function is used by {@link SatelliteSessionController} to notify {@link DemoSimulator}
+ * that satellite mode is ON.
+ */
+ public void onSatelliteModeOn() {
+ if (mSatelliteController.isDemoModeEnabled()) {
+ sendMessage(EVENT_SATELLITE_MODE_ON);
+ }
+ }
+
+ /**
+ * This function is used by {@link SatelliteSessionController} to notify {@link DemoSimulator}
+ * that satellite mode is OFF.
+ */
+ public void onSatelliteModeOff() {
+ sendMessage(EVENT_SATELLITE_MODE_OFF);
+ }
+
+ /**
+ * Set whether the device is aligned with the satellite.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void setDeviceAlignedWithSatellite(boolean isAligned) {
+ synchronized (mLock) {
+ if (mSatelliteController.isDemoModeEnabled()) {
+ mIsAligned = isAligned;
+ sendMessage(EVENT_DEVICE_ALIGNED_WITH_SATELLITE, isAligned);
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 1cc2977..c0917fe 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -162,6 +162,10 @@
* to enable satellite.
*/
public static final int TIMEOUT_TYPE_WAIT_FOR_SATELLITE_ENABLING_RESPONSE = 1;
+ /** This is used by CTS to override demo pointing aligned duration. */
+ public static final int TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS = 2;
+ /** This is used by CTS to override demo pointing not aligned duration. */
+ public static final int TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS = 3;
/** Key used to read/write OEM-enabled satellite provision status in shared preferences. */
private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY =
@@ -401,6 +405,8 @@
private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0);
private long mWaitTimeForSatelliteEnablingResponse;
+ private long mDemoPointingAlignedDurationMillis;
+ private long mDemoPointingNotAlignedDurationMillis;
/** Key used to read/write satellite system notification done in shared preferences. */
private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
@@ -528,6 +534,9 @@
null);
loadSatelliteSharedPreferences();
mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis();
+ mDemoPointingAlignedDurationMillis = getDemoPointingAlignedDurationMillisFromResources();
+ mDemoPointingNotAlignedDurationMillis =
+ getDemoPointingNotAlignedDurationMillisFromResources();
}
/**
@@ -2046,6 +2055,8 @@
logd("setDeviceAlignedWithSatellite: oemEnabledSatelliteFlag is disabled");
return;
}
+
+ DemoSimulator.getInstance().setDeviceAlignedWithSatellite(isAligned);
mDatagramController.setDeviceAlignedWithSatellite(isAligned);
}
@@ -2376,6 +2387,25 @@
}
/**
+ * This API can be used by only CTS to override the boolean configs used by the
+ * DatagramController module.
+ *
+ * @param enable Whether to enable or disable boolean config.
+ * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+ */
+ public boolean setDatagramControllerBooleanConfig(
+ boolean reset, int booleanType, boolean enable) {
+ if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+ logd("setDatagramControllerBooleanConfig: oemEnabledSatelliteFlag is disabled");
+ return false;
+ }
+ logd("setDatagramControllerBooleanConfig: reset=" + reset + ", booleanType="
+ + booleanType + ", enable=" + enable);
+ return mDatagramController.setDatagramControllerBooleanConfig(
+ reset, booleanType, enable);
+ }
+
+ /**
* This API can be used by only CTS to override timeout durations used by SatelliteController
* module.
*
@@ -2402,6 +2432,20 @@
mWaitTimeForSatelliteEnablingResponse = timeoutMillis;
}
logd("mWaitTimeForSatelliteEnablingResponse=" + mWaitTimeForSatelliteEnablingResponse);
+ } else if (timeoutType == TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS) {
+ if (reset) {
+ mDemoPointingAlignedDurationMillis =
+ getDemoPointingAlignedDurationMillisFromResources();
+ } else {
+ mDemoPointingAlignedDurationMillis = timeoutMillis;
+ }
+ } else if (timeoutType == TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS) {
+ if (reset) {
+ mDemoPointingNotAlignedDurationMillis =
+ getDemoPointingNotAlignedDurationMillisFromResources();
+ } else {
+ mDemoPointingNotAlignedDurationMillis = timeoutMillis;
+ }
} else {
logw("Invalid timeoutType=" + timeoutType);
return false;
@@ -4568,6 +4612,40 @@
}
}
+ private long getDemoPointingAlignedDurationMillisFromResources() {
+ long durationMillis = 15000L;
+ try {
+ durationMillis = mContext.getResources().getInteger(
+ R.integer.config_demo_pointing_aligned_duration_millis);
+ } catch (Resources.NotFoundException ex) {
+ loge("getPointingAlignedDurationMillis: ex=" + ex);
+ }
+
+ return durationMillis;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public long getDemoPointingAlignedDurationMillis() {
+ return mDemoPointingAlignedDurationMillis;
+ }
+
+ private long getDemoPointingNotAlignedDurationMillisFromResources() {
+ long durationMillis = 30000L;
+ try {
+ durationMillis = mContext.getResources().getInteger(
+ R.integer.config_demo_pointing_not_aligned_duration_millis);
+ } catch (Resources.NotFoundException ex) {
+ loge("getPointingNotAlignedDurationMillis: ex=" + ex);
+ }
+
+ return durationMillis;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public long getDemoPointingNotAlignedDurationMillis() {
+ return mDemoPointingNotAlignedDurationMillis;
+ }
+
private static void logd(@NonNull String log) {
Rlog.d(TAG, log);
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 2e99ae6..58260d1 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -42,6 +42,7 @@
import android.telephony.satellite.stub.ISatellite;
import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.SatelliteModemState;
import android.telephony.satellite.stub.SatelliteService;
import android.text.TextUtils;
import android.util.Pair;
@@ -64,6 +65,7 @@
@NonNull private static SatelliteModemInterface sInstance;
@NonNull private final Context mContext;
+ @NonNull private final DemoSimulator mDemoSimulator;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@NonNull protected final ExponentialBackoff mExponentialBackoff;
@NonNull private final Object mLock = new Object();
@@ -96,7 +98,14 @@
@NonNull private final RegistrantList mSatelliteSupportedStateChangedRegistrants =
new RegistrantList();
- @NonNull private final ISatelliteListener mListener = new ISatelliteListener.Stub() {
+ private class SatelliteListener extends ISatelliteListener.Stub {
+
+ private final boolean mIsDemoListener;
+
+ SatelliteListener(boolean isDemoListener) {
+ mIsDemoListener = isDemoListener;
+ }
+
@Override
public void onSatelliteProvisionStateChanged(boolean provisioned) {
mSatelliteProvisionStateChangedRegistrants.notifyResult(provisioned);
@@ -105,15 +114,19 @@
@Override
public void onSatelliteDatagramReceived(
android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
- logd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
- mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
- SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+ if (notifyResultIfExpectedListener()) {
+ logd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
+ mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
+ SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
+ }
}
@Override
public void onPendingDatagrams() {
- logd("onPendingDatagrams");
- mPendingDatagramsRegistrants.notifyResult(null);
+ if (notifyResultIfExpectedListener()) {
+ logd("onPendingDatagrams");
+ mPendingDatagramsRegistrants.notifyResult(null);
+ }
}
@Override
@@ -125,33 +138,39 @@
@Override
public void onSatelliteModemStateChanged(int state) {
- mSatelliteModemStateChangedRegistrants.notifyResult(
- SatelliteServiceUtils.fromSatelliteModemState(state));
- int datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
- switch (state) {
- case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
- datagramTransferState = SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
- break;
- case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
- datagramTransferState =
- SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
- break;
- case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
- datagramTransferState =
- SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
- break;
- case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
- // keep previous state as this could be retrying sending or receiving
- break;
+ if (notifyModemStateChanged(state)) {
+ mSatelliteModemStateChangedRegistrants.notifyResult(
+ SatelliteServiceUtils.fromSatelliteModemState(state));
+ int datagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
+ switch (state) {
+ case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
+ datagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
+ break;
+ case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
+ datagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
+ break;
+ case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
+ datagramTransferState =
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
+ break;
+ case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
+ // keep previous state as this could be retrying sending or receiving
+ break;
+ }
+ mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
}
- mDatagramTransferStateChangedRegistrants.notifyResult(datagramTransferState);
}
@Override
public void onNtnSignalStrengthChanged(
android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) {
- mNtnSignalStrengthChangedRegistrants.notifyResult(
- SatelliteServiceUtils.fromNtnSignalStrength(ntnSignalStrength));
+ if (notifyResultIfExpectedListener()) {
+ mNtnSignalStrengthChangedRegistrants.notifyResult(
+ SatelliteServiceUtils.fromNtnSignalStrength(ntnSignalStrength));
+ }
}
@Override
@@ -165,7 +184,22 @@
public void onSatelliteSupportedStateChanged(boolean supported) {
mSatelliteSupportedStateChangedRegistrants.notifyResult(supported);
}
- };
+
+ private boolean notifyResultIfExpectedListener() {
+ // Demo listener should notify results only during demo mode
+ // Vendor listener should notify result only during real mode
+ return mIsDemoListener == mSatelliteController.isDemoModeEnabled();
+ }
+
+ private boolean notifyModemStateChanged(int state) {
+ if (notifyResultIfExpectedListener()) {
+ return true;
+ }
+
+ return state == SatelliteModemState.SATELLITE_MODEM_STATE_OFF
+ || state == SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE;
+ }
+ }
/**
* @return The singleton instance of SatelliteModemInterface.
@@ -202,6 +236,7 @@
protected SatelliteModemInterface(@NonNull Context context,
SatelliteController satelliteController, @NonNull Looper looper) {
mContext = context;
+ mDemoSimulator = DemoSimulator.make(context, satelliteController);
mIsSatelliteServiceSupported = getSatelliteServiceSupport();
mSatelliteController = satelliteController;
mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
@@ -314,7 +349,11 @@
mSatelliteService = ISatellite.Stub.asInterface(service);
mExponentialBackoff.stop();
try {
- mSatelliteService.setSatelliteListener(mListener);
+ SatelliteListener vendorListener = new SatelliteListener(false);
+ mSatelliteService.setSatelliteListener(vendorListener);
+
+ SatelliteListener demoListener = new SatelliteListener(true);
+ mDemoSimulator.setSatelliteListener(demoListener);
} catch (RemoteException e) {
// TODO: Retry setSatelliteListener
logd("setSatelliteListener: RemoteException " + e);
@@ -584,19 +623,26 @@
@Nullable Message message) {
if (mSatelliteService != null) {
try {
- mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(enabled,
- new IIntegerConsumer.Stub() {
- @Override
- public void accept(int result) {
- int error = SatelliteServiceUtils.fromSatelliteError(result);
- logd("enableCellularModemWhileSatelliteModeIsOn: " + error);
- Binder.withCleanCallingIdentity(() -> {
- if (message != null) {
- sendMessageWithResult(message, null, error);
- }
- });
+ IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("enableCellularModemWhileSatelliteModeIsOn: " + error);
+ Binder.withCleanCallingIdentity(() -> {
+ if (message != null) {
+ sendMessageWithResult(message, null, error);
}
});
+ }
+ };
+
+ if (mSatelliteController.isDemoModeEnabled()) {
+ mDemoSimulator.enableCellularModemWhileSatelliteModeIsOn(
+ enabled, errorCallback);
+ } else {
+ mSatelliteService.enableCellularModemWhileSatelliteModeIsOn(
+ enabled, errorCallback);
+ }
} catch (RemoteException e) {
loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e);
if (message != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index cd3c05b..dc0cf47 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -450,6 +450,7 @@
}
unbindService();
stopNbIotInactivityTimer();
+ DemoSimulator.getInstance().onSatelliteModeOff();
notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
}
@@ -520,6 +521,7 @@
} else {
transitionTo(mIdleState);
}
+ DemoSimulator.getInstance().onSatelliteModeOn();
} else {
/*
* During the state transition from ENABLING to NOT_CONNECTED, modem might be
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
index ecec4cd..4896671 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -487,6 +487,7 @@
mDatagramDispatcherUT.setDemoMode(true);
mDatagramDispatcherUT.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
+ when(mMockDatagramController.waitForAligningToSatellite(false)).thenReturn(true);
int[] sosDatagramTypes = {DATAGRAM_TYPE1, DATAGRAM_TYPE4, DATAGRAM_TYPE5};
for (int datagramType : sosDatagramTypes) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
index 361c638..79d3657 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -372,6 +372,7 @@
mTestDemoModeDatagramReceiver.setDemoMode(true);
mTestDemoModeDatagramReceiver.setDuration(TEST_EXPIRE_TIMER_SATELLITE_ALIGN);
mTestDemoModeDatagramReceiver.setDeviceAlignedWithSatellite(false);
+ when(mMockDatagramController.waitForAligningToSatellite(false)).thenReturn(true);
when(mMockDatagramController.popDemoModeDatagram()).thenReturn(mDatagram);
mTestDemoModeDatagramReceiver.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
new file mode 100644
index 0000000..319e39f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.satellite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NtnSignalStrength;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for DemoSimulator
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DemoSimulatorTest extends TelephonyTest {
+ private static final String TAG = "DemoSimulatorTest";
+ private static final long TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS = 200L;
+ private static final long TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS = 300L;
+ private static final String STATE_POWER_OFF = "PowerOffState";
+ private static final String STATE_NOT_CONNECTED = "NotConnectedState";
+ private static final String STATE_CONNECTED = "ConnectedState";
+
+ private TestDemoSimulator mTestDemoSimulator;
+ @Mock private ISatelliteListener mISatelliteListener;
+
+ @Mock private SatelliteController mMockSatelliteController;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockSatelliteController.isDemoModeEnabled()).thenReturn(true);
+ when(mMockSatelliteController.getDemoPointingAlignedDurationMillis()).thenReturn(
+ TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS);
+ when(mMockSatelliteController.getDemoPointingNotAlignedDurationMillis()).thenReturn(
+ TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS);
+
+ mTestDemoSimulator = new TestDemoSimulator(mContext, Looper.myLooper(),
+ mMockSatelliteController);
+ mTestDemoSimulator.setSatelliteListener(mISatelliteListener);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testInitialState() {
+ assertNotNull(mTestDemoSimulator);
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+ }
+
+ @Test
+ public void testStateTransition() {
+ // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+ moveToConnectedState();
+
+ // Device is not aligned with satellite. EVENT_DEVICE_NOT_ALIGNED timer should start
+ mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+ processAllMessages();
+ assertTrue(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+
+ // After timeout, DemoSimulator should move to NOT_CONNECTED state.
+ moveTimeForward(TEST_DEVICE_POINTING_NOT_ALIGNED_DURATION_MILLIS);
+ processAllMessages();
+ assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+ // Satellite mode is OFF. DemoSimulator should move to POWER_OFF state.
+ mTestDemoSimulator.onSatelliteModeOff();
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+ }
+
+ @Test
+ public void testNotConnectedState_enter() throws Exception {
+ clearInvocations(mISatelliteListener);
+
+ // State transitions: POWER_OFF -> NOT_CONNECTED
+ moveToNotConnectedState();
+
+ verify(mISatelliteListener).onSatelliteModemStateChanged(
+ SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+ ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
+ NtnSignalStrength.class);
+ verify(mISatelliteListener).onNtnSignalStrengthChanged(ntnSignalStrength.capture());
+ assertEquals(0, ntnSignalStrength.getValue().signalStrengthLevel);
+ }
+
+ @Test
+ public void testNotConnectedState() {
+ // State transitions: POWER_OFF -> NOT_CONNECTED
+ moveToNotConnectedState();
+
+ // Device is aligned with satellite. EVENT_DEVICE_ALIGNED timer should start.
+ mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+ processAllMessages();
+ assertTrue(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+
+ // Device is not aligned with satellite. EVENT_DEVICE_ALIGNED messages should be removed.
+ mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+ processAllMessages();
+ assertFalse(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+ assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+ // Satellite mode is OFF. DemoSimulator should move to POWER_OFF state.
+ mTestDemoSimulator.onSatelliteModeOff();
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+ }
+
+ @Test
+ public void testConnectedState_enter() throws Exception {
+ clearInvocations(mISatelliteListener);
+
+ // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+ moveToConnectedState();
+
+ verify(mISatelliteListener).onSatelliteModemStateChanged(
+ SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+ verify(mISatelliteListener).onSatelliteModemStateChanged(
+ SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+ ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
+ NtnSignalStrength.class);
+ verify(mISatelliteListener, times(2))
+ .onNtnSignalStrengthChanged(ntnSignalStrength.capture());
+ NtnSignalStrength ntnSignalStrengthOnConnected = ntnSignalStrength.getAllValues().get(1);
+ assertEquals(2, ntnSignalStrengthOnConnected.signalStrengthLevel);
+ }
+
+ @Test
+ public void testConnectedState() {
+ // State transitions: POWER_OFF -> NOT_CONNECTED -> CONNECTED
+ moveToConnectedState();
+
+ // Device is not aligned with satellite. EVENT_DEVICE_NOT_ALIGNED timer should start
+ mTestDemoSimulator.setDeviceAlignedWithSatellite(false);
+ processAllMessages();
+ assertTrue(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+
+ // Device is aligned with satellite before timeout.
+ // EVENT_DEVICE_NOT_ALIGNED messages should be removed.
+ mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+ processAllMessages();
+ assertFalse(mTestDemoSimulator.isDeviceNotAlignedTimerStarted());
+ assertEquals(STATE_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+ // Satellite mode is off. DemoSimulator should move to POWER_OFF state
+ mTestDemoSimulator.onSatelliteModeOff();
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+ }
+
+ private void moveToNotConnectedState() {
+ // DemoSimulator will initially be in POWER_OFF state.
+ assertNotNull(mTestDemoSimulator);
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+
+ // Satellite mode is ON. DemoSimulator should move to NOT_CONNECTED state.
+ mTestDemoSimulator.onSatelliteModeOn();
+ processAllMessages();
+ assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+ }
+
+ private void moveToConnectedState() {
+ // DemoSimulator will initially be in POWER_OFF state.
+ assertNotNull(mTestDemoSimulator);
+ processAllMessages();
+ assertEquals(STATE_POWER_OFF, mTestDemoSimulator.getCurrentStateName());
+
+ // Satellite mode is ON. DemoSimulator should move to NOT_CONNECTED state.
+ mTestDemoSimulator.onSatelliteModeOn();
+ processAllMessages();
+ assertEquals(STATE_NOT_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+
+ // Device is aligned with satellite. EVENT_DEVICE_ALIGNED timer should start.
+ mTestDemoSimulator.setDeviceAlignedWithSatellite(true);
+ processAllMessages();
+ assertTrue(mTestDemoSimulator.isDeviceAlignedTimerStarted());
+
+ // After timeout, DemoSimulator should move to CONNECTED state.
+ moveTimeForward(TEST_DEVICE_POINTING_ALIGNED_DURATION_MILLIS);
+ processAllMessages();
+ assertEquals(STATE_CONNECTED, mTestDemoSimulator.getCurrentStateName());
+ }
+
+ private static class TestDemoSimulator extends DemoSimulator {
+
+ TestDemoSimulator(@NonNull Context context, @NonNull Looper looper,
+ @NonNull SatelliteController satelliteController) {
+ super(context, looper, satelliteController);
+ }
+
+ String getCurrentStateName() {
+ return getCurrentState().getName();
+ }
+
+ boolean isDeviceAlignedTimerStarted() {
+ return hasMessages(EVENT_DEVICE_ALIGNED);
+ }
+
+ boolean isDeviceNotAlignedTimerStarted() {
+ return hasMessages(EVENT_DEVICE_NOT_ALIGNED);
+ }
+ }
+}