Merge "Configure satellite network capabilities" into main
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 99bc2af..c4667e8 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -124,6 +124,16 @@
     }
 
     /**
+     * @return The singleton instance of DatagramDispatcher.
+     */
+    public static DatagramDispatcher getInstance() {
+        if (sInstance == null) {
+            loge("DatagramDispatcher was not yet initialized.");
+        }
+        return sInstance;
+    }
+
+    /**
      * Create a DatagramDispatcher to send satellite datagrams.
      *
      * @param context The Context for the DatagramDispatcher.
@@ -570,6 +580,22 @@
         }
     }
 
+    /** Return pending user messages count */
+    public int getPendingUserMessagesCount() {
+        synchronized (mLock) {
+            int pendingUserMessagesCount = 0;
+            for (Entry<Long, SendSatelliteDatagramArgument> entry :
+                    mPendingNonEmergencyDatagramsMap.entrySet()) {
+                SendSatelliteDatagramArgument argument = entry.getValue();
+                if (argument.datagramType != SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+                    pendingUserMessagesCount += 1;
+                }
+            }
+            pendingUserMessagesCount += mPendingEmergencyDatagramsMap.size();
+            return pendingUserMessagesCount;
+        }
+    }
+
     /**
      * Posts the specified command to be executed on the main thread and returns immediately.
      *
@@ -600,11 +626,12 @@
         if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType,
                     mIsDemoMode);
-            mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram();
+            mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType);
         } else {
             mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType,
                     mIsDemoMode);
-            mSessionMetricsStats.addCountOfFailedOutgoingDatagram();
+            mSessionMetricsStats.addCountOfFailedOutgoingDatagram(argument.datagramType,
+                    resultCode);
         }
     }
 
@@ -809,8 +836,7 @@
             }
 
             // Abort sending all the pending datagrams
-            abortSendingPendingDatagrams(argument.subId,
-                    SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+            abortSendingPendingDatagrams(argument.subId, SATELLITE_RESULT_MODEM_TIMEOUT);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 9cc342a..3a63ac3 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -2912,6 +2912,20 @@
     }
 
     /**
+     * Request to get the {@link SatelliteSessionStats} of the satellite service.
+     *
+     * @param subId The subId of the subscription to the satellite session stats for.
+     * @param result The result receiver that returns the {@link SatelliteSessionStats}
+     *               if the request is successful or an error code if the request failed.
+     */
+    public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            return;
+        }
+        mSessionMetricsStats.requestSatelliteSessionStats(subId, result);
+    }
+
+    /**
      * Get the carrier-enabled emergency call wait for connection timeout millis
      */
     public long getCarrierEmergencyCallWaitForConnectionTimeoutMillis() {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 8a26fd2..9f025db 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -187,6 +187,11 @@
             mSatelliteSupportedStateChangedRegistrants.notifyResult(supported);
         }
 
+        @Override
+        public void onRegistrationFailure(int causeCode) {
+            // TO-DO notify registrants
+        }
+
         private boolean notifyResultIfExpectedListener() {
             // Demo listener should notify results only during demo mode
             // Vendor listener should notify result only during real 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 73ede60..65181c0 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -17,13 +17,18 @@
 package com.android.internal.telephony.satellite.metrics;
 
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.ResultReceiver;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteSessionStats;
 import android.util.Log;
 
 import com.android.internal.telephony.metrics.SatelliteStats;
+import com.android.internal.telephony.satellite.DatagramDispatcher;
 
 /**
  * Stats to log to satellite session metrics
@@ -41,6 +46,8 @@
     private int mSessionDurationSec;
     private int mCountOfSuccessfulOutgoingDatagram;
     private int mCountOfFailedOutgoingDatagram;
+    private int mCountOfTimedOutUserMessagesWaitingForConnection;
+    private int mCountOfTimedOutUserMessagesWaitingForAck;
     private int mCountOfSuccessfulIncomingDatagram;
     private int mCountOfIncomingDatagramFailed;
     private boolean mIsDemoMode;
@@ -111,7 +118,13 @@
     }
 
     /** Increase the count of successful outgoing datagram transmission. */
-    public SessionMetricsStats addCountOfSuccessfulOutgoingDatagram() {
+    public SessionMetricsStats addCountOfSuccessfulOutgoingDatagram(
+            @NonNull @SatelliteManager.DatagramType int datagramType) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
         mCountOfSuccessfulOutgoingDatagram++;
         logd("addCountOfSuccessfulOutgoingDatagram: current count="
                 + mCountOfSuccessfulOutgoingDatagram);
@@ -119,9 +132,51 @@
     }
 
     /** Increase the count of failed outgoing datagram transmission. */
-    public SessionMetricsStats addCountOfFailedOutgoingDatagram() {
+    public SessionMetricsStats addCountOfFailedOutgoingDatagram(
+            @NonNull @SatelliteManager.DatagramType int datagramType,
+            @NonNull @SatelliteManager.SatelliteResult int resultCode) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
         mCountOfFailedOutgoingDatagram++;
         logd("addCountOfFailedOutgoingDatagram: current count=" + mCountOfFailedOutgoingDatagram);
+
+        if (resultCode == SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE) {
+            addCountOfTimedOutUserMessagesWaitingForConnection(datagramType);
+        } else if (resultCode == SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT) {
+            addCountOfTimedOutUserMessagesWaitingForAck(datagramType);
+        }
+
+        return this;
+    }
+
+    /** Increase the count of user messages that timed out waiting for connection. */
+    private SessionMetricsStats addCountOfTimedOutUserMessagesWaitingForConnection(
+            @NonNull @SatelliteManager.DatagramType int datagramType) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
+        mCountOfTimedOutUserMessagesWaitingForConnection++;
+        logd("addCountOfTimedOutUserMessagesWaitingForConnection: current count="
+                + mCountOfTimedOutUserMessagesWaitingForConnection);
+        return this;
+    }
+
+    /** Increase the count of user messages that timed out waiting for ack. */
+    private SessionMetricsStats addCountOfTimedOutUserMessagesWaitingForAck(
+            @NonNull @SatelliteManager.DatagramType int datagramType) {
+        if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+            // Ignore KEEP_ALIVE messages
+            return this;
+        }
+
+        mCountOfTimedOutUserMessagesWaitingForAck++;
+        logd("addCountOfTimedOutUserMessagesWaitingForAck: current count="
+                + mCountOfTimedOutUserMessagesWaitingForAck);
         return this;
     }
 
@@ -180,6 +235,23 @@
         initializeSessionMetricsParam();
     }
 
+    /** Returns {@link SatelliteSessionStats} of the satellite service. */
+    public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) {
+        Bundle bundle = new Bundle();
+        SatelliteSessionStats sessionStats = new SatelliteSessionStats.Builder()
+                .setCountOfSuccessfulUserMessages(mCountOfSuccessfulOutgoingDatagram)
+                .setCountOfUnsuccessfulUserMessages(mCountOfFailedOutgoingDatagram)
+                .setCountOfTimedOutUserMessagesWaitingForConnection(
+                        mCountOfTimedOutUserMessagesWaitingForConnection)
+                .setCountOfTimedOutUserMessagesWaitingForAck(
+                        mCountOfTimedOutUserMessagesWaitingForAck)
+                .setCountOfUserMessagesInQueueToBeSent(
+                        DatagramDispatcher.getInstance().getPendingUserMessagesCount())
+                .build();
+        bundle.putParcelable(SatelliteManager.KEY_SESSION_STATS, sessionStats);
+        result.send(SATELLITE_RESULT_SUCCESS, bundle);
+    }
+
     /** Returns the processing time for satellite session initialization. */
     public long getSessionInitializationProcessingTimeMillis() {
         return mInitializationProcessingTimeMillis;
@@ -199,6 +271,8 @@
         mSessionDurationSec = 0;
         mCountOfSuccessfulOutgoingDatagram = 0;
         mCountOfFailedOutgoingDatagram = 0;
+        mCountOfTimedOutUserMessagesWaitingForConnection = 0;
+        mCountOfTimedOutUserMessagesWaitingForAck = 0;
         mCountOfSuccessfulIncomingDatagram = 0;
         mCountOfIncomingDatagramFailed = 0;
         mIsDemoMode = false;
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 dba288e..1bb45b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -238,7 +238,8 @@
                             eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                             eq(SATELLITE_RESULT_SUCCESS));
             verifyNoMoreInteractions(mMockDatagramController);
-            verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType));
             verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
                     any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
@@ -279,7 +280,8 @@
             assertThat(mResultListener.peek()).isEqualTo(
                     SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE);
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
 
             mResultListener.clear();
             mDatagramDispatcherUT.onSatelliteModemStateChanged(
@@ -311,7 +313,8 @@
             assertThat(mResultListener.peek()).isEqualTo(
                     SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
         }
     }
 
@@ -372,7 +375,8 @@
             verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
                     any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
             assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
-            verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(anyInt());
             clearInvocations(mMockSatelliteModemInterface);
             clearInvocations(mMockDatagramController);
             clearInvocations(mMockSessionMetricsStats);
@@ -406,7 +410,8 @@
                     any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
             verify(mMockSatelliteModemInterface).abortSendingSatelliteDatagrams(any(Message.class));
             assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_MODEM_TIMEOUT);
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
 
             clearInvocations(mMockSatelliteModemInterface);
             clearInvocations(mMockDatagramController);
@@ -453,7 +458,8 @@
 
         assertThat(mResultListener.peek()).isEqualTo(
                 SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
-        verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+        verify(mMockSessionMetricsStats, times(1))
+                .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
     }
 
     @Test
@@ -494,7 +500,8 @@
                             eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                             eq(SATELLITE_RESULT_SUCCESS));
             assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS);
-            verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(eq(datagramType));
             mDatagramDispatcherUT.setDemoMode(false);
             mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
         }
@@ -542,7 +549,8 @@
             verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
             verify(mMockDatagramController, never()).pushDemoModeDatagram(
                     anyInt(), any(SatelliteDatagram.class));
-            verify(mMockSessionMetricsStats, times(1)).addCountOfFailedOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
         }
 
         mDatagramDispatcherUT.setDemoMode(false);
@@ -587,7 +595,8 @@
                 .updateSendStatus(eq(SUB_ID), eq(DATAGRAM_TYPE2),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                         eq(SATELLITE_RESULT_SUCCESS));
-        verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+        verify(mMockSessionMetricsStats, times(1))
+                .addCountOfSuccessfulOutgoingDatagram(eq(DATAGRAM_TYPE2));
 
         mDatagramDispatcherUT.setDemoMode(false);
         mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false);
@@ -699,7 +708,8 @@
             verify(mMockDatagramController).pushDemoModeDatagram(
                     anyInt(), any(SatelliteDatagram.class));
             verify(mMockDatagramController).pollPendingSatelliteDatagrams(anyInt(), any());
-            verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulOutgoingDatagram();
+            verify(mMockSessionMetricsStats, times(1))
+                    .addCountOfSuccessfulOutgoingDatagram(anyInt());
 
             // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is
             // false