1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.phone.testapps.satellitetestapp;
18 
19 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
20 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
21 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE;
22 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS;
23 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
24 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
25 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
26 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS;
27 
28 import android.app.Activity;
29 import android.content.Intent;
30 import android.content.SharedPreferences;
31 import android.os.Bundle;
32 import android.telephony.satellite.EnableRequestAttributes;
33 import android.telephony.satellite.PointingInfo;
34 import android.telephony.satellite.SatelliteDatagram;
35 import android.telephony.satellite.SatelliteDatagramCallback;
36 import android.telephony.satellite.SatelliteManager;
37 import android.telephony.satellite.SatelliteModemStateCallback;
38 import android.telephony.satellite.SatelliteTransmissionUpdateCallback;
39 import android.telephony.satellite.stub.SatelliteResult;
40 import android.util.Log;
41 import android.view.View;
42 import android.view.View.OnClickListener;
43 import android.widget.TextView;
44 
45 import java.util.LinkedList;
46 import java.util.concurrent.LinkedBlockingQueue;
47 import java.util.concurrent.TimeUnit;
48 import java.util.function.Consumer;
49 
50 /**
51  * Activity related to Datagram APIs.
52  */
53 public class Datagram extends Activity {
54 
55     private static final String TAG = "Datagram";
56     private static final int MAX_NUMBER_OF_STORED_STATES = 3;
57     private int mTransferState;
58     private int mModemState;
59     LinkedList<Integer> mModemStateQueue = new LinkedList<>();
60     LinkedList<Integer> mSendQueue = new LinkedList<>();
61     LinkedList<Integer> mReceiveQueue = new LinkedList<>();
62 
63     private SatelliteManager mSatelliteManager;
64     private SatelliteDatagramCallbackTestApp mDatagramCallback;
65     private SatelliteModemStateCallbackTestApp mStateCallback;
66     private SatelliteTransmissionUpdateCallbackTestApp mCallback;
67     private android.telephony.satellite.stub.SatelliteDatagram mReceivedDatagram;
68 
69     private String mShowSatelliteModemStateTransition;
70     private String mShowDatagramSendStateTransition;
71     private String mShowDatagramReceiveStateTransition;
72     private static final long TIMEOUT = 3000;
73 
74     @Override
onCreate(Bundle savedInstanceState)75     public void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77         mSatelliteManager = getSystemService(SatelliteManager.class);
78         mDatagramCallback = new SatelliteDatagramCallbackTestApp();
79         mStateCallback = new SatelliteModemStateCallbackTestApp();
80         mCallback = new SatelliteTransmissionUpdateCallbackTestApp();
81 
82         mReceivedDatagram = new android.telephony.satellite.stub.SatelliteDatagram();
83 
84         setContentView(R.layout.activity_Datagram);
85         findViewById(R.id.startSatelliteTransmissionUpdates)
86                 .setOnClickListener(this::startTransmissionUpdatesApp);
87         findViewById(R.id.stopSatelliteTransmissionUpdates)
88                 .setOnClickListener(this::stopTransmissionUpdatesApp);
89         findViewById(R.id.pollPendingSatelliteDatagrams)
90                 .setOnClickListener(this::pollPendingDatagramsApp);
91         findViewById(R.id.sendSatelliteDatagram)
92                 .setOnClickListener(this::sendDatagramApp);
93         findViewById(R.id.registerForSatelliteDatagram)
94                 .setOnClickListener(this::registerForIncomingDatagramApp);
95         findViewById(R.id.unregisterForSatelliteDatagram)
96                 .setOnClickListener(this::unregisterForIncomingDatagramApp);
97         findViewById(R.id.showDatagramSendStateTransition)
98                 .setOnClickListener(this::showDatagramSendStateTransitionApp);
99         findViewById(R.id.showDatagramReceiveStateTransition)
100                 .setOnClickListener(this::showDatagramReceiveStateTransitionApp);
101         findViewById(R.id.registerForSatelliteModemStateChanged)
102                 .setOnClickListener(this::registerForModemStateChangedApp);
103         findViewById(R.id.unregisterForSatelliteModemStateChanged)
104                 .setOnClickListener(this::unregisterForModemStateChangedApp);
105         findViewById(R.id.showSatelliteModemStateTransition)
106                 .setOnClickListener(this::showSatelliteModemStateTransitionApp);
107 
108         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
109             @Override
110             public void onClick(View view) {
111                 startActivity(new Intent(Datagram.this, SatelliteTestApp.class));
112             }
113         });
114     }
115 
116     protected class SatelliteDatagramCallbackTestApp implements SatelliteDatagramCallback {
117         @Override
onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram, int pendingCount, Consumer<Void> callback)118         public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
119                 int pendingCount, Consumer<Void> callback) {
120             Log.d(TAG, "onSatelliteDatagramReceived in TestApp: datagramId =" + datagramId
121                     + ", datagram =" + datagram + ", pendingCount=" + pendingCount);
122         }
123     }
124 
125     protected class SatelliteModemStateCallbackTestApp implements SatelliteModemStateCallback {
126         @Override
onSatelliteModemStateChanged(int state)127         public void onSatelliteModemStateChanged(int state) {
128             mModemState = state;
129             mModemStateQueue.addLast(state);
130             if (mModemStateQueue.size() > MAX_NUMBER_OF_STORED_STATES) {
131                 mModemStateQueue.removeFirst();
132             }
133             mShowSatelliteModemStateTransition = getSatelliteModemStateTransition(mModemStateQueue);
134             Log.d(TAG, "onSatelliteModemStateChanged in TestApp: state=" + mModemState);
135         }
136     }
137     protected class SatelliteTransmissionUpdateCallbackTestApp implements
138             SatelliteTransmissionUpdateCallback {
139         @Override
onSatellitePositionChanged(PointingInfo pointingInfo)140         public void onSatellitePositionChanged(PointingInfo pointingInfo) {
141             Log.d(TAG, "onSatellitePositionChanged in TestApp: pointingInfo =" + pointingInfo);
142         }
143 
144         @Override
onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode)145         public void onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode) {
146             mTransferState = state;
147             mSendQueue.addLast(state);
148             if (mSendQueue.size() > MAX_NUMBER_OF_STORED_STATES) {
149                 mSendQueue.removeFirst();
150             }
151             mShowDatagramSendStateTransition = getTransferStateTransition(mSendQueue);
152             Log.d(TAG, "onSendDatagramStateChanged in TestApp: state =" + mTransferState
153                     + ", sendPendingCount =" + sendPendingCount + ", errorCode=" + errorCode);
154         }
155 
156         @Override
onSendDatagramStateChanged( int datagramType, int state, int sendPendingCount, int errorCode)157         public void onSendDatagramStateChanged(
158                 int datagramType, int state, int sendPendingCount, int errorCode) {
159             Log.d(TAG, "onSendDatagramStateChanged in TestApp: datagramType = " + datagramType
160                     + ", state =" + mTransferState + ", sendPendingCount =" + sendPendingCount
161                     + ", errorCode=" + errorCode);
162         }
163 
164         @Override
onReceiveDatagramStateChanged( int state, int receivePendingCount, int errorCode)165         public void onReceiveDatagramStateChanged(
166                 int state, int receivePendingCount, int errorCode) {
167             mTransferState = state;
168             mReceiveQueue.addLast(state);
169             if (mReceiveQueue.size() > MAX_NUMBER_OF_STORED_STATES) {
170                 mReceiveQueue.removeFirst();
171             }
172             mShowDatagramReceiveStateTransition = getTransferStateTransition(mReceiveQueue);
173             Log.d(TAG, "onReceiveDatagramStateChanged in TestApp: state=" + mTransferState
174                     + ", receivePendingCount=" + receivePendingCount + ", errorCode=" + errorCode);
175         }
176     }
177 
startTransmissionUpdatesApp(View view)178     private void startTransmissionUpdatesApp(View view) {
179         TextView textView = findViewById(R.id.text_id);
180         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
181         mSatelliteManager.requestEnabled(
182                 new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
183                 Runnable::run, error::offer);
184         TextView showErrorStatusTextView = findViewById(R.id.showErrorStatus);
185         try {
186             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
187             if (value == null) {
188                 showErrorStatusTextView.setText("Timed out to enable the satellite");
189                 return;
190             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
191                 showErrorStatusTextView.setText("Failed to enable satellite, error = "
192                         + SatelliteErrorUtils.mapError(value));
193                 return;
194             }
195         } catch (InterruptedException e) {
196             showErrorStatusTextView.setText("Enable SatelliteService exception caught = " + e);
197             return;
198         }
199         error.clear();
200         mSatelliteManager.startTransmissionUpdates(Runnable::run, error::offer, mCallback);
201         try {
202             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
203             if (value == null) {
204                 textView.setText("Timed out to startSatelliteTransmissionUpdates");
205             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
206                 textView.setText("Failed to startSatelliteTransmissionUpdates with error = "
207                         + SatelliteErrorUtils.mapError(value));
208             } else {
209                 textView.setText("startSatelliteTransmissionUpdates is successful");
210             }
211         } catch (InterruptedException e) {
212             textView.setText("startSatelliteTransmissionUpdates exception caught =" + e);
213         }
214     }
215 
stopTransmissionUpdatesApp(View view)216     private void stopTransmissionUpdatesApp(View view) {
217         TextView textView = findViewById(R.id.text_id);
218         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
219         mSatelliteManager.stopTransmissionUpdates(mCallback, Runnable::run, error::offer);
220         try {
221             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
222             if (value == null) {
223                 textView.setText("Timed out to stopSatelliteTransmissionUpdates");
224             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
225                 textView.setText("Failed to stopSatelliteTransmissionUpdates with error = "
226                         + SatelliteErrorUtils.mapError(value));
227             } else {
228                 textView.setText("stopSatelliteTransmissionUpdates is successful");
229             }
230         } catch (InterruptedException e) {
231             textView.setText("stopSatelliteTransmissionUpdates exception caught =" + e);
232         }
233     }
pollPendingDatagramsApp(View view)234     private void pollPendingDatagramsApp(View view) {
235         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
236         TextView showErrorStatusTextView = findViewById(R.id.showErrorStatus);
237         TextView textView = findViewById(R.id.text_id);
238         mSatelliteManager.setDeviceAlignedWithSatellite(true);
239         if (SatelliteTestApp.getTestSatelliteService() != null) {
240             SatelliteTestApp.getTestSatelliteService().sendOnPendingDatagrams();
241         }
242         mSatelliteManager.requestEnabled(
243                 new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
244                 Runnable::run, resultListener::offer);
245         try {
246             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
247             if (value == null) {
248                 showErrorStatusTextView.setText("Timed out to enable the satellite");
249                 return;
250             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
251                 showErrorStatusTextView.setText("Failed to enable satellite, error = "
252                         + SatelliteErrorUtils.mapError(value));
253                 return;
254             }
255             resultListener.clear();
256             Log.d(TAG, "Poll to check queue is cleared = "
257                     + resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS));
258         } catch (InterruptedException e) {
259             showErrorStatusTextView.setText("Enable SatelliteService exception caught = " + e);
260             return;
261         }
262         mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
263         try {
264             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
265             if (value == null) {
266                 textView.setText("Timed out for poll message");
267             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
268                 textView.setText("Failed to pollPendingDatagrams with error = "
269                         + SatelliteErrorUtils.mapError(value));
270             } else {
271                 textView.setText("pollPendingDatagrams is successful");
272             }
273         } catch (InterruptedException e) {
274             textView.setText("pollPendingDatagrams exception caught =" + e);
275         }
276     }
277 
sendDatagramApp(View view)278     private void sendDatagramApp(View view) {
279         TextView textView = findViewById(R.id.text_id);
280         mSatelliteManager.setDeviceAlignedWithSatellite(true);
281         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
282         String mText = "This is a test datagram message";
283         SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
284         mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
285                 datagram, true, Runnable::run, resultListener::offer);
286         try {
287             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
288             if (value == null) {
289                 textView.setText("Timed out for sendDatagram");
290             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
291                 textView.setText("Failed to sendDatagram with error = "
292                         + SatelliteErrorUtils.mapError(value));
293             } else {
294                 textView.setText("sendDatagram is successful");
295             }
296         } catch (InterruptedException e) {
297             textView.setText("sendDatagram exception caught =" + e);
298         }
299     }
300 
registerForIncomingDatagramApp(View view)301     private void registerForIncomingDatagramApp(View view) {
302         int result = mSatelliteManager.registerForIncomingDatagram(Runnable::run,
303                 mDatagramCallback);
304         TextView textView = findViewById(R.id.text_id);
305         if (result == 0) {
306             textView.setText("registerForIncomingDatagram is successful");
307         } else {
308             textView.setText("Status for registerForIncomingDatagram : "
309                     + SatelliteErrorUtils.mapError(result));
310         }
311     }
312 
unregisterForIncomingDatagramApp(View view)313     private void unregisterForIncomingDatagramApp(View view) {
314         mSatelliteManager.unregisterForIncomingDatagram(mDatagramCallback);
315         TextView textView = findViewById(R.id.text_id);
316         textView.setText("unregisterForIncomingDatagram is successful");
317     }
318 
showDatagramSendStateTransitionApp(View view)319     private void showDatagramSendStateTransitionApp(View view) {
320         TextView textView = findViewById(R.id.text_id);
321         textView.setText("Last datagram send state transition is : "
322                 + mShowDatagramSendStateTransition);
323     }
324 
showDatagramReceiveStateTransitionApp(View view)325     private void showDatagramReceiveStateTransitionApp(View view) {
326         TextView textView = findViewById(R.id.text_id);
327         textView.setText("Last datagram receive state transition is : "
328                 + mShowDatagramReceiveStateTransition);
329     }
330 
registerForModemStateChangedApp(View view)331     private void registerForModemStateChangedApp(View view) {
332         int result = mSatelliteManager.registerForModemStateChanged(Runnable::run,
333                 mStateCallback);
334         TextView textView = findViewById(R.id.text_id);
335         if (result == 0) {
336             textView.setText("registerForModemStateChanged is successful");
337         } else {
338             textView.setText("Status for registerForModemStateChanged : "
339                     + SatelliteErrorUtils.mapError(result));
340         }
341     }
342 
unregisterForModemStateChangedApp(View view)343     private void unregisterForModemStateChangedApp(View view) {
344         mSatelliteManager.unregisterForModemStateChanged(mStateCallback);
345         TextView textView = findViewById(R.id.text_id);
346         textView.setText("unregisterForModemStateChanged is successful");
347     }
348 
showSatelliteModemStateTransitionApp(View view)349     private void showSatelliteModemStateTransitionApp(View view) {
350         TextView textView = findViewById(R.id.text_id);
351         textView.setText(
352                     "Last modem transition state is: " + mShowSatelliteModemStateTransition);
353     }
354 
getSatelliteModemStateName(@atelliteManager.SatelliteModemState int state)355     private String getSatelliteModemStateName(@SatelliteManager.SatelliteModemState int state) {
356         switch (state) {
357             case SatelliteManager.SATELLITE_MODEM_STATE_IDLE:
358                 return "IDLE";
359             case SatelliteManager.SATELLITE_MODEM_STATE_LISTENING:
360                 return "LISTENING";
361             case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING:
362                 return "DATAGRAM_TRANSFERRING";
363             case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING:
364                 return "DATAGRAM_RETRYING";
365             case SatelliteManager.SATELLITE_MODEM_STATE_OFF:
366                 return "OFF";
367             case SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE:
368                 return "UNAVAILABLE";
369             default: return "UNKNOWN";
370         }
371     }
372 
getSatelliteModemStateTransition(LinkedList<Integer> states)373     private String getSatelliteModemStateTransition(LinkedList<Integer> states) {
374         StringBuilder sb = new StringBuilder();
375         for (int state : states) {
376             sb.append(getSatelliteModemStateName(state));
377             sb.append("=>");
378         }
379         if (!sb.isEmpty()) {
380             sb.delete(sb.length() - 2, sb.length());
381         }
382         return sb.toString();
383     }
384 
getDatagramTransferStateName( @atelliteManager.SatelliteDatagramTransferState int state)385     private String getDatagramTransferStateName(
386             @SatelliteManager.SatelliteDatagramTransferState int state) {
387         switch (state) {
388             case SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE: return "IDLE";
389             case SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING: return "SENDING";
390             case SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS: return "SEND_SUCCESS";
391             case SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED: return "SEND_FAILED";
392             case SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING: return "RECEIVING";
393             case SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS: return "RECEIVE_SUCCESS";
394             case SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE: return "RECEIVE_NONE";
395             case SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED: return "RECEIVE_FAILED";
396             default: return "UNKNOWN";
397         }
398     }
399 
getTransferStateTransition(LinkedList<Integer> states)400     private String getTransferStateTransition(LinkedList<Integer> states) {
401         StringBuilder sb = new StringBuilder();
402         for (int state : states) {
403             sb.append(getDatagramTransferStateName(state));
404             sb.append("=>");
405         }
406         if (!sb.isEmpty()) {
407             sb.delete(sb.length() - 2, sb.length());
408         }
409         return sb.toString();
410     }
411 
412     @Override
onResume()413     protected void onResume() {
414         super.onResume();
415         SharedPreferences sh = getSharedPreferences("TestSatelliteSharedPref", MODE_PRIVATE);
416         String modemStateTransition = sh.getString("modem_state",
417                 mShowSatelliteModemStateTransition);
418         String datagramSendStateTransition = sh.getString("datagram_send_state",
419                 mShowDatagramSendStateTransition);
420         String datagramReceiveStateTransition = sh.getString("datagram_receive_state",
421                 mShowDatagramReceiveStateTransition);
422 
423         // Setting the fetched data
424         mShowSatelliteModemStateTransition = modemStateTransition;
425         mShowDatagramSendStateTransition = datagramSendStateTransition;
426         mShowDatagramReceiveStateTransition = datagramReceiveStateTransition;
427     }
428 
429     @Override
onPause()430     protected void onPause() {
431         super.onPause();
432         SharedPreferences sharedPreferences = getSharedPreferences("TestSatelliteSharedPref",
433                     MODE_PRIVATE);
434         SharedPreferences.Editor myEdit = sharedPreferences.edit();
435 
436         myEdit.putString("modem_state", mShowSatelliteModemStateTransition);
437         myEdit.putString("datagram_send_state", mShowDatagramSendStateTransition);
438         myEdit.putString("datagram_receive_state", mShowDatagramReceiveStateTransition);
439         myEdit.apply();
440     }
441 
onDestroy()442     protected void onDestroy() {
443         super.onDestroy();
444         SharedPreferences sharedPreferences = getSharedPreferences("TestSatelliteSharedPref",
445                     MODE_PRIVATE);
446         final SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit();
447 
448         sharedPrefsEditor.remove("modem_state");
449         sharedPrefsEditor.remove("datagram_send_state");
450         sharedPrefsEditor.remove("datagram_receive_state");
451         sharedPrefsEditor.commit();
452     }
453 }
454