[VZW P2P] Add metric for Carrier Roaming NB-IoT NTN module, maxInactivityDurationSec, to track the maximum user inactivity duration of a satellite session.

Bug: 383033991
Test: atest
FLAG: com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1bcb3d701e907580c8ff6bd2928f437dfd939bfd)
Merged-In: I29f0bd4ef55606a7cd8109457ec31f06c4da0de9
Change-Id: I29f0bd4ef55606a7cd8109457ec31f06c4da0de9
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index ca68912..8d87a27 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -748,6 +748,7 @@
     optional int32 count_of_auto_exit_due_to_tn_network = 17;
     optional bool is_emergency = 18;
     optional bool is_ntn_only_carrier = 19;
+    optional int32 max_inactivity_duration_sec = 20;
 }
 
 message SatelliteIncomingDatagram {
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 5ef4c5b..8ef5a8f 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -1507,7 +1507,8 @@
                 satelliteSession.countOfAutoExitDueToScreenOff,
                 satelliteSession.countOfAutoExitDueToTnNetwork,
                 satelliteSession.isEmergency,
-                satelliteSession.isNtnOnlyCarrier);
+                satelliteSession.isNtnOnlyCarrier,
+                satelliteSession.maxInactivityDurationSec);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteIncomingDatagram stats) {
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index f828876..581d54c 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -2347,7 +2347,8 @@
                     == key.countOfSatelliteNotificationDisplayed
                     && stats.countOfAutoExitDueToScreenOff == key.countOfAutoExitDueToScreenOff
                     && stats.countOfAutoExitDueToTnNetwork == key.countOfAutoExitDueToTnNetwork
-                    && stats.isEmergency == key.isEmergency) {
+                    && stats.isEmergency == key.isEmergency
+                    && stats.maxInactivityDurationSec == key.maxInactivityDurationSec) {
                 return stats;
             }
         }
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
index 4513f6c..c17c8ab 100644
--- a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -804,6 +804,7 @@
         private final int mCountOfAutoExitDueToScreenOff;
         private final int mCountOfAutoExitDueToTnNetwork;
         private final boolean mIsEmergency;
+        private final int mMaxInactivityDurationSec;
         private final boolean mIsNtnOnlyCarrier;
 
         private SatelliteSessionParams(Builder builder) {
@@ -828,6 +829,7 @@
             this.mCountOfAutoExitDueToTnNetwork = builder.mCountOfAutoExitDueToTnNetwork;
             this.mIsEmergency = builder.mIsEmergency;
             this.mIsNtnOnlyCarrier = builder.mIsNtnOnlyCarrier;
+            this.mMaxInactivityDurationSec = builder.mMaxInactivityDurationSec;
         }
 
         public int getSatelliteServiceInitializationResult() {
@@ -902,6 +904,10 @@
             return mIsNtnOnlyCarrier;
         }
 
+        public int getMaxInactivityDurationSec() {
+            return mMaxInactivityDurationSec;
+        }
+
         /**
          * A builder class to create {@link SatelliteSessionParams} data structure class
          */
@@ -925,6 +931,7 @@
             private int mCountOfAutoExitDueToTnNetwork = -1;
             private boolean mIsEmergency = false;
             private boolean mIsNtnOnlyCarrier = false;
+            private int mMaxInactivityDurationSec = -1;
 
             /**
              * Sets satelliteServiceInitializationResult value of {@link SatelliteSession}
@@ -1058,6 +1065,12 @@
                 return this;
             }
 
+            /** Sets the max user inactivity duration in seconds. */
+            public Builder setMaxInactivityDurationSec(int maxInactivityDurationSec) {
+                this.mMaxInactivityDurationSec = maxInactivityDurationSec;
+                return this;
+            }
+
             /**
              * Returns SessionParams, which contains whole component of
              * {@link SatelliteSession} atom
@@ -1090,6 +1103,7 @@
                     + ", CountOfAutoExitDueToTnNetwork" + mCountOfAutoExitDueToTnNetwork
                     + ", IsEmergency=" + mIsEmergency
                     + ", IsNtnOnlyCarrier=" + mIsNtnOnlyCarrier
+                    + ", MaxInactivityDurationSec=" + mMaxInactivityDurationSec
                     + ")";
         }
     }
@@ -2715,6 +2729,7 @@
         proto.countOfAutoExitDueToTnNetwork = param.getCountOfAutoExitDueToTnNetwork();
         proto.isEmergency = param.getIsEmergency();
         proto.isNtnOnlyCarrier = param.isNtnOnlyCarrier();
+        proto.maxInactivityDurationSec = param.getMaxInactivityDurationSec();
         mAtomsStorage.addSatelliteSessionStats(proto);
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 5cf3c13..a002457 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -21,6 +21,7 @@
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE;
@@ -30,6 +31,7 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE;
@@ -143,6 +145,7 @@
     private static final int DEFAULT_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC = 30;
     private static final int DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC = 180;
     private static final int DEFAULT_ESOS_INACTIVITY_TIMEOUT_SEC = 600;
+    private static final long UNDEFINED_TIMESTAMP = 0L;
 
     @NonNull private final ExponentialBackoff mExponentialBackoff;
     @NonNull private final Object mLock = new Object();
@@ -181,6 +184,12 @@
     // Interested in screen off, so use default value true
     boolean mIsScreenOn = true;
     private boolean mIsDeviceAlignedWithSatellite = false;
+    private long mInactivityStartTimestamp = UNDEFINED_TIMESTAMP;
+    private DatagramTransferState mLastDatagramTransferState =
+            new DatagramTransferState(
+                    SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN,
+                    SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN,
+                    DATAGRAM_TYPE_UNKNOWN);
 
     @NonNull private final SatelliteController mSatelliteController;
     @NonNull private final DatagramController mDatagramController;
@@ -334,11 +343,21 @@
             @SatelliteManager.SatelliteDatagramTransferState int sendState,
             @SatelliteManager.SatelliteDatagramTransferState int receiveState,
             @SatelliteManager.DatagramType int datagramType) {
-        sendMessage(EVENT_DATAGRAM_TRANSFER_STATE_CHANGED,
-                new DatagramTransferState(sendState, receiveState, datagramType));
+        DatagramTransferState datagramTransferState =
+                new DatagramTransferState(sendState, receiveState, datagramType);
+        mLastDatagramTransferState = datagramTransferState;
+
+        sendMessage(EVENT_DATAGRAM_TRANSFER_STATE_CHANGED, datagramTransferState);
+
         if (sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING) {
             mIsSendingTriggeredDuringTransferringState.set(true);
         }
+
+        if (datagramTransferState.isIdle()) {
+            checkForInactivity();
+        } else {
+            endUserInactivity();
+        }
     }
 
     /**
@@ -556,11 +575,13 @@
         if (mIsDeviceAlignedWithSatellite) {
             stopEsosInactivityTimer();
             stopP2pSmsInactivityTimer();
+            endUserInactivity();
         } else {
             if (mCurrentState == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
                 evaluateStartingEsosInactivityTimer();
                 evaluateStartingP2pSmsInactivityTimer();
             }
+            checkForInactivity();
         }
     }
 
@@ -591,6 +612,7 @@
     public void cleanUpResource() {
         plogd("cleanUpResource");
         mIsDeviceAlignedWithSatellite = false;
+        mInactivityStartTimestamp = UNDEFINED_TIMESTAMP;
         unregisterForScreenStateChanged();
         if (mAlarmManager != null) {
             mAlarmManager.cancel(mAlarmListener);
@@ -648,6 +670,11 @@
             this.receiveState = receiveState;
             this.datagramType = datagramType;
         }
+
+        public boolean isIdle() {
+            return (sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
+                    && receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        }
     }
 
     private class UnavailableState extends State {
@@ -678,6 +705,7 @@
             mIsSendingTriggeredDuringTransferringState.set(false);
             unbindService();
             stopNbIotInactivityTimer();
+            endUserInactivity();
             DemoSimulator.getInstance().onSatelliteModeOff();
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
 
@@ -1834,6 +1862,50 @@
         return hasMessages(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT);
     }
 
+    /**
+     * Initializes the inactivity start timestamp.
+     *
+     * <p>This method is called when 1) the datagram transfer state changes to idle or 2) the
+     * device is unaligned with the satellite.
+     */
+    private void checkForInactivity() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+
+        // If the inactivity start timestamp is not undefined, it means the inactivity has already
+        // started.
+        if (mInactivityStartTimestamp != UNDEFINED_TIMESTAMP) {
+            return;
+        }
+
+        boolean isInactive = mLastDatagramTransferState.isIdle() && !mIsDeviceAlignedWithSatellite;
+        if (isInactive) {
+            mInactivityStartTimestamp = SystemClock.elapsedRealtime();
+        }
+    }
+
+    /**
+     * Updates the max inactivity duration session metric.
+     *
+     * <p>This method is called when 1) the datagram transfer state changes to not idle, 2) the
+     * device is aligned with the satellite, or 3) modem state moves to PowerOffState.
+     */
+    private void endUserInactivity() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("endUserInactivity: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        if (mInactivityStartTimestamp != UNDEFINED_TIMESTAMP) {
+            long inactivityDurationMs = SystemClock.elapsedRealtime() - mInactivityStartTimestamp;
+            int inactivityDurationSec = (int) (inactivityDurationMs / 1000);
+            mSessionMetricsStats.updateMaxInactivityDurationSec(inactivityDurationSec);
+
+            mInactivityStartTimestamp = UNDEFINED_TIMESTAMP;
+        }
+    }
+
     private void handleEventScreenOffInactivityTimerTimedOut() {
         if (mSatelliteController.getRequestIsEmergency()) {
             loge("handleEventScreenOffInactivityTimerTimedOut: Emergency mode");
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
index c0f8cc1..6b10ff1 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -63,6 +63,7 @@
     private int mCountOfAutoExitDueToTnNetwork;
     private boolean mIsEmergency;
     private boolean mIsNtnOnlyCarrier;
+    private int mMaxInactivityDurationSec;
     private SatelliteSessionStats mDatagramStats;
 
     private SessionMetricsStats() {
@@ -275,6 +276,18 @@
         return this;
     }
 
+    /** Updates the max inactivity duration session metric. */
+    public SessionMetricsStats updateMaxInactivityDurationSec(int inactivityDurationSec) {
+        if (inactivityDurationSec > mMaxInactivityDurationSec) {
+            mMaxInactivityDurationSec = inactivityDurationSec;
+        }
+        logd("updateMaxInactivityDurationSec: latest inactivty duration (sec)="
+                + inactivityDurationSec
+                + ", max inactivity duration="
+                + mMaxInactivityDurationSec);
+        return this;
+    }
+
     /** Report the session metrics atoms to PersistAtomsStorage in telephony. */
     public void reportSessionMetrics() {
         SatelliteStats.SatelliteSessionParams sessionParams =
@@ -298,6 +311,7 @@
                         .setCountOfAutoExitDueToTnNetwork(mCountOfAutoExitDueToTnNetwork)
                         .setIsEmergency(mIsEmergency)
                         .setIsNtnOnlyCarrier(mIsNtnOnlyCarrier)
+                        .setMaxInactivityDurationSec(mMaxInactivityDurationSec)
                         .build();
         logd("reportSessionMetrics: " + sessionParams.toString());
         SatelliteStats.getInstance().onSatelliteSessionMetrics(sessionParams);
@@ -357,6 +371,7 @@
         mCountOfAutoExitDueToTnNetwork = 0;
         mIsEmergency = false;
         mIsNtnOnlyCarrier = false;
+        mMaxInactivityDurationSec = 0;
     }
 
     public void resetSessionStatsShadowCounters() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index 2645dbf..1c0febf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -1216,6 +1216,7 @@
         mSatelliteSession1.countOfAutoExitDueToScreenOff = 6;
         mSatelliteSession1.countOfAutoExitDueToTnNetwork = 7;
         mSatelliteSession1.isEmergency = true;
+        mSatelliteSession1.maxInactivityDurationSec = 8;
 
         mSatelliteSession2 = new SatelliteSession();
         mSatelliteSession2.satelliteServiceInitializationResult =
@@ -1239,6 +1240,7 @@
         mSatelliteSession2.countOfAutoExitDueToScreenOff = 60;
         mSatelliteSession2.countOfAutoExitDueToTnNetwork = 70;
         mSatelliteSession2.isEmergency = false;
+        mSatelliteSession2.maxInactivityDurationSec = 80;
 
         mSatelliteSessions =
                 new SatelliteSession[] {
@@ -6060,32 +6062,33 @@
         int actualCount = 0;
         for (SatelliteSession stats : tested) {
             if (stats.satelliteServiceInitializationResult
-                    == expectedStats.satelliteServiceInitializationResult
+                            == expectedStats.satelliteServiceInitializationResult
                     && stats.satelliteTechnology == expectedStats.satelliteTechnology
                     && stats.satelliteServiceTerminationResult
-                        == expectedStats.satelliteServiceTerminationResult
+                            == expectedStats.satelliteServiceTerminationResult
                     && stats.initializationProcessingTimeMillis
-                        == expectedStats.initializationProcessingTimeMillis
+                            == expectedStats.initializationProcessingTimeMillis
                     && stats.terminationProcessingTimeMillis
-                        == expectedStats.terminationProcessingTimeMillis
+                            == expectedStats.terminationProcessingTimeMillis
                     && stats.sessionDurationSeconds == expectedStats.sessionDurationSeconds
                     && stats.countOfOutgoingDatagramSuccess
-                        == expectedStats.countOfOutgoingDatagramSuccess
+                            == expectedStats.countOfOutgoingDatagramSuccess
                     && stats.countOfOutgoingDatagramFailed
-                        == expectedStats.countOfOutgoingDatagramFailed
+                            == expectedStats.countOfOutgoingDatagramFailed
                     && stats.countOfIncomingDatagramSuccess
-                        == expectedStats.countOfIncomingDatagramSuccess
+                            == expectedStats.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed
-                        == expectedStats.countOfIncomingDatagramFailed
+                            == expectedStats.countOfIncomingDatagramFailed
                     && stats.isDemoMode == expectedStats.isDemoMode
                     && stats.carrierId == expectedStats.carrierId
                     && stats.countOfSatelliteNotificationDisplayed
-                    == expectedStats.countOfSatelliteNotificationDisplayed
+                            == expectedStats.countOfSatelliteNotificationDisplayed
                     && stats.countOfAutoExitDueToScreenOff
-                    == expectedStats.countOfAutoExitDueToScreenOff
+                            == expectedStats.countOfAutoExitDueToScreenOff
                     && stats.countOfAutoExitDueToTnNetwork
-                    == expectedStats.countOfAutoExitDueToTnNetwork
-                    && stats.isEmergency == expectedStats.isEmergency) {
+                            == expectedStats.countOfAutoExitDueToTnNetwork
+                    && stats.isEmergency == expectedStats.isEmergency
+                    && stats.maxInactivityDurationSec == expectedStats.maxInactivityDurationSec) {
                 actualCount = stats.count;
             }
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
index 51ae7a7..e981e88 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -254,6 +254,7 @@
                         .setCountOfAutoExitDueToScreenOff(7)
                         .setCountOfAutoExitDueToTnNetwork(3)
                         .setIsEmergency(true)
+                        .setMaxInactivityDurationSec(10)
                         .build();
 
         mSatelliteStats.onSatelliteSessionMetrics(param);
@@ -283,6 +284,7 @@
         assertEquals(param.getCountOfAutoExitDueToScreenOff(), stats.countOfAutoExitDueToScreenOff);
         assertEquals(param.getCountOfAutoExitDueToTnNetwork(), stats.countOfAutoExitDueToTnNetwork);
         assertEquals(param.getIsEmergency(), stats.isEmergency);
+        assertEquals(param.getMaxInactivityDurationSec(), stats.maxInactivityDurationSec);
 
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index b5b639e..766d1fb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -24,12 +24,14 @@
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static org.junit.Assert.assertEquals;
@@ -2166,6 +2168,86 @@
                 eq(false), eq(false), eq(false), any(IIntegerConsumer.Stub.class));
     }
 
+    @Test
+    public void testSetDeviceAlignedWithSatellite_updatesMaxInactivityDuration() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        setUserInactivityStart();
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        verify(mMockSessionMetricsStats, times(1)).updateMaxInactivityDurationSec(anyInt());
+    }
+
+    @Test
+    public void
+            testSetDeviceAlignedWithSatellite_setsInactivityStartTimestampUndefinedAfterUpdate() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        setUserInactivityStart();
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        // There should be only one call to updateMaxInactivityDurationSec since the inactivity
+        // start timestamp is reset to undefined.
+        verify(mMockSessionMetricsStats, times(1)).updateMaxInactivityDurationSec(anyInt());
+    }
+
+    @Test
+    public void
+            testSetDeviceAlignedWithSatellite_noInactivityStart_noUpdateMaxInactivityDuration() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        verify(mMockSessionMetricsStats, times(0)).updateMaxInactivityDurationSec(anyInt());
+    }
+
+    @Test
+    public void testSetDeviceAlignedWithSatellite_flagOff_noUpdateMaxInactivityDuration() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
+        setUserInactivityStart();
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        verify(mMockSessionMetricsStats, times(0)).updateMaxInactivityDurationSec(anyInt());
+    }
+
+    @Test
+    public void testOnDatagramTransferStateChanged_notIdle_updatesMaxInactivityDuration() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        setUserInactivityStart();
+
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                DATAGRAM_TYPE_UNKNOWN);
+
+        // Since both the send and receive datagram transfer state is not idle, the max inactivity
+        // duration should be updated.
+        verify(mMockSessionMetricsStats, times(1)).updateMaxInactivityDurationSec(anyInt());
+    }
+
+    @Test
+    public void testOnDatagramTransferStateChanged_idle_updatesMaxInactivityDuration() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        setUserInactivityStart();
+        moveToIdleState();
+
+        moveToPowerOffState();
+
+        verify(mMockSessionMetricsStats, times(1)).updateMaxInactivityDurationSec(anyInt());
+    }
+
+    private void setUserInactivityStart() {
+        // Set DatagramTransferState to idle and unaligned with satellite to define inactivity start
+        // timestamp
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                DATAGRAM_TYPE_UNKNOWN);
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false);
+    }
+
     private void verifyEsosP2pSmsInactivityTimer(boolean esosTimer, boolean p2pSmsTimer) {
         assertEquals(mTestSatelliteSessionController.isEsosInActivityTimerStarted(), esosTimer);
         assertEquals(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted(),