TestApp for Satellite Module

TestApp for SatelliteService

Bug: 276824915
Test: mm
Change-Id: I7d4bdd3923ad74ce6b62279ecd21a7a485f0f6d4
diff --git a/testapps/TestSatelliteApp/Android.bp b/testapps/TestSatelliteApp/Android.bp
new file mode 100644
index 0000000..fd3c424
--- /dev/null
+++ b/testapps/TestSatelliteApp/Android.bp
@@ -0,0 +1,17 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+    name: "SatelliteTestApp",
+    system_ext_specific: true,
+    platform_apis: true,
+    manifest: "AndroidManifest.xml",
+    srcs: [
+        "src/**/*.java",
+        "src/**/I*.aidl",
+    ],
+    owner: "google",
+    privileged: true,
+    certificate: "platform",
+}
diff --git a/testapps/TestSatelliteApp/AndroidManifest.xml b/testapps/TestSatelliteApp/AndroidManifest.xml
new file mode 100644
index 0000000..278d102
--- /dev/null
+++ b/testapps/TestSatelliteApp/AndroidManifest.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.phone.testapps.satellitetestapp">
+    <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE"/>
+    <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION"/>
+    <application android:label="SatelliteTestApp">
+        <activity android:name=".SatelliteTestApp"
+             android:label="SatelliteTestApp"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <service android:name=".TestSatelliteService"
+             android:directBootAware="true"
+             android:persistent="true"
+             android:permission="android.permission.BIND_SATELLITE_SERVICE"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.telephony.satellite.SatelliteService"/>
+            </intent-filter>
+        </service>
+
+        <activity android:name=".SatelliteControl" />
+        <activity android:name=".SatelliteTransmissionUpdates" />
+        <activity android:name=".Datagram" />
+        <activity android:name=".Provisioning" />
+        <activity android:name=".SatelliteModemState" />
+    </application>
+</manifest>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml b/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml
new file mode 100644
index 0000000..5cfe7d9
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20dp"
+            android:text="Datagram APIs"/>
+        <Button
+            android:id="@+id/pollPendingSatelliteDatagrams"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/pollPendingSatelliteDatagrams"/>
+        <Button
+            android:id="@+id/multiplePollPendingSatelliteDatagrams"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/multiplePollPendingSatelliteDatagrams"/>
+        <Button
+            android:id="@+id/sendSatelliteDatagram"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/sendSatelliteDatagram"/>
+        <Button
+            android:id="@+id/multipleSendSatelliteDatagram"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/multipleSendSatelliteDatagram"/>
+        <Button
+            android:id="@+id/multipleSendReceiveSatelliteDatagram"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/multipleSendReceiveSatelliteDatagram"/>
+        <Button
+            android:id="@+id/registerForSatelliteDatagram"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/registerForSatelliteDatagram"/>
+        <Button
+            android:id="@+id/unregisterForSatelliteDatagram"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/unregisterForSatelliteDatagram"/>
+        <Button
+            android:id="@+id/Back"
+            android:onClick="Back"
+            android:textColor="@android:color/holo_blue_dark"
+            android:layout_marginTop="100dp"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Back"/>
+        <TextView
+            android:id="@+id/text_id"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:capitalize="characters"
+            android:textColor="@android:color/holo_blue_light"
+            android:layout_centerVertical="true"
+            android:textSize="15dp" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml b/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml
new file mode 100644
index 0000000..c62aebf
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20dp"
+            android:text="Provisioning APIs"/>
+        <Button
+            android:id="@+id/provisionSatelliteService"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/provisionSatelliteService"/>
+        <Button
+            android:id="@+id/deprovisionSatelliteService"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/deprovisionSatelliteService"/>
+        <Button
+            android:id="@+id/requestIsSatelliteProvisioned"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsSatelliteProvisioned"/>
+         <Button
+            android:id="@+id/registerForSatelliteProvisionStateChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/registerForSatelliteProvisionStateChanged"/>
+        <Button
+            android:id="@+id/unregisterForSatelliteProvisionStateChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/unregisterForSatelliteProvisionStateChanged"/>
+        <Button
+            android:id="@+id/Back"
+            android:onClick="Back"
+            android:textColor="@android:color/holo_blue_dark"
+            android:layout_marginTop="100dp"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Back"/>
+        <TextView
+            android:id="@+id/text_id"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:capitalize="characters"
+            android:textColor="@android:color/holo_blue_light"
+            android:layout_centerVertical="true"
+            android:textSize="15dp" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
new file mode 100644
index 0000000..1484ba7
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20dp"
+            android:text="Satellite Control APIs"/>
+        <Button
+            android:id="@+id/requestSatelliteEnabled"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestSatelliteEnabled"/>
+         <Button
+            android:id="@+id/requestIsSatelliteEnabled"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsSatelliteEnabled"/>
+         <Button
+            android:id="@+id/requestIsDemoModeEnabled"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsDemoModeEnabled"/>
+          <Button
+            android:id="@+id/requestIsSatelliteSupported"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsSatelliteSupported"/>
+         <Button
+            android:id="@+id/requestSatelliteCapabilities"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestSatelliteCapabilities"/>
+         <Button
+            android:id="@+id/requestIsSatelliteCommunicationAllowedForCurrentLocation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsSatelliteCommunicationAllowedForCurrentLocation"/>
+          <Button
+            android:id="@+id/requestTimeForNextSatelliteVisibility"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestTimeForNextSatelliteVisibility"/>
+         <Button
+            android:id="@+id/Back"
+            android:onClick="Back"
+            android:textColor="@android:color/holo_blue_dark"
+            android:layout_marginTop="100dp"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Back"/>
+         <TextView
+            android:id="@+id/text_id"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:capitalize="characters"
+            android:textColor="@android:color/holo_blue_light"
+            android:layout_centerVertical="true"
+            android:textSize="15dp" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteModemState.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteModemState.xml
new file mode 100644
index 0000000..5e70ef3
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteModemState.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20dp"
+            android:text="SatelliteModemState APIs"/>
+        <Button
+            android:id="@+id/registerForSatelliteModemStateChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/registerForSatelliteModemStateChanged"/>
+        <Button
+            android:id="@+id/unregisterForSatelliteModemStateChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/unregisterForSatelliteModemStateChanged"/>
+        <Button
+            android:id="@+id/Back"
+            android:onClick="Back"
+            android:textColor="@android:color/holo_blue_dark"
+            android:layout_marginTop="100dp"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Back"/>
+         <TextView
+            android:id="@+id/text_id"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:capitalize="characters"
+            android:textColor="@android:color/holo_blue_light"
+            android:layout_centerVertical="true"
+            android:textSize="15dp" />
+    </LinearLayout>
+</LinearLayout>
+
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
new file mode 100644
index 0000000..f9a0809
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20dp"
+            android:text="Available Satellite APIs"/>
+        <Button
+            android:id="@+id/SatelliteControl"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/SatelliteControl"/>
+        <Button
+            android:id="@+id/SatelliteTransmissionUpdates"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/SatelliteTransmissionUpdates"/>
+        <Button
+            android:id="@+id/Datagram"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Datagram"/>
+        <Button
+            android:id="@+id/Provisioning"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Provisioning"/>
+        <Button
+            android:id="@+id/SatelliteModemState"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/SatelliteModemState"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteTransmissionUpdates.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTransmissionUpdates.xml
new file mode 100644
index 0000000..4eb3851
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTransmissionUpdates.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20dp"
+            android:text="SatelliteTransmissionUpdates APIs"/>
+        <Button
+            android:id="@+id/startSatelliteTransmissionUpdates"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/startSatelliteTransmissionUpdates"/>
+        <Button
+            android:id="@+id/stopSatelliteTransmissionUpdates"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/stopSatelliteTransmissionUpdates"/>
+        <Button
+            android:id="@+id/Back"
+            android:onClick="Back"
+            android:textColor="@android:color/holo_blue_dark"
+            android:layout_marginTop="100dp"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/Back"/>
+         <TextView
+            android:id="@+id/text_id"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:capitalize="characters"
+            android:textColor="@android:color/holo_blue_light"
+            android:layout_centerVertical="true"
+            android:textSize="15dp" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..bd6f389
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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
+  -->
+
+<resources>
+    <string name="SatelliteControl">SatelliteControl APIs</string>
+    <string name="SatelliteTransmissionUpdates">SatelliteTransmissionUpdates APIs</string>
+    <string name="Datagram">Datagram APIs</string>
+    <string name="Provisioning">Provisioning APIs</string>
+    <string name="SatelliteModemState">SatelliteModemState APIs</string>
+
+    <string name="requestSatelliteEnabled">requestSatelliteEnabled</string>
+    <string name="requestIsSatelliteEnabled">requestIsSatelliteEnabled</string>
+    <string name="requestIsDemoModeEnabled">requestIsDemoModeEnabled</string>
+    <string name="requestIsSatelliteSupported">requestIsSatelliteSupported</string>
+    <string name="requestSatelliteCapabilities">requestSatelliteCapabilities</string>
+    <string name="requestIsSatelliteCommunicationAllowedForCurrentLocation">requestIsSatelliteCommunicationAllowedForCurrentLocation</string>
+    <string name="requestTimeForNextSatelliteVisibility">requestTimeForNextSatelliteVisibility</string>
+
+    <string name="startSatelliteTransmissionUpdates">startSatelliteTransmissionUpdates</string>
+    <string name="stopSatelliteTransmissionUpdates">stopSatelliteTransmissionUpdates</string>
+
+    <string name="pollPendingSatelliteDatagrams">pollPendingSatelliteDatagrams</string>
+    <string name="multiplePollPendingSatelliteDatagrams">multiplePollPendingSatelliteDatagrams</string>
+    <string name="sendSatelliteDatagram">sendSatelliteDatagram</string>
+    <string name="multipleSendSatelliteDatagram">multipleSendSatelliteDatagram</string>
+    <string name="multipleSendReceiveSatelliteDatagram">multipleSendReceiveSatelliteDatagram</string>
+    <string name="registerForSatelliteDatagram">registerForSatelliteDatagram</string>
+    <string name="unregisterForSatelliteDatagram">unregisterForSatelliteDatagram</string>
+
+    <string name="provisionSatelliteService">provisionSatelliteService</string>
+    <string name="deprovisionSatelliteService">deprovisionSatelliteService</string>
+    <string name="requestIsSatelliteProvisioned">requestIsSatelliteProvisioned</string>
+    <string name="registerForSatelliteProvisionStateChanged">registerForSatelliteProvisionStateChanged</string>
+    <string name="unregisterForSatelliteProvisionStateChanged">unregisterForSatelliteProvisionStateChanged</string>
+
+    <string name="registerForSatelliteModemStateChanged">registerForSatelliteModemStateChanged</string>
+    <string name="unregisterForSatelliteModemStateChanged">unregisterForSatelliteModemStateChanged</string>
+
+    <string name="Back">Back</string>
+</resources>
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java
new file mode 100644
index 0000000..1391c47
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.satellite.SatelliteDatagram;
+import android.telephony.satellite.SatelliteDatagramCallback;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Activity related to Datagram APIs.
+ */
+public class Datagram extends Activity {
+
+    private static final String TAG = "Datagram";
+
+    private SatelliteManager mSatelliteManager;
+    private SatelliteDatagramCallbackTestApp mCallback;
+    private android.telephony.satellite.stub.SatelliteDatagram mReceivedDatagram;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSatelliteManager = getSystemService(SatelliteManager.class);
+        mCallback = new SatelliteDatagramCallbackTestApp();
+
+        mReceivedDatagram = new android.telephony.satellite.stub.SatelliteDatagram();
+
+        setContentView(R.layout.activity_Datagram);
+        findViewById(R.id.pollPendingSatelliteDatagrams)
+                .setOnClickListener(this::pollPendingSatelliteDatagramsApp);
+        findViewById(R.id.multiplePollPendingSatelliteDatagrams)
+                .setOnClickListener(this::multiplePollPendingSatelliteDatagramsApp);
+        findViewById(R.id.sendSatelliteDatagram)
+                .setOnClickListener(this::sendSatelliteDatagramApp);
+        findViewById(R.id.multipleSendSatelliteDatagram)
+                .setOnClickListener(this::multipleSendSatelliteDatagramApp);
+        findViewById(R.id.multipleSendReceiveSatelliteDatagram)
+                .setOnClickListener(this::multipleSendReceiveSatelliteDatagramApp);
+        findViewById(R.id.registerForSatelliteDatagram)
+                .setOnClickListener(this::registerForSatelliteDatagramApp);
+        findViewById(R.id.unregisterForSatelliteDatagram)
+                .setOnClickListener(this::unregisterForSatelliteDatagramApp);
+        findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startActivity(new Intent(Datagram.this, SatelliteTestApp.class));
+            }
+        });
+    }
+
+    protected static class SatelliteDatagramCallbackTestApp implements SatelliteDatagramCallback {
+        @Override
+        public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
+                int pendingCount, Consumer<Void> callback) {
+            Log.d(TAG, "onSatelliteDatagramReceived in SatelliteTestApp: datagramId =" + datagramId
+                    + ", datagram =" + datagram + ", pendingCount=" + pendingCount);
+        }
+    }
+
+    private void pollPendingSatelliteDatagramsApp(View view) {
+        SatelliteTestApp.getTestSatelliteService().sendOnPendingDatagrams();
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 0);
+        LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        try {
+            Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("pollPendingSatelliteDatagrams is Successful");
+            } else {
+                textView.setText("Status for pollPendingSatelliteDatagrams : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void multiplePollPendingSatelliteDatagramsApp(View view) {
+        SatelliteTestApp.getTestSatelliteService().sendOnPendingDatagrams();
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 4);
+        LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 3);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 2);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 1);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 0);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        try {
+            Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("multiplePollPendingSatelliteDatagrams is Successful");
+            } else {
+                textView.setText("Status for multiplePollPendingSatelliteDatagrams : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void sendSatelliteDatagramApp(View view) {
+        LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
+        String mText = "This is a test datagram message";
+        SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        try {
+            Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("sendSatelliteDatagram is Successful");
+            } else {
+                textView.setText("Status for sendSatelliteDatagram : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void multipleSendSatelliteDatagramApp(View view) {
+        LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
+        String mText = "This is a test datagram message";
+        SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        try {
+            Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("multipleSendSatelliteDatagram is Successful");
+            } else {
+                textView.setText("Status for multipleSendSatelliteDatagram : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void multipleSendReceiveSatelliteDatagramApp(View view) {
+        LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
+        String mText = "This is a test datagram message";
+        SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 4);
+        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 3);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 2);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 1);
+        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+                datagram, true, Runnable::run, resultListener::offer);
+        SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
+                    mReceivedDatagram, 0);
+        try {
+            Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("multipleSendReceiveSatelliteDatagram is Successful");
+            } else {
+                textView.setText("Status for multipleSendReceiveSatelliteDatagram : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void registerForSatelliteDatagramApp(View view) {
+        int result = mSatelliteManager.registerForSatelliteDatagram(Runnable::run, mCallback);
+        TextView textView = findViewById(R.id.text_id);
+        if (result == 0) {
+            textView.setText("registerForSatelliteDatagram is successful");
+        } else {
+            textView.setText("Status for registerForSatelliteDatagram : "
+                    + SatelliteErrorUtils.mapError(result));
+        }
+    }
+
+    private void unregisterForSatelliteDatagramApp(View view) {
+        mSatelliteManager.unregisterForSatelliteDatagram(mCallback);
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("unregisterForSatelliteDatagram is successful");
+    }
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl
new file mode 100644
index 0000000..2c320c8
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.telephony.satellite.stub.SatelliteDatagram;
+
+/**
+ * {@hide}
+ */
+oneway interface ILocalSatelliteListener {
+    /**
+     * Indicates that the remote service - SatelliteModemInterface - has successfully connected to
+     * the TestSatelliteService.
+     */
+    void onRemoteServiceConnected();
+
+    /**
+     * Indicates that TestSatelliteService has just received the request
+     * startSendingSatellitePointingInfo from Telephony.
+     */
+    void onStartSendingSatellitePointingInfo();
+
+    /**
+     * Indicates that TestSatelliteService has just received the request
+     * stopSendingSatellitePointingInfo from Telephony.
+     */
+    void onStopSendingSatellitePointingInfo();
+
+    /**
+     * Indicates that TestSatelliteService has just received the request
+     * pollPendingSatelliteDatagrams from Telephony.
+     */
+    void onPollPendingSatelliteDatagrams();
+
+    /**
+     * Indicates that TestSatelliteService has just received the request
+     * sendSatelliteDatagram from Telephony.
+     */
+    void onSendSatelliteDatagram(in SatelliteDatagram datagram, in boolean isEmergency);
+
+    /**
+     * Indicates that TestSatelliteService has just received the request
+     * requestSatelliteListeningEnabled from Telephony.
+     */
+    void onSatelliteListeningEnabled(in boolean enabled);
+
+    /**
+     * Indicates that TestSatelliteService has just received the request
+     * enableCellularModemWhileSatelliteModeIsOn from Telephony.
+     */
+    void onEnableCellularModemWhileSatelliteModeIsOn(in boolean enable);
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
new file mode 100644
index 0000000..b403b2c
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OutcomeReceiver;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteProvisionStateCallback;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Activity related to Provisioning APIs.
+ */
+public class Provisioning extends Activity {
+
+    private static final String TAG = "Provisioning";
+
+    private SatelliteManager mSatelliteManager;
+    private SatelliteProvisionStateCallbackTestApp mCallback;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSatelliteManager = getSystemService(SatelliteManager.class);
+        mCallback = new SatelliteProvisionStateCallbackTestApp();
+
+        setContentView(R.layout.activity_Provisioning);
+        findViewById(R.id.provisionSatelliteService)
+                .setOnClickListener(this::provisionSatelliteServiceApp);
+        findViewById(R.id.deprovisionSatelliteService)
+                .setOnClickListener(this::deprovisionSatelliteServiceApp);
+        findViewById(R.id.requestIsSatelliteProvisioned)
+                .setOnClickListener(this::requestIsSatelliteProvisionedApp);
+        findViewById(R.id.registerForSatelliteProvisionStateChanged)
+                .setOnClickListener(this::registerForSatelliteProvisionStateChangedApp);
+        findViewById(R.id.unregisterForSatelliteProvisionStateChanged)
+                .setOnClickListener(this::unregisterForSatelliteProvisionStateChangedApp);
+        findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startActivity(new Intent(Provisioning.this, SatelliteTestApp.class));
+            }
+        });
+    }
+
+    protected static class SatelliteProvisionStateCallbackTestApp implements
+            SatelliteProvisionStateCallback {
+        @Override
+        public void onSatelliteProvisionStateChanged(boolean provisioned) {
+            Log.d(TAG, "onSatelliteProvisionStateChanged in SatelliteTestApp: provisioned="
+                    + provisioned);
+        }
+    }
+
+    private void provisionSatelliteServiceApp(View view) {
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        String mText = "This is test provision data.";
+        byte[] testProvisionData = mText.getBytes();
+        mSatelliteManager.provisionSatelliteService("SATELLITE_TOKEN", testProvisionData,
+                cancellationSignal, Runnable::run, error::offer);
+        try {
+            Integer value = error.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            textView.setText("Status for provisionSatelliteService : "
+                    + SatelliteErrorUtils.mapError(value));
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void deprovisionSatelliteServiceApp(View view) {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.deprovisionSatelliteService("SATELLITE_TOKEN", Runnable::run,
+                error::offer);
+        try {
+            Integer value = error.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            textView.setText("Status for deprovisionSatelliteService : "
+                    + SatelliteErrorUtils.mapError(value));
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void requestIsSatelliteProvisionedApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> mReceiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Boolean result) {
+                enabled.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestIsSatelliteProvisioned result: "
+                        + enabled.get());
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestIsSatelliteProvisioned error : "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestIsSatelliteProvisioned(Runnable::run, mReceiver);
+    }
+
+    private void registerForSatelliteProvisionStateChangedApp(View view) {
+        int result = mSatelliteManager.registerForSatelliteProvisionStateChanged(Runnable::run,
+                mCallback);
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("Status for registerForSatelliteProvisionStateChanged : "
+                + SatelliteErrorUtils.mapError(result));
+    }
+
+    private void unregisterForSatelliteProvisionStateChangedApp(View view) {
+        mSatelliteManager.unregisterForSatelliteProvisionStateChanged(mCallback);
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("unregisterForSatelliteProvisionStateChanged is successful");
+    }
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
new file mode 100644
index 0000000..9cfbc6a
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.OutcomeReceiver;
+import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteManager;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import java.time.Duration;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Activity related to SatelliteControl APIs for satellite.
+ */
+public class SatelliteControl extends Activity {
+
+    private static final String TAG = "SatelliteControl";
+
+    private SatelliteManager mSatelliteManager;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSatelliteManager = getSystemService(SatelliteManager.class);
+
+        setContentView(R.layout.activity_SatelliteControl);
+        findViewById(R.id.requestSatelliteEnabled)
+                .setOnClickListener(this::requestSatelliteEnabledApp);
+        findViewById(R.id.requestIsSatelliteEnabled)
+                .setOnClickListener(this::requestIsSatelliteEnabledApp);
+        findViewById(R.id.requestIsDemoModeEnabled)
+                .setOnClickListener(this::requestIsDemoModeEnabledApp);
+        findViewById(R.id.requestIsSatelliteSupported)
+                .setOnClickListener(this::requestIsSatelliteSupportedApp);
+        findViewById(R.id.requestSatelliteCapabilities)
+                .setOnClickListener(this::requestSatelliteCapabilitiesApp);
+        findViewById(R.id.requestIsSatelliteCommunicationAllowedForCurrentLocation)
+                .setOnClickListener(
+                this::requestIsSatelliteCommunicationAllowedForCurrentLocationApp);
+        findViewById(R.id.requestTimeForNextSatelliteVisibility)
+                .setOnClickListener(this::requestTimeForNextSatelliteVisibilityApp);
+        findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startActivity(new Intent(SatelliteControl.this, SatelliteTestApp.class));
+            }
+        });
+    }
+
+    private void requestSatelliteEnabledApp(View view) {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, error::offer);
+        try {
+            Integer value = error.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("requestSatelliteEnabled is successful");
+            } else {
+                textView.setText("Status for requestSatelliteEnabled: "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void requestIsSatelliteEnabledApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Boolean result) {
+                enabled.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                if (enabled.get()) {
+                    textView.setText("requestIsSatelliteEnabled is true");
+                } else {
+                    textView.setText("Status for requestIsSatelliteEnabled result : "
+                            + enabled.get());
+                }
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestIsSatelliteEnabled error : "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestIsSatelliteEnabled(Runnable::run, receiver);
+    }
+
+    private void requestIsDemoModeEnabledApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Boolean result) {
+                enabled.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                if (enabled.get()) {
+                    textView.setText("requestIsDemoModeEnabled is true");
+                } else {
+                    textView.setText("Status for requestIsDemoModeEnabled result : "
+                            + enabled.get());
+                }
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestIsDemoModeEnabled error : "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestIsDemoModeEnabled(Runnable::run, receiver);
+    }
+
+    private void requestIsSatelliteSupportedApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Boolean result) {
+                enabled.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                if (enabled.get()) {
+                    textView.setText("requestIsSatelliteSupported is true");
+                } else {
+                    textView.setText("Status for requestIsSatelliteSupported result : "
+                            + enabled.get());
+                }
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestIsSatelliteSupported error : "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestIsSatelliteSupported(Runnable::run, receiver);
+    }
+
+    private void requestSatelliteCapabilitiesApp(View view) {
+        final AtomicReference<SatelliteCapabilities> capabilities = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<SatelliteCapabilities, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(SatelliteCapabilities result) {
+                capabilities.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestSatelliteCapabilities result: "
+                        + capabilities.get());
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestSatelliteCapabilities error : "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestSatelliteCapabilities(Runnable::run, receiver);
+    }
+
+    private void requestIsSatelliteCommunicationAllowedForCurrentLocationApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        String display = "requestIsSatelliteCommunicationAllowedForCurrentLocation";
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Boolean result) {
+                enabled.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                if (enabled.get()) {
+                    textView.setText(display + "is true");
+                } else {
+                    textView.setText("Status for" + display + "result: " + enabled.get());
+                }
+
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for"  + display + "error: "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestIsSatelliteCommunicationAllowedForCurrentLocation(Runnable::run,
+                receiver);
+    }
+
+    private void requestTimeForNextSatelliteVisibilityApp(View view) {
+        final AtomicReference<Duration> nextVisibilityDuration = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Duration, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Duration result) {
+                nextVisibilityDuration.set(result);
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestTimeForNextSatelliteVisibility result : "
+                        + result.getSeconds());
+            }
+
+            @Override
+            public void onError(SatelliteManager.SatelliteException exception) {
+                errorCode.set(exception.getErrorCode());
+                TextView textView = findViewById(R.id.text_id);
+                textView.setText("Status for requestTimeForNextSatelliteVisibility error : "
+                        + SatelliteErrorUtils.mapError(errorCode.get()));
+            }
+        };
+        mSatelliteManager.requestTimeForNextSatelliteVisibility(Runnable::run, receiver);
+    }
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java
new file mode 100644
index 0000000..a52d3ba
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.telephony.satellite.stub.SatelliteError;
+import android.util.Log;
+
+/**
+ * Utils class for satellite error to display as string in SatelliteTestApp UI.
+ */
+public class SatelliteErrorUtils {
+    private static final String TAG = "SatelliteErrorUtils";
+
+   /**
+     * @param error int from the satellite manager.
+     * @return The converted SatelliteError for the testapp, result of the operation.
+     */
+    public static String mapError(int error) {
+        switch (error) {
+            case SatelliteError.ERROR_NONE:
+                return "SATELLITE_ERROR_NONE";
+            case SatelliteError.SATELLITE_ERROR:
+                return "SATELLITE_ERROR";
+            case SatelliteError.SERVER_ERROR:
+                return "SATELLITE_SERVER_ERROR";
+            case SatelliteError.SERVICE_ERROR:
+                return "SATELLITE_SERVICE_ERROR";
+            case SatelliteError.MODEM_ERROR:
+                return "SATELLITE_MODEM_ERROR";
+            case SatelliteError.NETWORK_ERROR:
+                return "SATELLITE_NETWORK_ERROR";
+            case SatelliteError.INVALID_TELEPHONY_STATE:
+                return "SATELLITE_INVALID_TELEPHONY_STATE";
+            case SatelliteError.INVALID_MODEM_STATE:
+                return "SATELLITE_INVALID_MODEM_STATE";
+            case SatelliteError.INVALID_ARGUMENTS:
+                return "SATELLITE_INVALID_ARGUMENTS";
+            case SatelliteError.REQUEST_FAILED:
+                return "SATELLITE_REQUEST_FAILED";
+            case SatelliteError.RADIO_NOT_AVAILABLE:
+                return "SATELLITE_RADIO_NOT_AVAILABLE";
+            case SatelliteError.REQUEST_NOT_SUPPORTED:
+                return "SATELLITE_REQUEST_NOT_SUPPORTED";
+            case SatelliteError.NO_RESOURCES:
+                return "SATELLITE_NO_RESOURCES";
+            case SatelliteError.SERVICE_NOT_PROVISIONED:
+                return "SATELLITE_SERVICE_NOT_PROVISIONED";
+            case SatelliteError.SERVICE_PROVISION_IN_PROGRESS:
+                return "SATELLITE_SERVICE_PROVISION_IN_PROGRESS";
+            case SatelliteError.REQUEST_ABORTED:
+                return "SATELLITE_REQUEST_ABORTED";
+            case SatelliteError.SATELLITE_ACCESS_BARRED:
+                return "SATELLITE_ACCESS_BARRED";
+            case SatelliteError.NETWORK_TIMEOUT:
+                return "SATELLITE_NETWORK_TIMEOUT";
+            case SatelliteError.SATELLITE_NOT_REACHABLE:
+                return "SATELLITE_NOT_REACHABLE";
+            case SatelliteError.NOT_AUTHORIZED:
+                return "SATELLITE_NOT_AUTHORIZED";
+        }
+        Log.d(TAG, "Received invalid satellite service error: " + error);
+        return "SATELLITE_SERVICE_ERROR";
+    }
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteModemState.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteModemState.java
new file mode 100644
index 0000000..aa0d5b0
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteModemState.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteStateCallback;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+/**
+ * Activity related to SatelliteModemState APIs.
+ */
+public class SatelliteModemState extends Activity {
+
+    private static final String TAG = "SatelliteModemState";
+
+    private SatelliteManager mSatelliteManager;
+    private SatelliteStateCallbackTestApp mCallback;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSatelliteManager = getSystemService(SatelliteManager.class);
+        mCallback = new SatelliteStateCallbackTestApp();
+
+        setContentView(R.layout.activity_SatelliteModemState);
+        findViewById(R.id.registerForSatelliteModemStateChanged)
+                .setOnClickListener(this::registerForSatelliteModemStateChangedApp);
+        findViewById(R.id.unregisterForSatelliteModemStateChanged)
+                .setOnClickListener(this::unregisterForSatelliteModemStateChangedApp);
+        findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startActivity(new Intent(SatelliteModemState.this, SatelliteTestApp.class));
+            }
+        });
+    }
+
+    protected static class SatelliteStateCallbackTestApp implements SatelliteStateCallback {
+        @Override
+        public void onSatelliteModemStateChanged(int state) {
+            Log.d(TAG, "onSatelliteModemStateChanged in SatelliteTestApp: state=" + state);
+        }
+    }
+
+    private void registerForSatelliteModemStateChangedApp(View view) {
+        int result = mSatelliteManager.registerForSatelliteModemStateChanged(Runnable::run,
+                mCallback);
+        TextView textView = findViewById(R.id.text_id);
+        if (result == 0) {
+            textView.setText("registerForSatelliteModemStateChanged is successful");
+        } else {
+            textView.setText("Status for registerForSatelliteModemStateChanged : "
+                    + SatelliteErrorUtils.mapError(result));
+        }
+    }
+
+    private void unregisterForSatelliteModemStateChangedApp(View view) {
+        mSatelliteManager.unregisterForSatelliteModemStateChanged(mCallback);
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("unregisterForSatelliteModemStateChanged is successful");
+    }
+}
+
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
new file mode 100644
index 0000000..ed5b75e
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.telephony.satellite.stub.SatelliteDatagram;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SatelliteTestApp main activity to navigate to other APIs related to satellite.
+ */
+public class SatelliteTestApp extends Activity {
+
+    private static final String TAG = "SatelliteTestApp";
+    private static TestSatelliteService sSatelliteService;
+    private final Object mSendDatagramLock = new Object();
+
+    private TestSatelliteServiceConnection mSatelliteServiceConn;
+    private List<SatelliteDatagram> mSentSatelliteDatagrams = new ArrayList<>();
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (mSatelliteServiceConn == null) {
+            mSatelliteServiceConn = new TestSatelliteServiceConnection();
+            getBaseContext().bindService(new Intent(getBaseContext(),
+                    TestSatelliteService.class), mSatelliteServiceConn, Context.BIND_AUTO_CREATE);
+        }
+
+        setContentView(R.layout.activity_SatelliteTestApp);
+        findViewById(R.id.SatelliteControl).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(SatelliteTestApp.this, SatelliteControl.class);
+                startActivity(intent);
+            }
+        });
+        findViewById(R.id.SatelliteTransmissionUpdates).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(SatelliteTestApp.this,
+                        SatelliteTransmissionUpdates.class);
+                startActivity(intent);
+            }
+        });
+        findViewById(R.id.Datagram).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(SatelliteTestApp.this, Datagram.class);
+                startActivity(intent);
+            }
+        });
+        findViewById(R.id.Provisioning).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(SatelliteTestApp.this, Provisioning.class);
+                startActivity(intent);
+            }
+        });
+        findViewById(R.id.SatelliteModemState).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(SatelliteTestApp.this, SatelliteModemState.class);
+                startActivity(intent);
+            }
+        });
+    }
+
+    private final ILocalSatelliteListener mSatelliteListener =
+            new ILocalSatelliteListener.Stub() {
+                @Override
+                public void onRemoteServiceConnected() {
+                    Log.d(TAG, "onRemoteServiceConnected");
+                }
+
+                @Override
+                public void onStartSendingSatellitePointingInfo() {
+                    Log.d(TAG, "onStartSendingSatellitePointingInfo");
+                }
+
+                @Override
+                public void onStopSendingSatellitePointingInfo() {
+                    Log.d(TAG, "onStopSendingSatellitePointingInfo");
+                }
+
+                @Override
+                public void onPollPendingSatelliteDatagrams() {
+                    Log.d(TAG, "onPollPendingSatelliteDatagrams");
+                }
+
+                @Override
+                public void onSendSatelliteDatagram(
+                        SatelliteDatagram datagram, boolean isEmergency) {
+                    Log.d(TAG, "onSendSatelliteDatagram");
+                    synchronized (mSendDatagramLock) {
+                        mSentSatelliteDatagrams.add(datagram);
+                    }
+                }
+
+                @Override
+                public void onSatelliteListeningEnabled(boolean enable) {
+                    Log.d(TAG, "onSatelliteListeningEnabled");
+                }
+
+                @Override
+                public void onEnableCellularModemWhileSatelliteModeIsOn(boolean enable) {
+                    Log.d(TAG, "onEnableCellularModemWhileSatelliteModeIsOn");
+                }
+            };
+
+    private class TestSatelliteServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "onServiceConnected in SatelliteTestApp");
+            sSatelliteService = ((TestSatelliteService.LocalBinder) service).getService();
+            sSatelliteService.setLocalSatelliteListener(mSatelliteListener);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, "onServiceDisconnected in SatelliteTestApp");
+            sSatelliteService = null;
+        }
+    }
+
+    public static TestSatelliteService getTestSatelliteService() {
+        return sSatelliteService;
+    }
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTransmissionUpdates.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTransmissionUpdates.java
new file mode 100644
index 0000000..368f519
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTransmissionUpdates.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteTransmissionUpdateCallback;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity related to SatelliteTransmissionUpdates APIs.
+ */
+public class SatelliteTransmissionUpdates extends Activity {
+
+    private static final String TAG = "SatelliteTransmissionUpdates";
+
+    private SatelliteManager mSatelliteManager;
+    private SatelliteTransmissionUpdateCallbackTestApp mCallback;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mSatelliteManager = getSystemService(SatelliteManager.class);
+        mCallback = new SatelliteTransmissionUpdateCallbackTestApp();
+
+        setContentView(R.layout.activity_SatelliteTransmissionUpdates);
+        findViewById(R.id.startSatelliteTransmissionUpdates)
+                .setOnClickListener(this::startSatelliteTransmissionUpdatesApp);
+        findViewById(R.id.stopSatelliteTransmissionUpdates)
+                .setOnClickListener(this::stopSatelliteTransmissionUpdatesApp);
+        findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startActivity(new Intent(SatelliteTransmissionUpdates.this,
+                        SatelliteTestApp.class));
+            }
+        });
+    }
+
+    protected static class SatelliteTransmissionUpdateCallbackTestApp implements
+            SatelliteTransmissionUpdateCallback {
+        @Override
+        public void onSatellitePositionChanged(PointingInfo pointingInfo) {
+            Log.d(TAG, "onSatellitePositionChanged in TestApp: pointingInfo =" + pointingInfo);
+        }
+
+        @Override
+        public void onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode) {
+            Log.d(TAG, "onSendDatagramStateChanged in TestApp: state =" + state
+                    + ", sendPendingCount =" + sendPendingCount + ", errorCode=" + errorCode);
+        }
+
+        @Override
+        public void onReceiveDatagramStateChanged(
+                int state, int receivePendingCount, int errorCode) {
+            Log.d(TAG, "onReceiveDatagramStateChanged in TestApp: state=" + state + ", "
+                    + "receivePendingCount=" + receivePendingCount + ", errorCode=" + errorCode);
+        }
+    }
+
+    private void startSatelliteTransmissionUpdatesApp(View view) {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.startSatelliteTransmissionUpdates(Runnable::run, error::offer, mCallback);
+        try {
+            Integer value = error.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("startSatelliteTransmissionUpdates is Successful");
+            } else {
+                textView.setText("Status for startSatelliteTransmissionUpdates : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+
+    private void stopSatelliteTransmissionUpdatesApp(View view) {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.stopSatelliteTransmissionUpdates(mCallback, Runnable::run, error::offer);
+        try {
+            Integer value = error.poll(1000, TimeUnit.MILLISECONDS);
+            TextView textView = findViewById(R.id.text_id);
+            if (value == 0) {
+                textView.setText("stopSatelliteTransmissionUpdates is Successful");
+            } else {
+                textView.setText("Status for stopSatelliteTransmissionUpdates : "
+                        + SatelliteErrorUtils.mapError(value));
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "exception caught =" + e);
+        }
+    }
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
new file mode 100644
index 0000000..5c7f4a2
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2023 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.phone.testapps.satellitetestapp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.telephony.satellite.AntennaDirection;
+import android.telephony.satellite.AntennaPosition;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
+import android.telephony.satellite.stub.ISatelliteListener;
+import android.telephony.satellite.stub.NTRadioTechnology;
+import android.telephony.satellite.stub.PointingInfo;
+import android.telephony.satellite.stub.SatelliteCapabilities;
+import android.telephony.satellite.stub.SatelliteDatagram;
+import android.telephony.satellite.stub.SatelliteError;
+import android.telephony.satellite.stub.SatelliteImplBase;
+import android.telephony.satellite.stub.SatelliteModemState;
+import android.telephony.satellite.stub.SatelliteService;
+import android.util.Log;
+
+import com.android.internal.telephony.IBooleanConsumer;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.util.FunctionalUtils;
+import com.android.telephony.Rlog;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Test service for Satellite to verify end to end flow via testapp.
+ */
+public class TestSatelliteService extends SatelliteImplBase {
+    private static final String TAG = "TestSatelliteService";
+
+    // Hardcoded values below
+    private static final int SATELLITE_ALWAYS_VISIBLE = 0;
+    /** SatelliteCapabilities constant indicating that the radio technology is proprietary. */
+    private static final int[] SUPPORTED_RADIO_TECHNOLOGIES =
+            new int[]{NTRadioTechnology.PROPRIETARY};
+    /** SatelliteCapabilities constant indicating that pointing to satellite is required. */
+    private static final boolean POINTING_TO_SATELLITE_REQUIRED = true;
+    /** SatelliteCapabilities constant indicating the maximum number of characters per datagram. */
+    private static final int MAX_BYTES_PER_DATAGRAM = 339;
+    /** SatelliteCapabilities constant keys which are used to fill mAntennaPositionMap. */
+    private static final int[] ANTENNA_POSITION_KEYS = new int[]{
+            SatelliteManager.DISPLAY_MODE_OPENED, SatelliteManager.DISPLAY_MODE_CLOSED};
+    /** SatelliteCapabilities constant values which are used to fill mAntennaPositionMap. */
+    private static final AntennaPosition[] ANTENNA_POSITION_VALUES = new AntennaPosition[] {
+            new AntennaPosition(new AntennaDirection(1, 1, 1),
+                    SatelliteManager.DEVICE_HOLD_POSITION_PORTRAIT),
+            new AntennaPosition(new AntennaDirection(2, 2, 2),
+                    SatelliteManager.DEVICE_HOLD_POSITION_LANDSCAPE_LEFT)
+    };
+
+    @NonNull
+    private final Map<IBinder, ISatelliteListener> mRemoteListeners = new HashMap<>();
+    @Nullable private ILocalSatelliteListener mLocalListener;
+    private final LocalBinder mBinder = new LocalBinder();
+    @SatelliteError
+    private int mErrorCode = SatelliteError.ERROR_NONE;
+
+    // For local access of this Service.
+    class LocalBinder extends Binder {
+        TestSatelliteService getService() {
+            return TestSatelliteService.this;
+        }
+    }
+
+    private boolean mIsCommunicationAllowedInLocation;
+    private boolean mIsEnabled;
+    private boolean mIsProvisioned;
+    private boolean mIsSupported;
+    private int mModemState;
+    private boolean mIsCellularModemEnabledMode;
+
+    /**
+     * Create TestSatelliteService using the Executor specified for methods being called from
+     * the framework.
+     *
+     * @param executor The executor for the framework to use when executing satellite methods.
+     */
+    public TestSatelliteService(@NonNull Executor executor) {
+        super(executor);
+        mIsCommunicationAllowedInLocation = true;
+        mIsEnabled = false;
+        mIsProvisioned = false;
+        mIsSupported = true;
+        mModemState = SatelliteModemState.SATELLITE_MODEM_STATE_OFF;
+        mIsCellularModemEnabledMode = false;
+    }
+
+    /**
+     * Zero-argument constructor to prevent service binding exception.
+     */
+    public TestSatelliteService() {
+        this(Runnable::run);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SatelliteService.SERVICE_INTERFACE.equals(intent.getAction())) {
+            logd("Remote service bound");
+            return getBinder();
+        }
+        logd("Local service bound");
+        return mBinder;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        logd("onCreate");
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        logd("onDestroy");
+    }
+
+    @Override
+    public void setSatelliteListener(@NonNull ISatelliteListener listener) {
+        logd("setSatelliteListener");
+        mRemoteListeners.put(listener.asBinder(), listener);
+        notifyRemoteServiceConnected();
+    }
+
+    @Override
+    public void requestSatelliteListeningEnabled(boolean enabled, int timeout,
+            @NonNull IIntegerConsumer errorCallback) {
+        logd("requestSatelliteListeningEnabled: mErrorCode=" + mErrorCode);
+
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener.onSatelliteListeningEnabled(enabled));
+        } else {
+            loge("requestSatelliteListeningEnabled: mLocalListener is null");
+        }
+
+        if (!verifySatelliteModemState(errorCallback)) {
+            return;
+        }
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+
+        if (enabled) {
+            updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_LISTENING);
+        } else {
+            updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_IDLE);
+        }
+        runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+    }
+
+    @Override
+    public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
+            @NonNull IIntegerConsumer errorCallback) {
+        logd("requestSatelliteEnabled: mErrorCode=" + mErrorCode + " enable = " + enableSatellite
+                + "mIsCellularModemEnabledMode = " + mIsCellularModemEnabledMode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+
+        if (enableSatellite) {
+            enableSatellite(errorCallback);
+        } else {
+            mIsCellularModemEnabledMode = false;
+            disableSatellite(errorCallback);
+        }
+    }
+
+    @Override
+    public void enableCellularModemWhileSatelliteModeIsOn(boolean enable,
+             @NonNull IIntegerConsumer errorCallback) {
+        logd("enableCellularModemWhileSatelliteModeIsOn :" + enable + " error Code = " + mErrorCode
+                    + "mIsCellularModemEnabledMode = " + mIsCellularModemEnabledMode
+                    + " mIsEnabled = " + mIsEnabled);
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener
+                    .onEnableCellularModemWhileSatelliteModeIsOn(enable));
+        } else {
+            loge("requestSatelliteListeningEnabled: mLocalListener is null");
+        }
+        if (mIsEnabled) {
+            if (enable) {
+                mIsCellularModemEnabledMode = true;
+                disableSatellite(errorCallback);
+            } else {
+                mIsCellularModemEnabledMode = false;
+                enableSatellite(errorCallback);
+            }
+        } else {
+            final int finalError = SatelliteError.INVALID_MODEM_STATE;
+            runWithExecutor(() -> errorCallback.accept(finalError));
+        }
+
+    }
+    private void enableSatellite(@NonNull IIntegerConsumer errorCallback) {
+        mIsEnabled = true;
+        updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_IDLE);
+        runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+    }
+
+    private void disableSatellite(@NonNull IIntegerConsumer errorCallback) {
+        mIsEnabled = false;
+        updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_OFF);
+        runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+    }
+
+    @Override
+    public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer errorCallback,
+            @NonNull IBooleanConsumer callback) {
+        logd("requestIsSatelliteEnabled: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> callback.accept(mIsEnabled));
+    }
+
+    @Override
+    public void requestIsSatelliteSupported(@NonNull IIntegerConsumer errorCallback,
+            @NonNull IBooleanConsumer callback) {
+        logd("requestIsSatelliteSupported");
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> callback.accept(mIsSupported));
+    }
+
+    @Override
+    public void requestSatelliteCapabilities(@NonNull IIntegerConsumer errorCallback,
+            @NonNull ISatelliteCapabilitiesConsumer callback) {
+        logd("requestSatelliteCapabilities: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+
+        SatelliteCapabilities capabilities = new SatelliteCapabilities();
+        capabilities.supportedRadioTechnologies = SUPPORTED_RADIO_TECHNOLOGIES;
+        capabilities.isPointingRequired = POINTING_TO_SATELLITE_REQUIRED;
+        capabilities.maxBytesPerOutgoingDatagram = MAX_BYTES_PER_DATAGRAM;
+        capabilities.antennaPositionKeys = ANTENNA_POSITION_KEYS;
+        capabilities.antennaPositionValues = ANTENNA_POSITION_VALUES;
+        runWithExecutor(() -> callback.accept(capabilities));
+    }
+
+    @Override
+    public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) {
+        logd("startSendingSatellitePointingInfo: mErrorCode=" + mErrorCode);
+        if (!verifySatelliteModemState(errorCallback)) {
+            if (mLocalListener != null) {
+                runWithExecutor(() -> mLocalListener.onStartSendingSatellitePointingInfo());
+            } else {
+                loge("startSendingSatellitePointingInfo: mLocalListener is null");
+            }
+            return;
+        }
+
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+        } else {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+        }
+
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener.onStartSendingSatellitePointingInfo());
+        } else {
+            loge("startSendingSatellitePointingInfo: mLocalListener is null");
+        }
+    }
+
+    @Override
+    public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) {
+        logd("stopSendingSatellitePointingInfo: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+        } else {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+        }
+
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener.onStopSendingSatellitePointingInfo());
+        } else {
+            loge("stopSendingSatellitePointingInfo: mLocalListener is null");
+        }
+    }
+
+    @Override
+    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
+            @NonNull IIntegerConsumer errorCallback) {
+        logd("provisionSatelliteService: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+        updateSatelliteProvisionState(true);
+    }
+
+    @Override
+    public void deprovisionSatelliteService(@NonNull String token,
+            @NonNull IIntegerConsumer errorCallback) {
+        logd("deprovisionSatelliteService: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+        updateSatelliteProvisionState(false);
+    }
+
+    @Override
+    public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer errorCallback,
+            @NonNull IBooleanConsumer callback) {
+        logd("requestIsSatelliteProvisioned: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> callback.accept(mIsProvisioned));
+    }
+
+    @Override
+    public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer errorCallback) {
+        logd("pollPendingSatelliteDatagrams: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+        } else {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+        }
+
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener.onPollPendingSatelliteDatagrams());
+        } else {
+            loge("pollPendingSatelliteDatagrams: mLocalListener is null");
+        }
+    }
+
+    @Override
+    public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency,
+            @NonNull IIntegerConsumer errorCallback) {
+        logd("sendSatelliteDatagram: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+        } else {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE));
+        }
+
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener.onSendSatelliteDatagram(datagram, isEmergency));
+        } else {
+            loge("sendSatelliteDatagram: mLocalListener is null");
+        }
+    }
+
+    @Override
+    public void requestSatelliteModemState(@NonNull IIntegerConsumer errorCallback,
+            @NonNull IIntegerConsumer callback) {
+        logd("requestSatelliteModemState: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> callback.accept(mModemState));
+    }
+
+    @Override
+    public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
+            @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) {
+        logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+
+        if (mIsCommunicationAllowedInLocation) {
+            runWithExecutor(() -> callback.accept(true));
+        } else {
+            runWithExecutor(() -> callback.accept(false));
+        }
+    }
+
+    @Override
+    public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer errorCallback,
+            @NonNull IIntegerConsumer callback) {
+        logd("requestTimeForNextSatelliteVisibility: mErrorCode=" + mErrorCode);
+        if (mErrorCode != SatelliteError.ERROR_NONE) {
+            runWithExecutor(() -> errorCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> callback.accept(SATELLITE_ALWAYS_VISIBLE));
+    }
+
+    public void setLocalSatelliteListener(@NonNull ILocalSatelliteListener listener) {
+        logd("setLocalSatelliteListener: listener=" + listener);
+        mLocalListener = listener;
+    }
+
+    public void setErrorCode(@SatelliteError int errorCode) {
+        logd("setErrorCode: errorCode=" + errorCode);
+        mErrorCode = errorCode;
+    }
+
+    public void setSatelliteSupport(boolean supported) {
+        logd("setSatelliteSupport: supported=" + supported);
+        mIsSupported = supported;
+    }
+
+    public void sendOnSatelliteDatagramReceived(SatelliteDatagram datagram, int pendingCount) {
+        logd("sendOnSatelliteDatagramReceived");
+        mRemoteListeners.values().forEach(listener -> runWithExecutor(() ->
+                listener.onSatelliteDatagramReceived(datagram, pendingCount)));
+    }
+
+    public void sendOnPendingDatagrams() {
+        logd("sendOnPendingDatagrams");
+        mRemoteListeners.values().forEach(listener -> runWithExecutor(() ->
+                listener.onPendingDatagrams()));
+    }
+
+    public void sendOnSatellitePositionChanged(PointingInfo pointingInfo) {
+        logd("sendOnSatellitePositionChanged");
+        mRemoteListeners.values().forEach(listener -> runWithExecutor(() ->
+                listener.onSatellitePositionChanged(pointingInfo)));
+    }
+
+    /**
+     * Helper method to verify that the satellite modem is properly configured to receive
+     * requests.
+     *
+     * @param errorCallback The callback to notify of any errors preventing satellite requests.
+     * @return {@code true} if the satellite modem is configured to receive requests and
+     * {@code false} if it is not.
+     */
+    private boolean verifySatelliteModemState(@NonNull IIntegerConsumer errorCallback) {
+        if (!mIsSupported) {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.REQUEST_NOT_SUPPORTED));
+            return false;
+        }
+        if (!mIsProvisioned) {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.SERVICE_NOT_PROVISIONED));
+            return false;
+        }
+        if (!mIsEnabled) {
+            runWithExecutor(() -> errorCallback.accept(SatelliteError.INVALID_MODEM_STATE));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Update the satellite modem state and notify listeners if it changed.
+     *
+     * @param modemState The {@link SatelliteModemState} to update.
+     */
+    private void updateSatelliteModemState(int modemState) {
+        if (modemState == mModemState) {
+            return;
+        }
+        if (mIsCellularModemEnabledMode
+                && modemState == SatelliteModemState.SATELLITE_MODEM_STATE_OFF) {
+            logd("Not updating the Modem state to Off as it is in CellularModemEnabledMode");
+            return;
+        }
+        mRemoteListeners.values().forEach(listener -> runWithExecutor(() ->
+                listener.onSatelliteModemStateChanged(modemState)));
+        mModemState = modemState;
+    }
+
+    /**
+     * Update the satellite provision state and notify listeners if it changed.
+     *
+     * @param isProvisioned {@code true} if the satellite is currently provisioned and
+     *                      {@code false} if it is not.
+     */
+    private void updateSatelliteProvisionState(boolean isProvisioned) {
+        logd("updateSatelliteProvisionState: isProvisioned=" + isProvisioned
+                + ", mIsProvisioned=" + mIsProvisioned);
+        if (isProvisioned == mIsProvisioned) {
+            return;
+        }
+        mIsProvisioned = isProvisioned;
+        logd("updateSatelliteProvisionState: mRemoteListeners.size=" + mRemoteListeners.size());
+        mRemoteListeners.values().forEach(listener -> runWithExecutor(() ->
+                listener.onSatelliteProvisionStateChanged(mIsProvisioned)));
+    }
+
+    /**
+     * Execute the given runnable using the executor that this service was created with.
+     *
+     * @param r A runnable that can throw an exception.
+     */
+    private void runWithExecutor(@NonNull FunctionalUtils.ThrowingRunnable r) {
+        mExecutor.execute(() -> Binder.withCleanCallingIdentity(r));
+    }
+
+    private void notifyRemoteServiceConnected() {
+        logd("notifyRemoteServiceConnected");
+        runWithExecutor(() -> mLocalListener.onRemoteServiceConnected());
+    }
+
+    /**
+     * Log the message to the radio buffer with {@code DEBUG} priority.
+     *
+     * @param log The message to log.
+     */
+    private static void logd(@NonNull String log) {
+        Rlog.d(TAG, log);
+    }
+
+    /**
+     * Log with error attribute
+     *
+     * @param s is string log
+     */
+    protected void loge(@NonNull String s) {
+        Log.e(TAG, s);
+    }
+}