• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.telephony.satellite.cts;
18 
19 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
20 
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertNull;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28 
29 import android.Manifest;
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.bluetooth.BluetoothAdapter;
33 import android.content.BroadcastReceiver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.PackageManager;
37 import android.database.ContentObserver;
38 import android.net.wifi.WifiManager;
39 import android.nfc.NfcAdapter;
40 import android.os.Handler;
41 import android.os.Looper;
42 import android.os.OutcomeReceiver;
43 import android.os.PersistableBundle;
44 import android.provider.Settings;
45 import android.telephony.CarrierConfigManager;
46 import android.telephony.Rlog;
47 import android.telephony.SubscriptionInfo;
48 import android.telephony.SubscriptionManager;
49 import android.telephony.TelephonyManager;
50 import android.telephony.cts.SatelliteReceiver;
51 import android.telephony.cts.TelephonyManagerTest.ServiceStateRadioStateListener;
52 import android.telephony.satellite.EarfcnRange;
53 import android.telephony.satellite.EnableRequestAttributes;
54 import android.telephony.satellite.NtnSignalStrength;
55 import android.telephony.satellite.NtnSignalStrengthCallback;
56 import android.telephony.satellite.PointingInfo;
57 import android.telephony.satellite.SatelliteAccessConfiguration;
58 import android.telephony.satellite.SatelliteCapabilities;
59 import android.telephony.satellite.SatelliteCapabilitiesCallback;
60 import android.telephony.satellite.SatelliteCommunicationAccessStateCallback;
61 import android.telephony.satellite.SatelliteDatagram;
62 import android.telephony.satellite.SatelliteDatagramCallback;
63 import android.telephony.satellite.SatelliteDisallowedReasonsCallback;
64 import android.telephony.satellite.SatelliteInfo;
65 import android.telephony.satellite.SatelliteManager;
66 import android.telephony.satellite.SatelliteModemStateCallback;
67 import android.telephony.satellite.SatellitePosition;
68 import android.telephony.satellite.SatelliteProvisionStateCallback;
69 import android.telephony.satellite.SatelliteSubscriberInfo;
70 import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
71 import android.telephony.satellite.SatelliteTransmissionUpdateCallback;
72 import android.telephony.satellite.SelectedNbIotSatelliteSubscriptionCallback;
73 import android.telephony.satellite.SystemSelectionSpecifier;
74 import android.text.TextUtils;
75 import android.util.Log;
76 import android.util.Pair;
77 import android.uwb.UwbManager;
78 
79 import androidx.test.InstrumentationRegistry;
80 
81 import com.android.compatibility.common.util.ShellIdentityUtils;
82 
83 import java.util.ArrayList;
84 import java.util.Arrays;
85 import java.util.List;
86 import java.util.UUID;
87 import java.util.concurrent.CountDownLatch;
88 import java.util.concurrent.LinkedBlockingQueue;
89 import java.util.concurrent.Semaphore;
90 import java.util.concurrent.TimeUnit;
91 import java.util.concurrent.atomic.AtomicReference;
92 import java.util.function.Consumer;
93 import java.util.stream.Collectors;
94 import java.util.stream.IntStream;
95 
96 public class SatelliteManagerTestBase {
97     protected static String TAG = "SatelliteManagerTestBase";
98 
99     protected static final String TOKEN = "TEST_TOKEN";
100     protected static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
101     public static final int RADIO_HAL_VERSION_2_3 = makeRadioVersion(2, 3);
102 
103     /**
104      * Since SST sets waiting time up to 10 seconds for the power off radio, the timer waiting for
105      * radio power state change should be greater than 10 seconds.
106      */
107     protected static final long EXTERNAL_DEPENDENT_TIMEOUT = TimeUnit.SECONDS.toMillis(15);
108 
109     protected static SatelliteManager sSatelliteManager;
110     protected static TelephonyManager sTelephonyManager = null;
111 
112     protected UwbManager mUwbManager = null;
113     protected NfcAdapter mNfcAdapter = null;
114     protected BluetoothAdapter mBluetoothAdapter = null;
115     protected WifiManager mWifiManager = null;
116 
beforeAllTestsBase()117     protected static void beforeAllTestsBase() {
118         sSatelliteManager = getContext().getSystemService(SatelliteManager.class);
119         sTelephonyManager = getContext().getSystemService(TelephonyManager.class);
120         turnRadioOn();
121     }
122 
afterAllTestsBase()123     protected static void afterAllTestsBase() {
124         sSatelliteManager = null;
125         sTelephonyManager = null;
126     }
127 
shouldTestSatellite()128     protected static boolean shouldTestSatellite() {
129         if (!getContext().getPackageManager().hasSystemFeature(
130                 PackageManager.FEATURE_TELEPHONY_SATELLITE)) {
131             logd("Skipping tests because FEATURE_TELEPHONY_SATELLITE is not available");
132             return false;
133         }
134         try {
135             getContext().getSystemService(TelephonyManager.class)
136                     .getHalVersion(TelephonyManager.HAL_SERVICE_RADIO);
137         } catch (IllegalStateException e) {
138             logd("Skipping tests because Telephony service is null, exception=" + e);
139             return false;
140         }
141         return true;
142     }
143 
shouldTestSatelliteWithMockService()144     protected static boolean shouldTestSatelliteWithMockService() {
145         if (!getContext().getPackageManager().hasSystemFeature(
146                 PackageManager.FEATURE_TELEPHONY)) {
147             logd("Skipping tests because FEATURE_TELEPHONY is not available");
148             return false;
149         }
150         if (!getContext().getPackageManager().hasSystemFeature(
151                 PackageManager.FEATURE_TELEPHONY_SATELLITE)) {
152             // Satellite test against mock service should pass on satellite-less devices, but it's
153             // still too flaky.
154             logd("Skipping tests because FEATURE_TELEPHONY_SATELLITE is not available");
155             return false;
156         }
157         try {
158             getContext().getSystemService(TelephonyManager.class)
159                     .getHalVersion(TelephonyManager.HAL_SERVICE_RADIO);
160         } catch (IllegalStateException e) {
161             logd("Skipping tests because Telephony service is null, exception=" + e);
162             return false;
163         }
164         return true;
165     }
166 
getContext()167     protected static Context getContext() {
168         return InstrumentationRegistry.getContext();
169     }
170 
grantSatellitePermission()171     protected static void grantSatellitePermission() {
172         InstrumentationRegistry.getInstrumentation().getUiAutomation()
173                 .adoptShellPermissionIdentity(Manifest.permission.SATELLITE_COMMUNICATION);
174     }
175 
revokeSatellitePermission()176     protected static void revokeSatellitePermission() {
177         InstrumentationRegistry.getInstrumentation().getUiAutomation()
178                 .dropShellPermissionIdentity();
179     }
180 
grantSatelliteAndReadBasicPhoneStatePermissions()181     protected static void grantSatelliteAndReadBasicPhoneStatePermissions() {
182         InstrumentationRegistry.getInstrumentation().getUiAutomation()
183                 .adoptShellPermissionIdentity(Manifest.permission.SATELLITE_COMMUNICATION,
184                         Manifest.permission.READ_BASIC_PHONE_STATE);
185     }
186 
187     protected static class SatelliteTransmissionUpdateCallbackTest implements
188             SatelliteTransmissionUpdateCallback {
189 
190         protected static final class DatagramStateChangeArgument {
191             protected int state;
192             protected int pendingCount;
193             protected int errorCode;
194             // Sending datagram type, should not be used as a comparison for the equals().
195             // Because receiving case there is no datagram type.
196             protected int datagramType;
197 
DatagramStateChangeArgument(int state, int pendingCount, int errorCode)198             DatagramStateChangeArgument(int state, int pendingCount, int errorCode) {
199                 this.state = state;
200                 this.pendingCount = pendingCount;
201                 this.errorCode = errorCode;
202                 this.datagramType = DATAGRAM_TYPE_UNKNOWN;
203             }
204 
DatagramStateChangeArgument(int datagramType, int state, int pendingCount, int errorCode)205             DatagramStateChangeArgument(int datagramType, int state, int pendingCount, int errorCode) {
206                 this.datagramType = datagramType;
207                 this.state = state;
208                 this.pendingCount = pendingCount;
209                 this.errorCode = errorCode;
210             }
211 
212             @Override
equals(Object other)213             public boolean equals(Object other) {
214                 if (this == other) return true;
215                 if (other == null || getClass() != other.getClass()) return false;
216                 DatagramStateChangeArgument that = (DatagramStateChangeArgument) other;
217                 return state == that.state
218                         && pendingCount  == that.pendingCount
219                         && errorCode == that.errorCode;
220             }
221 
222             @Override
toString()223             public String toString() {
224                 return ("state: " + state + " pendingCount: " + pendingCount
225                         + " errorCode: " + errorCode);
226             }
227         }
228 
229         public PointingInfo mPointingInfo;
230         private final Semaphore mPositionChangeSemaphore = new Semaphore(0);
231         private List<DatagramStateChangeArgument> mSendDatagramStateChanges = new ArrayList<>();
232         private final Object mSendDatagramStateChangesLock = new Object();
233         private final Semaphore mSendSemaphore = new Semaphore(0);
234         private List<DatagramStateChangeArgument> mReceiveDatagramStateChanges = new ArrayList<>();
235         private final Object mReceiveDatagramStateChangesLock = new Object();
236         private final Semaphore mReceiveSemaphore = new Semaphore(0);
237         private final Object mSendDatagramRequestedLock = new Object();
238         private final Semaphore mSendDatagramRequestedSemaphore = new Semaphore(0);
239         @SatelliteManager.DatagramType
240         private List<Integer> mSendDatagramRequestedList = new ArrayList<>();
241 
242         @Override
onSatellitePositionChanged(PointingInfo pointingInfo)243         public void onSatellitePositionChanged(PointingInfo pointingInfo) {
244             logd("onSatellitePositionChanged: pointingInfo=" + pointingInfo);
245             mPointingInfo = pointingInfo;
246 
247             try {
248                 mPositionChangeSemaphore.release();
249             } catch (Exception e) {
250                 loge("onSatellitePositionChanged: Got exception, ex=" + e);
251             }
252         }
253 
254         @Override
onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode)255         public void onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode) {
256             logd("onSendDatagramStateChanged: state=" + state + ", sendPendingCount="
257                     + sendPendingCount + ", errorCode=" + errorCode);
258             // Implementation moved to onSendDatagramStateChanged(int, int, int, int)
259             // This callback is called only for backward compatibility after calling
260             // onSendDatagramStateChanged(int, int, int, int). This call flows is guaranteed by
261             // SatelliteManager#startTransmissionUpdates().
262         }
263 
264         @Override
onSendDatagramStateChanged( int datagramType, int state, int sendPendingCount, int errorCode)265         public void onSendDatagramStateChanged(
266                 int datagramType, int state, int sendPendingCount, int errorCode) {
267             logd("onSendDatagramStateChanged:datagramType=" + datagramType + ", state=" + state
268                     + ", sendPendingCount=" + sendPendingCount + ", errorCode=" + errorCode);
269             synchronized (mSendDatagramStateChangesLock) {
270                 mSendDatagramStateChanges.add(new DatagramStateChangeArgument(datagramType,
271                         state, sendPendingCount, errorCode));
272             }
273 
274             try {
275                 mSendSemaphore.release();
276             } catch (Exception e) {
277                 loge("onSendDatagramStateChanged: Got exception, ex=" + e);
278             }
279         }
280 
281         @Override
onReceiveDatagramStateChanged( int state, int receivePendingCount, int errorCode)282         public void onReceiveDatagramStateChanged(
283                 int state, int receivePendingCount, int errorCode) {
284             logd("onReceiveDatagramStateChanged: state=" + state + ", "
285                     + "receivePendingCount=" + receivePendingCount + ", errorCode=" + errorCode);
286             synchronized (mReceiveDatagramStateChangesLock) {
287                 mReceiveDatagramStateChanges.add(new DatagramStateChangeArgument(state,
288                         receivePendingCount, errorCode));
289             }
290 
291             try {
292                 mReceiveSemaphore.release();
293             } catch (Exception e) {
294                 loge("onReceiveDatagramStateChanged: Got exception, ex=" + e);
295             }
296         }
297 
298         @Override
onSendDatagramRequested(int datagramType)299         public void onSendDatagramRequested(int datagramType) {
300             logd("onSendDatagramRequested: datagramType=" + datagramType);
301             synchronized (mSendDatagramRequestedLock) {
302                 mSendDatagramRequestedList.add(datagramType);
303             }
304 
305             try {
306                 mSendDatagramRequestedSemaphore.release();
307             } catch (Exception e) {
308                 loge("onSendDatagramRequested: Got exception, ex=" + e);
309             }
310         }
311 
waitUntilOnSatellitePositionChanged(int expectedNumberOfEvents)312         public boolean waitUntilOnSatellitePositionChanged(int expectedNumberOfEvents) {
313             for (int i = 0; i < expectedNumberOfEvents; i++) {
314                 try {
315                     if (!mPositionChangeSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
316                         loge("Timeout to receive onSatellitePositionChanged() callback");
317                         return false;
318                     }
319                 } catch (Exception ex) {
320                     loge("SatelliteTransmissionUpdateCallback "
321                             + "waitUntilOnSatellitePositionChanged: Got exception=" + ex);
322                     return false;
323                 }
324             }
325             return true;
326         }
327 
waitUntilOnSendDatagramStateChanged(int expectedNumberOfEvents)328         public boolean waitUntilOnSendDatagramStateChanged(int expectedNumberOfEvents) {
329             logd(
330                     "waitUntilOnSendDatagramStateChanged expectedNumberOfEvents:"
331                             + expectedNumberOfEvents);
332             for (int i = 0; i < expectedNumberOfEvents; i++) {
333                 try {
334                     if (!mSendSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
335                         loge("Timeout to receive onSendDatagramStateChanged() callback");
336                         return false;
337                     }
338                 } catch (Exception ex) {
339                     loge("SatelliteTransmissionUpdateCallback "
340                             + "waitUntilOnSendDatagramStateChanged: Got exception=" + ex);
341                     return false;
342                 }
343             }
344             return true;
345         }
346 
waitUntilOnReceiveDatagramStateChanged(int expectedNumberOfEvents)347         public boolean waitUntilOnReceiveDatagramStateChanged(int expectedNumberOfEvents) {
348             for (int i = 0; i < expectedNumberOfEvents; i++) {
349                 try {
350                     if (!mReceiveSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
351                         loge("Timeout to receive onReceiveDatagramStateChanged()");
352                         return false;
353                     }
354                 } catch (Exception ex) {
355                     loge("SatelliteTransmissionUpdateCallback "
356                             + "waitUntilOnReceiveDatagramStateChanged: Got exception=" + ex);
357                     return false;
358                 }
359             }
360             return true;
361         }
362 
waitUntilOnSendDatagramRequested(int expectedNumberOfEvents)363         public boolean waitUntilOnSendDatagramRequested(int expectedNumberOfEvents) {
364             logd("waitUntilOnSendDatagramRequested expectedNumberOfEvents:"
365                     + expectedNumberOfEvents);
366             for (int i = 0; i < expectedNumberOfEvents; i++) {
367                 try {
368                     if (!mSendDatagramRequestedSemaphore.tryAcquire(
369                             TIMEOUT, TimeUnit.MILLISECONDS)) {
370                         loge("Timeout to receive onSendDatagramRequested() callback");
371                         return false;
372                     }
373                 } catch (Exception ex) {
374                     loge("SatelliteTransmissionUpdateCallback "
375                             + "waitUntilOnSendDatagramRequested: Got exception=" + ex);
376                     return false;
377                 }
378             }
379             return true;
380         }
381 
clearPointingInfo()382         public void clearPointingInfo() {
383             mPointingInfo = null;
384             mPositionChangeSemaphore.drainPermits();
385         }
386 
clearSendDatagramStateChanges()387         public void clearSendDatagramStateChanges() {
388             synchronized (mSendDatagramStateChangesLock) {
389                 logd("clearSendDatagramStateChanges");
390                 mSendDatagramStateChanges.clear();
391                 mSendSemaphore.drainPermits();
392             }
393         }
394 
clearReceiveDatagramStateChanges()395         public void clearReceiveDatagramStateChanges() {
396             synchronized (mReceiveDatagramStateChangesLock) {
397                 logd("clearReceiveDatagramStateChanges");
398                 mReceiveDatagramStateChanges.clear();
399                 mReceiveSemaphore.drainPermits();
400             }
401         }
402 
clearSendDatagramRequested()403         public void clearSendDatagramRequested() {
404             synchronized (mSendDatagramRequestedLock) {
405                 logd("clearSendDatagramRequested");
406                 mSendDatagramRequestedList.clear();
407                 mSendDatagramRequestedSemaphore.drainPermits();
408             }
409         }
410 
411         @Nullable
getSendDatagramStateChange(int index)412         public DatagramStateChangeArgument getSendDatagramStateChange(int index) {
413             synchronized (mSendDatagramStateChangesLock) {
414                 if (index < mSendDatagramStateChanges.size()) {
415                     return mSendDatagramStateChanges.get(index);
416                 } else {
417                     Log.e(TAG, "getSendDatagramStateChange: invalid index= " + index
418                             + " mSendDatagramStateChanges.size= "
419                             + mSendDatagramStateChanges.size());
420                     return null;
421                 }
422             }
423         }
424 
425         @Nullable
getReceiveDatagramStateChange(int index)426         public DatagramStateChangeArgument getReceiveDatagramStateChange(int index) {
427             synchronized (mReceiveDatagramStateChangesLock) {
428                 if (index < mReceiveDatagramStateChanges.size()) {
429                     return mReceiveDatagramStateChanges.get(index);
430                 } else {
431                     Log.e(TAG, "getReceiveDatagramStateChange: invalid index= " + index
432                             + " mReceiveDatagramStateChanges.size= "
433                             + mReceiveDatagramStateChanges.size());
434                     return null;
435                 }
436             }
437         }
438 
getNumOfSendDatagramStateChanges()439         public int getNumOfSendDatagramStateChanges() {
440             synchronized (mSendDatagramStateChangesLock) {
441                 logd("getNumOfSendDatagramStateChanges size:" + mSendDatagramStateChanges.size());
442                 return mSendDatagramStateChanges.size();
443             }
444         }
445 
getNumOfReceiveDatagramStateChanges()446         public int getNumOfReceiveDatagramStateChanges() {
447             synchronized (mReceiveDatagramStateChangesLock) {
448                 return mReceiveDatagramStateChanges.size();
449             }
450         }
451 
452         @SatelliteManager.DatagramType
getSendDatagramRequestedType(int index)453         public int getSendDatagramRequestedType(int index) {
454             synchronized (mSendDatagramRequestedLock) {
455                 if (index < mSendDatagramRequestedList.size()) {
456                     return mSendDatagramRequestedList.get(index);
457                 } else {
458                     Log.e(TAG, "getSendDatagramRequestedType: invalid index= " + index
459                             + " mSendDatagramRequestedList.size= "
460                             + mSendDatagramRequestedList.size());
461                     return DATAGRAM_TYPE_UNKNOWN;
462                 }
463             }
464         }
465 
getNumOfSendDatagramRequestedChanges()466         public int getNumOfSendDatagramRequestedChanges() {
467             synchronized (mSendDatagramRequestedLock) {
468                 return mSendDatagramRequestedList.size();
469             }
470         }
471     }
472 
473     protected static class SatelliteProvisionStateCallbackTest implements
474             SatelliteProvisionStateCallback {
475         public boolean isProvisioned = false;
476         private List<Boolean> mProvisionedStates = new ArrayList<>();
477         private final Object mProvisionedStatesLock = new Object();
478         private final Semaphore mSemaphore = new Semaphore(0);
479 
480         @Override
onSatelliteProvisionStateChanged(boolean provisioned)481         public void onSatelliteProvisionStateChanged(boolean provisioned) {
482             logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
483             isProvisioned = provisioned;
484             synchronized (mProvisionedStatesLock) {
485                 mProvisionedStates.add(provisioned);
486             }
487             try {
488                 mSemaphore.release();
489             } catch (Exception ex) {
490                 loge("onSatelliteProvisionStateChanged: Got exception, ex=" + ex);
491             }
492         }
493 
waitUntilResult(int expectedNumberOfEvents)494         public boolean waitUntilResult(int expectedNumberOfEvents) {
495             for (int i = 0; i < expectedNumberOfEvents; i++) {
496                 try {
497                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
498                         loge("Timeout to receive onSatelliteProvisionStateChanged");
499                         return false;
500                     }
501                 } catch (Exception ex) {
502                     loge("onSatelliteProvisionStateChanged: Got exception=" + ex);
503                     return false;
504                 }
505             }
506             return true;
507         }
508 
clearProvisionedStates()509         public void clearProvisionedStates() {
510             synchronized (mProvisionedStatesLock) {
511                 mProvisionedStates.clear();
512                 mSemaphore.drainPermits();
513             }
514         }
515 
getTotalCountOfProvisionedStates()516         public int getTotalCountOfProvisionedStates() {
517             synchronized (mProvisionedStatesLock) {
518                 return mProvisionedStates.size();
519             }
520         }
521 
getProvisionedState(int index)522         public boolean getProvisionedState(int index) {
523             synchronized (mProvisionedStatesLock) {
524                 if (index < mProvisionedStates.size()) {
525                     return mProvisionedStates.get(index);
526                 }
527             }
528             loge("getProvisionedState: invalid index=" + index);
529             return false;
530         }
531     }
532 
533     protected static class SatelliteSubscriptionProvisionStateChangedTest implements
534             SatelliteProvisionStateCallback {
535         private List<SatelliteSubscriberProvisionStatus> mResultList = new ArrayList<>();
536         private final Object mProvisionedStatesLock = new Object();
537         private final Semaphore mSemaphore = new Semaphore(0);
538 
539         @Override
onSatelliteProvisionStateChanged(boolean provisioned)540         public void onSatelliteProvisionStateChanged(boolean provisioned) {
541             logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
542         }
543 
544         @Override
onSatelliteSubscriptionProvisionStateChanged( List<SatelliteSubscriberProvisionStatus> list)545         public void onSatelliteSubscriptionProvisionStateChanged(
546                 List<SatelliteSubscriberProvisionStatus> list) {
547             logd("onSatelliteSubscriptionProvisionStateChanged:" + list);
548             synchronized (mProvisionedStatesLock) {
549                 mResultList = list;
550             }
551             try {
552                 mSemaphore.release();
553             } catch (Exception ex) {
554                 loge("onSatelliteSubscriptionProvisionStateChanged: Got exception, ex=" + ex);
555             }
556         }
557 
waitUntilResult(int expectedNumberOfEvents)558         public boolean waitUntilResult(int expectedNumberOfEvents) {
559             for (int i = 0; i < expectedNumberOfEvents; i++) {
560                 try {
561                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
562                         loge("Timeout to receive onSatelliteSubscriptionProvisionStateChanged");
563                         return false;
564                     }
565                 } catch (Exception ex) {
566                     loge("onSatelliteSubscriptionProvisionStateChanged: Got exception=" + ex);
567                     return false;
568                 }
569             }
570             return true;
571         }
572 
clearProvisionedStates()573         public void clearProvisionedStates() {
574             synchronized (mProvisionedStatesLock) {
575                 mResultList.clear();
576                 mSemaphore.drainPermits();
577             }
578         }
579 
getResultList()580         public List<SatelliteSubscriberProvisionStatus> getResultList() {
581             synchronized (mProvisionedStatesLock) {
582                 return mResultList;
583             }
584         }
585     }
586 
587     protected static class SatelliteModemStateCallbackTest implements SatelliteModemStateCallback {
588         public int modemState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
589         private List<Integer> mModemStates = new ArrayList<>();
590         private final Object mModemStatesLock = new Object();
591         private final Semaphore mSemaphore = new Semaphore(0);
592         private final Semaphore mModemOffSemaphore = new Semaphore(0);
593 
594         @Override
onSatelliteModemStateChanged(int state)595         public void onSatelliteModemStateChanged(int state) {
596             Log.d(TAG, "onSatelliteModemStateChanged: state=" + state);
597             modemState = state;
598             synchronized (mModemStatesLock) {
599                 mModemStates.add(state);
600             }
601             try {
602                 mSemaphore.release();
603             } catch (Exception ex) {
604                 Log.e(TAG, "onSatelliteModemStateChanged: Got exception, ex=" + ex);
605             }
606 
607             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
608                 try {
609                     mModemOffSemaphore.release();
610                 } catch (Exception ex) {
611                     Log.e(TAG, "onSatelliteModemStateChanged: Got exception in "
612                             + "releasing mModemOffSemaphore, ex=" + ex);
613                 }
614             }
615         }
616 
waitUntilResult(int expectedNumberOfEvents)617         public boolean waitUntilResult(int expectedNumberOfEvents) {
618             for (int i = 0; i < expectedNumberOfEvents; i++) {
619                 try {
620                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
621                         Log.e(TAG, "Timeout to receive onSatelliteModemStateChanged");
622                         return false;
623                     }
624                 } catch (Exception ex) {
625                     Log.e(TAG, "onSatelliteModemStateChanged: Got exception=" + ex);
626                     return false;
627                 }
628             }
629             return true;
630         }
631 
waitUntilModemOff()632         public boolean waitUntilModemOff() {
633             try {
634                 if (!mModemOffSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
635                     Log.e(TAG, "Timeout to receive satellite modem off event");
636                     return false;
637                 }
638             } catch (Exception ex) {
639                 Log.e(TAG, "Waiting for satellite modem off event: Got exception=" + ex);
640                 return false;
641             }
642             return true;
643         }
644 
waitUntilModemOff(long timeoutMillis)645         public boolean waitUntilModemOff(long timeoutMillis) {
646             try {
647                 if (!mModemOffSemaphore.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS)) {
648                     Log.e(TAG, "Timeout to receive satellite modem off event");
649                     return false;
650                 }
651             } catch (Exception ex) {
652                 Log.e(TAG, "Waiting for satellite modem off event: Got exception=" + ex);
653                 return false;
654             }
655             return true;
656         }
657 
clearModemStates()658         public void clearModemStates() {
659             synchronized (mModemStatesLock) {
660                 Log.d(TAG, "onSatelliteModemStateChanged: clearModemStates");
661                 mModemStates.clear();
662                 mSemaphore.drainPermits();
663                 mModemOffSemaphore.drainPermits();
664             }
665         }
666 
getModemState(int index)667         public int getModemState(int index) {
668             synchronized (mModemStatesLock) {
669                 if (index < mModemStates.size()) {
670                     return mModemStates.get(index);
671                 } else {
672                     Log.e(TAG, "getModemState: invalid index=" + index
673                             + ", mModemStates.size=" + mModemStates.size());
674                     return -1;
675                 }
676             }
677         }
678 
getTotalCountOfModemStates()679         public int getTotalCountOfModemStates() {
680             synchronized (mModemStatesLock) {
681                 return mModemStates.size();
682             }
683         }
684     }
685 
686     protected static class SatelliteDatagramCallbackTest implements SatelliteDatagramCallback {
687         public SatelliteDatagram mDatagram;
688         public final List<SatelliteDatagram> mDatagramList = new ArrayList<>();
689         public long mDatagramId;
690         private final Semaphore mSemaphore = new Semaphore(0);
691 
692         @Override
onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram, int pendingCount, Consumer<Void> callback)693         public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
694                 int pendingCount, Consumer<Void> callback) {
695             logd("onSatelliteDatagramReceived: datagramId=" + datagramId + ", datagram="
696                     + datagram + ", pendingCount=" + pendingCount);
697             mDatagram = datagram;
698             mDatagramList.add(datagram);
699             mDatagramId = datagramId;
700             if (callback != null) {
701                 logd("onSatelliteDatagramReceived: callback.accept() datagramId=" + datagramId);
702                 callback.accept(null);
703             } else {
704                 logd("onSatelliteDatagramReceived: callback is null datagramId=" + datagramId);
705             }
706 
707             try {
708                 mSemaphore.release();
709             } catch (Exception e) {
710                 loge("onSatelliteDatagramReceived: Got exception, ex=" + e);
711             }
712         }
713 
waitUntilResult(int expectedNumberOfEvents)714         public boolean waitUntilResult(int expectedNumberOfEvents) {
715             for (int i = 0; i < expectedNumberOfEvents; i++) {
716                 try {
717                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
718                         loge("Timeout to receive onSatelliteDatagramReceived");
719                         return false;
720                     }
721                 } catch (Exception ex) {
722                     loge("onSatelliteDatagramReceived: Got exception=" + ex);
723                     return false;
724                 }
725             }
726             return true;
727         }
728     }
729 
730     protected static class NtnSignalStrengthCallbackTest implements NtnSignalStrengthCallback {
731         public NtnSignalStrength mNtnSignalStrength;
732         private final Semaphore mSemaphore = new Semaphore(0);
733 
734         @Override
onNtnSignalStrengthChanged(@onNull NtnSignalStrength ntnSignalStrength)735         public void onNtnSignalStrengthChanged(@NonNull NtnSignalStrength ntnSignalStrength) {
736             logd("onNtnSignalStrengthChanged: ntnSignalStrength=" + ntnSignalStrength);
737             mNtnSignalStrength = new NtnSignalStrength(ntnSignalStrength);
738 
739             try {
740                 mSemaphore.release();
741             } catch (Exception e) {
742                 loge("onNtnSignalStrengthChanged: Got exception, ex=" + e);
743             }
744         }
745 
waitUntilResult(int expectedNumberOfEvents)746         public boolean waitUntilResult(int expectedNumberOfEvents) {
747             for (int i = 0; i < expectedNumberOfEvents; i++) {
748                 try {
749                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
750                         loge("Timeout to receive onNtnSignalStrengthChanged");
751                         return false;
752                     }
753                 } catch (Exception ex) {
754                     loge("onNtnSignalStrengthChanged: Got exception=" + ex);
755                     return false;
756                 }
757             }
758             return true;
759         }
760 
drainPermits()761         public void drainPermits() {
762             mSemaphore.drainPermits();
763         }
764     }
765 
766     protected static class SatelliteCapabilitiesCallbackTest implements
767             SatelliteCapabilitiesCallback {
768         public SatelliteCapabilities mSatelliteCapabilities;
769         private final Semaphore mSemaphore = new Semaphore(0);
770 
771         @Override
onSatelliteCapabilitiesChanged( @onNull SatelliteCapabilities satelliteCapabilities)772         public void onSatelliteCapabilitiesChanged(
773                 @NonNull SatelliteCapabilities satelliteCapabilities) {
774             logd("onSatelliteCapabilitiesChanged: satelliteCapabilities=" + satelliteCapabilities);
775             mSatelliteCapabilities = satelliteCapabilities;
776 
777             try {
778                 mSemaphore.release();
779             } catch (Exception e) {
780                 loge("onSatelliteCapabilitiesChanged: Got exception, ex=" + e);
781             }
782         }
783 
waitUntilResult(int expectedNumberOfEvents)784         public boolean waitUntilResult(int expectedNumberOfEvents) {
785             for (int i = 0; i < expectedNumberOfEvents; i++) {
786                 try {
787                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
788                         loge("Timeout to receive onSatelliteCapabilitiesChanged");
789                         return false;
790                     }
791                 } catch (Exception ex) {
792                     loge("onSatelliteCapabilitiesChanged: Got exception=" + ex);
793                     return false;
794                 }
795             }
796             return true;
797         }
798     }
799 
800     protected static class SelectedNbIotSatelliteSubscriptionCallbackTest implements
801             SelectedNbIotSatelliteSubscriptionCallback {
802         public int mSelectedSubId;
803         private final Semaphore mSemaphore = new Semaphore(0);
804 
805         @Override
onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId)806         public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) {
807             logd("onSelectedNbIotSatelliteSubscriptionChanged: selectedSubId=" + selectedSubId);
808             mSelectedSubId = selectedSubId;
809 
810             try {
811                 mSemaphore.release();
812             } catch (Exception e) {
813                 loge("onSelectedNbIotSatelliteSubscriptionChanged: Got exception, ex=" + e);
814             }
815         }
816 
waitUntilResult(int expectedNumberOfEvents)817         public boolean waitUntilResult(int expectedNumberOfEvents) {
818             for (int i = 0; i < expectedNumberOfEvents; i++) {
819                 try {
820                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
821                         loge("Timeout to receive onSelectedNbIotSatelliteSubscriptionChanged");
822                         return false;
823                     }
824                 } catch (Exception ex) {
825                     loge("onSelectedNbIotSatelliteSubscriptionChanged: Got exception=" + ex);
826                     return false;
827                 }
828             }
829             return true;
830         }
831 
drainPermits()832         public void drainPermits() {
833             mSemaphore.drainPermits();
834         }
835     }
836 
837     protected static class SatelliteSupportedStateCallbackTest implements Consumer<Boolean> {
838         public boolean isSupported = false;
839         private List<Boolean> mSupportedStates = new ArrayList<>();
840         private final Object mSupportedStatesLock = new Object();
841         private final Semaphore mSemaphore = new Semaphore(0);
842 
843         @Override
accept(Boolean supported)844         public void accept(Boolean supported) {
845             logd("onSatelliteSupportedStateChanged: supported=" + supported);
846             isSupported = supported;
847             synchronized (mSupportedStatesLock) {
848                 mSupportedStates.add(supported);
849             }
850             try {
851                 mSemaphore.release();
852             } catch (Exception ex) {
853                 loge("onSatelliteSupportedStateChanged: Got exception, ex=" + ex);
854             }
855         }
856 
waitUntilResult(int expectedNumberOfEvents)857         public boolean waitUntilResult(int expectedNumberOfEvents) {
858             for (int i = 0; i < expectedNumberOfEvents; i++) {
859                 try {
860                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
861                         loge("Timeout to receive onSatelliteSupportedStateChanged");
862                         return false;
863                     }
864                 } catch (Exception ex) {
865                     loge("onSatelliteSupportedStateChanged: Got exception=" + ex);
866                     return false;
867                 }
868             }
869             return true;
870         }
871 
clearSupportedStates()872         public void clearSupportedStates() {
873             synchronized (mSupportedStatesLock) {
874                 mSupportedStates.clear();
875                 mSemaphore.drainPermits();
876             }
877         }
878 
getTotalCountOfSupportedStates()879         public int getTotalCountOfSupportedStates() {
880             synchronized (mSupportedStatesLock) {
881                 return mSupportedStates.size();
882             }
883         }
884 
getSupportedState(int index)885         public Boolean getSupportedState(int index) {
886             synchronized (mSupportedStatesLock) {
887                 if (index < mSupportedStates.size()) {
888                     return mSupportedStates.get(index);
889                 }
890             }
891             loge("getSupportedState: invalid index=" + index);
892             return null;
893         }
894     }
895 
896     protected static class SatelliteCommunicationAccessStateCallbackTest
897             implements SatelliteCommunicationAccessStateCallback {
898         public boolean isAllowed = false;
899         @Nullable
900         private SatelliteAccessConfiguration mSatelliteAccessConfiguration;
901         private final Semaphore mSemaphore = new Semaphore(0);
902         private final Semaphore mSatelliteAccessConfigurationChangedSemaphore = new Semaphore(0);
903 
904         @Override
onAccessAllowedStateChanged(boolean allowed)905         public void onAccessAllowedStateChanged(boolean allowed) {
906             logd("onAccessAllowedStateChanged: isAllowed=" + allowed);
907             isAllowed = allowed;
908 
909             try {
910                 mSemaphore.release();
911             } catch (Exception e) {
912                 loge("onAccessAllowedStateChanged: Got exception, ex=" + e);
913             }
914         }
915 
916         @Override
onAccessConfigurationChanged( @ullable SatelliteAccessConfiguration satelliteAccessConfiguration)917         public void onAccessConfigurationChanged(
918                 @Nullable SatelliteAccessConfiguration satelliteAccessConfiguration) {
919             logd(
920                     "onAccessConfigurationChanged: satelliteAccessConfiguration="
921                             + satelliteAccessConfiguration);
922             mSatelliteAccessConfiguration = satelliteAccessConfiguration;
923 
924             try {
925                 mSatelliteAccessConfigurationChangedSemaphore.release();
926             } catch (Exception e) {
927                 loge("onAccessConfigurationChanged: Got exception, ex=" + e);
928             }
929         }
930 
waitUntilResult(int expectedNumberOfEvents)931         public boolean waitUntilResult(int expectedNumberOfEvents) {
932             for (int i = 0; i < expectedNumberOfEvents; i++) {
933                 try {
934                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
935                         loge("Timeout to receive onAccessAllowedStateChanged");
936                         return false;
937                     }
938                 } catch (Exception ex) {
939                     loge("onAccessAllowedStateChanged: Got exception=" + ex);
940                     return false;
941                 }
942             }
943             return true;
944         }
945 
waitUntilSatelliteAccessConfigurationChangedEvent( int expectedNumberOfEvents, long timeOutMilliSec)946         public boolean waitUntilSatelliteAccessConfigurationChangedEvent(
947                 int expectedNumberOfEvents, long timeOutMilliSec) {
948             logd("waitUntilSatelliteAccessConfigurationChangedEvent");
949             for (int i = 0; i < expectedNumberOfEvents; i++) {
950                 try {
951                     if (!mSatelliteAccessConfigurationChangedSemaphore.tryAcquire(timeOutMilliSec,
952                             TimeUnit.MILLISECONDS)) {
953                         loge("Timeout to receive "
954                                 + "waitUntilSatelliteAccessConfigurationChangedEvent");
955                         return false;
956                     }
957                 } catch (Exception ex) {
958                     loge("waitUntilSatelliteAccessConfigurationChangedEvent: Got exception=" + ex);
959                     return false;
960                 }
961             }
962             return true;
963         }
964 
drainPermits()965         public void drainPermits() {
966             mSemaphore.drainPermits();
967             mSatelliteAccessConfigurationChangedSemaphore.drainPermits();
968         }
969 
970         @Nullable
getSatelliteAccessConfiguration()971         public SatelliteAccessConfiguration getSatelliteAccessConfiguration() {
972             return mSatelliteAccessConfiguration;
973         }
974     }
975 
976     protected static class SatelliteDisallowedReasonsCallbackTest
977             implements SatelliteDisallowedReasonsCallback {
978         private int[] mSatelliteDisabledReasons;
979         private final Semaphore mSemaphore = new Semaphore(0);
980 
981         @Override
onSatelliteDisallowedReasonsChanged(@onNull int[] disallowedReasons)982         public void onSatelliteDisallowedReasonsChanged(@NonNull int[] disallowedReasons) {
983             logd("onSatelliteDisallowedReasonsChanged: disallowedReasons="
984                      + Arrays.toString(disallowedReasons));
985             mSatelliteDisabledReasons = disallowedReasons;
986             try {
987                 mSemaphore.release();
988             } catch (Exception e) {
989                 loge("onSatelliteDisallowedReasonsChanged: Got exception, ex=" + e);
990             }
991         }
992 
waitUntilResult(int expectedNumberOfEvents)993         public boolean waitUntilResult(int expectedNumberOfEvents) {
994             for (int i = 0; i < expectedNumberOfEvents; i++) {
995                 try {
996                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
997                         loge("Timeout to receive onSatelliteDisallowedReasonsChanged");
998                         return false;
999                     }
1000                 } catch (Exception ex) {
1001                     loge("onSatelliteDisallowedReasonsChanged: Got exception=" + ex);
1002                     return false;
1003                 }
1004             }
1005             return true;
1006         }
1007 
drainPermits()1008         public void drainPermits() {
1009             mSemaphore.drainPermits();
1010         }
1011 
hasSatelliteDisabledReason(int reason)1012         public boolean hasSatelliteDisabledReason(int reason) {
1013             return Arrays.stream(mSatelliteDisabledReasons).anyMatch(i -> i == reason);
1014         }
1015     }
1016 
1017     protected static class SatelliteModeRadiosUpdater extends ContentObserver implements
1018             AutoCloseable {
1019         private final Context mContext;
1020         private final Semaphore mSemaphore = new Semaphore(0);
1021         private String mExpectedSatelliteModeRadios = "";
1022         private final Object mLock = new Object();
1023 
SatelliteModeRadiosUpdater(Context context)1024         public SatelliteModeRadiosUpdater(Context context) {
1025             super(new Handler(Looper.getMainLooper()));
1026             mContext = context;
1027             mContext.getContentResolver().registerContentObserver(
1028                     Settings.Global.getUriFor(Settings.Global.SATELLITE_MODE_RADIOS), false, this);
1029             InstrumentationRegistry.getInstrumentation().getUiAutomation()
1030                     .adoptShellPermissionIdentity(Manifest.permission.SATELLITE_COMMUNICATION,
1031                             Manifest.permission.WRITE_SECURE_SETTINGS,
1032                             Manifest.permission.NETWORK_SETTINGS,
1033                             Manifest.permission.ACCESS_FINE_LOCATION,
1034                             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1035                             Manifest.permission.UWB_PRIVILEGED);
1036         }
1037 
1038         @Override
onChange(boolean selfChange)1039         public void onChange(boolean selfChange) {
1040             String newSatelliteModeRadios = Settings.Global.getString(
1041                     mContext.getContentResolver(), Settings.Global.SATELLITE_MODE_RADIOS);
1042             synchronized (mLock) {
1043                 if (TextUtils.equals(mExpectedSatelliteModeRadios, newSatelliteModeRadios)) {
1044                     logd("SatelliteModeRadiosUpdater: onChange, newSatelliteModeRadios="
1045                             + newSatelliteModeRadios);
1046                     try {
1047                         mSemaphore.release();
1048                     } catch (Exception ex) {
1049                         loge("SatelliteModeRadiosUpdater: onChange, ex=" + ex);
1050                     }
1051                 }
1052             }
1053         }
1054 
1055         @Override
close()1056         public void close() throws Exception {
1057             mContext.getContentResolver().unregisterContentObserver(this);
1058             InstrumentationRegistry.getInstrumentation().getUiAutomation()
1059                     .dropShellPermissionIdentity();
1060         }
1061 
setSatelliteModeRadios(String expectedSatelliteModeRadios)1062         public boolean setSatelliteModeRadios(String expectedSatelliteModeRadios) {
1063             logd("setSatelliteModeRadios: expectedSatelliteModeRadios="
1064                     + expectedSatelliteModeRadios);
1065             String originalSatelliteModeRadios =  Settings.Global.getString(
1066                     mContext.getContentResolver(), Settings.Global.SATELLITE_MODE_RADIOS);
1067             if (TextUtils.equals(expectedSatelliteModeRadios, originalSatelliteModeRadios)) {
1068                 logd("setSatelliteModeRadios: satellite radios mode is already as expected");
1069                 return true;
1070             }
1071 
1072             setExpectedSatelliteModeRadios(expectedSatelliteModeRadios);
1073             clearSemaphorePermits();
1074             Settings.Global.putString(mContext.getContentResolver(),
1075                     Settings.Global.SATELLITE_MODE_RADIOS, expectedSatelliteModeRadios);
1076             return waitForModeChanged();
1077         }
1078 
clearSemaphorePermits()1079         private void clearSemaphorePermits() {
1080             mSemaphore.drainPermits();
1081         }
1082 
waitForModeChanged()1083         private boolean waitForModeChanged() {
1084             logd("SatelliteModeRadiosUpdater: waitForModeChanged start");
1085             try {
1086                 if (!mSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT, TimeUnit.MILLISECONDS)) {
1087                     loge("SatelliteModeRadiosUpdater: Timeout to wait for mode changed");
1088                     return false;
1089                 }
1090             } catch (InterruptedException e) {
1091                 loge("SatelliteModeRadiosUpdater: waitForModeChanged, e=" + e);
1092                 return false;
1093             }
1094             return true;
1095         }
1096 
setExpectedSatelliteModeRadios(String expectedSatelliteModeRadios)1097         private void setExpectedSatelliteModeRadios(String expectedSatelliteModeRadios) {
1098             synchronized (mLock) {
1099                 mExpectedSatelliteModeRadios = expectedSatelliteModeRadios;
1100             }
1101             logd("SatelliteModeRadiosUpdater: mExpectedSatelliteModeRadios="
1102                     + mExpectedSatelliteModeRadios);
1103         }
1104     }
1105 
provisionSatellite()1106     protected static boolean provisionSatellite() {
1107         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1108         String mText = "This is test provision data.";
1109         byte[] testProvisionData = mText.getBytes();
1110 
1111         sSatelliteManager.provisionService(
1112                 TOKEN, testProvisionData, null, getContext().getMainExecutor(), error::offer);
1113         Integer errorCode;
1114         try {
1115             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1116         } catch (InterruptedException ex) {
1117             loge("provisionSatellite ex=" + ex);
1118             return false;
1119         }
1120         if (errorCode == null || errorCode != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
1121             loge("provisionSatellite failed with errorCode=" + errorCode);
1122             return false;
1123         }
1124         return true;
1125     }
1126 
deprovisionSatellite()1127     protected static boolean deprovisionSatellite() {
1128         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1129 
1130         sSatelliteManager.deprovisionService(
1131                 TOKEN, getContext().getMainExecutor(), error::offer);
1132         Integer errorCode;
1133         try {
1134             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1135         } catch (InterruptedException ex) {
1136             loge("deprovisionSatellite ex=" + ex);
1137             return false;
1138         }
1139         if (errorCode == null || errorCode != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
1140             loge("deprovisionSatellite failed with errorCode=" + errorCode);
1141             return false;
1142         }
1143         return true;
1144     }
1145 
isSatelliteProvisioned()1146     protected static boolean isSatelliteProvisioned() {
1147         final AtomicReference<Boolean> provisioned = new AtomicReference<>();
1148         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1149         CountDownLatch latch = new CountDownLatch(1);
1150         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1151                 new OutcomeReceiver<>() {
1152                     @Override
1153                     public void onResult(Boolean result) {
1154                         logd("isSatelliteProvisioned: result=" + result);
1155                         provisioned.set(result);
1156                         latch.countDown();
1157                     }
1158 
1159                     @Override
1160                     public void onError(SatelliteManager.SatelliteException exception) {
1161                         logd("isSatelliteProvisioned: onError, exception=" + exception);
1162                         errorCode.set(exception.getErrorCode());
1163                         latch.countDown();
1164                     }
1165                 };
1166 
1167         sSatelliteManager.requestIsProvisioned(
1168                 getContext().getMainExecutor(), receiver);
1169         try {
1170             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1171         } catch (InterruptedException ex) {
1172             loge("isSatelliteProvisioned ex=" + ex);
1173             return false;
1174         }
1175 
1176         Integer error = errorCode.get();
1177         Boolean isProvisioned = provisioned.get();
1178         if (error == null) {
1179             logd("isSatelliteProvisioned isProvisioned=" + isProvisioned);
1180             assertNotNull(isProvisioned);
1181             return isProvisioned;
1182         } else {
1183             assertNull(isProvisioned);
1184             logd("isSatelliteProvisioned error=" + error);
1185             return false;
1186         }
1187     }
1188 
isSatelliteEnabled()1189     protected static boolean isSatelliteEnabled() {
1190         logd("isSatelliteEnabled");
1191 
1192         final AtomicReference<Boolean> enabled = new AtomicReference<>();
1193         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1194         CountDownLatch latch = new CountDownLatch(1);
1195         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1196                 new OutcomeReceiver<>() {
1197                     @Override
1198                     public void onResult(Boolean result) {
1199                         logd("isSatelliteEnabled: satellite enablement result: " + result);
1200                         enabled.set(result);
1201                         latch.countDown();
1202                     }
1203 
1204                     @Override
1205                     public void onError(SatelliteManager.SatelliteException exception) {
1206                         logd(
1207                                 "isSatelliteEnabled: satellite enablement error: "
1208                                         + exception.getErrorCode());
1209                         errorCode.set(exception.getErrorCode());
1210                         latch.countDown();
1211                     }
1212                 };
1213 
1214         logd("isSatelliteEnabled: querying satellite enable state");
1215         sSatelliteManager.requestIsEnabled(
1216                 getContext().getMainExecutor(), receiver);
1217         try {
1218             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1219         } catch (InterruptedException ex) {
1220             loge("isSatelliteEnabled ex=" + ex);
1221             return false;
1222         }
1223 
1224         Integer error = errorCode.get();
1225         Boolean isEnabled = enabled.get();
1226         if (error == null) {
1227             assertNotNull(isEnabled);
1228             return isEnabled;
1229         } else {
1230             assertNull(isEnabled);
1231             logd("isSatelliteEnabled error=" + error);
1232             return false;
1233         }
1234     }
1235 
isSatelliteDemoModeEnabled()1236     protected static boolean isSatelliteDemoModeEnabled() {
1237         final AtomicReference<Boolean> enabled = new AtomicReference<>();
1238         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1239         CountDownLatch latch = new CountDownLatch(1);
1240         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1241                 new OutcomeReceiver<>() {
1242                     @Override
1243                     public void onResult(Boolean result) {
1244                         enabled.set(result);
1245                         latch.countDown();
1246                     }
1247 
1248                     @Override
1249                     public void onError(SatelliteManager.SatelliteException exception) {
1250                         errorCode.set(exception.getErrorCode());
1251                         latch.countDown();
1252                     }
1253                 };
1254 
1255         sSatelliteManager.requestIsDemoModeEnabled(
1256                 getContext().getMainExecutor(), receiver);
1257         try {
1258             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1259         } catch (InterruptedException ex) {
1260             loge("isSatelliteDemoModeEnabled ex=" + ex);
1261             return false;
1262         }
1263 
1264         Integer error = errorCode.get();
1265         Boolean isEnabled = enabled.get();
1266         if (error == null) {
1267             assertNotNull(isEnabled);
1268             return isEnabled;
1269         } else {
1270             assertNull(isEnabled);
1271             logd("isSatelliteEnabled error=" + error);
1272             return false;
1273         }
1274     }
1275 
requestSatelliteEnabled(boolean enabled)1276     protected static void requestSatelliteEnabled(boolean enabled) {
1277         logd("requestSatelliteEnabled: enabled=" + enabled);
1278         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1279         logd(
1280                 "requestSatelliteEnabled: requesting satellite to be "
1281                         + (enabled ? "enabled" : "disabled"));
1282         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled).build(),
1283                 getContext().getMainExecutor(), error::offer);
1284         Integer errorCode;
1285         try {
1286             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1287         } catch (InterruptedException ex) {
1288             fail("requestSatelliteEnabled failed with ex=" + ex);
1289             return;
1290         }
1291         logd("requestSatelliteEnabled: errorCode=" + errorCode);
1292         assertNotNull(errorCode);
1293         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
1294     }
1295 
requestSatelliteEnabled(boolean enabled, boolean emergency)1296     protected static void requestSatelliteEnabled(boolean enabled, boolean emergency) {
1297         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1298         sSatelliteManager.requestEnabled(
1299                 new EnableRequestAttributes.Builder(enabled)
1300                         .setEmergencyMode(emergency)
1301                         .build(),
1302                 getContext().getMainExecutor(), error::offer);
1303         Integer errorCode;
1304         try {
1305             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1306         } catch (InterruptedException ex) {
1307             fail("requestSatelliteEnabled failed with ex=" + ex);
1308             return;
1309         }
1310         logd("requestSatelliteEnabled: errorCode=" + errorCode);
1311         assertNotNull(errorCode);
1312         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
1313     }
1314 
requestSatelliteEnabled(boolean enabled, long timeoutMillis)1315     protected static void requestSatelliteEnabled(boolean enabled, long timeoutMillis) {
1316         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1317         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled).build(),
1318                 getContext().getMainExecutor(), error::offer);
1319         Integer errorCode;
1320         try {
1321             errorCode = error.poll(timeoutMillis, TimeUnit.MILLISECONDS);
1322         } catch (InterruptedException ex) {
1323             fail("requestSatelliteEnabled failed with ex=" + ex);
1324             return;
1325         }
1326         logd("requestSatelliteEnabled: errorCode=" + errorCode);
1327         assertNotNull(errorCode);
1328         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
1329     }
1330 
requestSatelliteEnabledWithResult(boolean enabled, long timeoutMillis)1331     protected static int requestSatelliteEnabledWithResult(boolean enabled, long timeoutMillis) {
1332         logd(
1333                 "requestSatelliteEnabledWithResult: enabled="
1334                         + enabled
1335                         + ", timeoutMillis="
1336                         + timeoutMillis);
1337         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1338         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled).build(),
1339                 getContext().getMainExecutor(), error::offer);
1340         Integer errorCode = null;
1341         try {
1342             errorCode = error.poll(timeoutMillis, TimeUnit.MILLISECONDS);
1343         } catch (InterruptedException ex) {
1344             fail("requestSatelliteEnabled failed with ex=" + ex);
1345         }
1346         logd("requestSatelliteEnabledWithResult: errorCode=" + errorCode);
1347         assertNotNull(errorCode);
1348         return errorCode;
1349     }
1350 
requestSatelliteEnabledForDemoMode(boolean enabled)1351     protected static void requestSatelliteEnabledForDemoMode(boolean enabled) {
1352         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1353         sSatelliteManager.requestEnabled(
1354                 new EnableRequestAttributes.Builder(enabled)
1355                         .setDemoMode(true)
1356                         .setEmergencyMode(true)
1357                         .build(),
1358                 getContext().getMainExecutor(), error::offer);
1359         Integer errorCode;
1360         try {
1361             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1362         } catch (InterruptedException ex) {
1363             fail("requestSatelliteEnabled failed with ex=" + ex);
1364             return;
1365         }
1366         logd("requestSatelliteEnabledForDemoMode: errorCode=" + errorCode);
1367         assertNotNull(errorCode);
1368         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
1369     }
1370 
requestSatelliteEnabled(boolean enabled, boolean demoEnabled, int expectedError)1371     protected static void requestSatelliteEnabled(boolean enabled, boolean demoEnabled,
1372             int expectedError) {
1373         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1374         sSatelliteManager.requestEnabled(
1375                 new EnableRequestAttributes.Builder(enabled).setDemoMode(demoEnabled).build(),
1376                 getContext().getMainExecutor(), error::offer);
1377         Integer errorCode;
1378         try {
1379             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1380         } catch (InterruptedException ex) {
1381             fail("requestSatelliteEnabled failed with ex=" + ex);
1382             return;
1383         }
1384         logd("requestSatelliteEnabled: errorCode=" + errorCode);
1385         assertNotNull(errorCode);
1386         assertEquals(expectedError, (long) errorCode);
1387     }
1388 
verifyEmergencyMode(boolean expectedEmergencyMode)1389     protected static void verifyEmergencyMode(boolean expectedEmergencyMode) {
1390         final AtomicReference<Boolean> emergency = new AtomicReference<>();
1391         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1392         CountDownLatch latch = new CountDownLatch(1);
1393         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1394                 new OutcomeReceiver<>() {
1395                     @Override
1396                     public void onResult(Boolean result) {
1397                         emergency.set(result);
1398                         latch.countDown();
1399                     }
1400 
1401                     @Override
1402                     public void onError(SatelliteManager.SatelliteException exception) {
1403                         errorCode.set(exception.getErrorCode());
1404                         latch.countDown();
1405                     }
1406                 };
1407 
1408         sSatelliteManager.requestIsEmergencyModeEnabled(getContext().getMainExecutor(),
1409                 receiver);
1410         try {
1411             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1412         } catch (InterruptedException ex) {
1413             fail("Got InterruptedException for requestIsEmergencyModeEnabled, ex=" + ex);
1414         }
1415 
1416         Integer error = errorCode.get();
1417         Boolean isEmergency = emergency.get();
1418         if (error == null) {
1419             logd("verifyEmergencyMode isEmergency=" + isEmergency);
1420             assertNotNull(isEmergency);
1421             assertEquals(expectedEmergencyMode, isEmergency);
1422         } else {
1423             fail("Got error for requestIsEmergencyModeEnabled, error=" + error);
1424         }
1425     }
1426 
verifyDemoMode(boolean expectedDemoMode)1427     protected static void verifyDemoMode(boolean expectedDemoMode) {
1428         final AtomicReference<Boolean> demoMode = new AtomicReference<>();
1429         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1430         CountDownLatch latch = new CountDownLatch(1);
1431         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1432                 new OutcomeReceiver<>() {
1433                     @Override
1434                     public void onResult(Boolean result) {
1435                         demoMode.set(result);
1436                         latch.countDown();
1437                     }
1438 
1439                     @Override
1440                     public void onError(SatelliteManager.SatelliteException exception) {
1441                         errorCode.set(exception.getErrorCode());
1442                         latch.countDown();
1443                     }
1444                 };
1445 
1446         sSatelliteManager.requestIsDemoModeEnabled(getContext().getMainExecutor(),
1447                 receiver);
1448         try {
1449             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1450         } catch (InterruptedException ex) {
1451             fail("Got InterruptedException for requestIsEmergencyModeEnabled, ex=" + ex);
1452         }
1453 
1454         Integer error = errorCode.get();
1455         Boolean isDemoModeEnabled = demoMode.get();
1456         if (error == null) {
1457             logd("verifyDemoMode isDemoModeEnabled=" + isDemoModeEnabled);
1458             assertNotNull(isDemoModeEnabled);
1459             assertEquals(expectedDemoMode, isDemoModeEnabled);
1460         } else {
1461             fail("Got error for requestIsEmergencyModeEnabled, error=" + error);
1462         }
1463     }
1464 
requestSatelliteEnabledWithoutWaitingForResult( boolean enabled, boolean demoMode, boolean emergency)1465     protected static LinkedBlockingQueue<Integer> requestSatelliteEnabledWithoutWaitingForResult(
1466             boolean enabled, boolean demoMode, boolean emergency) {
1467         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1468         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled)
1469                         .setDemoMode(demoMode)
1470                         .setEmergencyMode(emergency)
1471                         .build(),
1472                 getContext().getMainExecutor(), error::offer);
1473         return error;
1474     }
1475 
assertResult(LinkedBlockingQueue<Integer> result, int expectedError)1476     protected static void assertResult(LinkedBlockingQueue<Integer> result, int expectedError) {
1477         Integer errorCode;
1478         try {
1479             errorCode = result.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1480         } catch (InterruptedException ex) {
1481             fail("assertResult failed with ex=" + ex);
1482             return;
1483         }
1484         logd("assertResult: errorCode=" + errorCode);
1485         assertNotNull(errorCode);
1486         assertEquals(expectedError, (long) errorCode);
1487     }
1488 
isSatelliteSupported()1489     protected static boolean isSatelliteSupported() {
1490         final AtomicReference<Boolean> supported = new AtomicReference<>();
1491         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1492         CountDownLatch latch = new CountDownLatch(1);
1493         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1494                 new OutcomeReceiver<>() {
1495                     @Override
1496                     public void onResult(Boolean result) {
1497                         supported.set(result);
1498                         latch.countDown();
1499                     }
1500 
1501                     @Override
1502                     public void onError(SatelliteManager.SatelliteException exception) {
1503                         errorCode.set(exception.getErrorCode());
1504                         latch.countDown();
1505                     }
1506                 };
1507 
1508         sSatelliteManager.requestIsSupported(getContext().getMainExecutor(),
1509                 receiver);
1510         try {
1511             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1512         } catch (InterruptedException ex) {
1513             loge("isSatelliteSupported ex=" + ex);
1514             return false;
1515         }
1516 
1517         Integer error = errorCode.get();
1518         Boolean isSupported = supported.get();
1519         if (error == null) {
1520             assertNotNull(isSupported);
1521             logd("isSatelliteSupported isSupported=" + isSupported);
1522             return isSupported;
1523         } else {
1524             assertNull(isSupported);
1525             logd("isSatelliteSupported error=" + error);
1526             return false;
1527         }
1528     }
1529 
turnRadioOff()1530     protected static void turnRadioOff() {
1531         ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener(
1532                 sTelephonyManager.getServiceState(), sTelephonyManager.getRadioPowerState());
1533         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1534                 tm -> tm.registerTelephonyCallback(Runnable::run, callback));
1535         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1536                 tm -> tm.requestRadioPowerOffForReason(TelephonyManager.RADIO_POWER_REASON_USER),
1537                 android.Manifest.permission.MODIFY_PHONE_STATE);
1538         callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF);
1539     }
1540 
turnRadioOn()1541     protected static void turnRadioOn() {
1542         ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener(
1543                 sTelephonyManager.getServiceState(), sTelephonyManager.getRadioPowerState());
1544         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1545                 tm -> tm.registerTelephonyCallback(Runnable::run, callback));
1546         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1547                 tm -> tm.clearRadioPowerOffForReason(TelephonyManager.RADIO_POWER_REASON_USER),
1548                 android.Manifest.permission.MODIFY_PHONE_STATE);
1549         callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON);
1550     }
1551 
1552     protected class UwbAdapterStateCallback implements UwbManager.AdapterStateCallback {
1553         private final Semaphore mUwbSemaphore = new Semaphore(0);
1554         private final Object mUwbExpectedStateLock = new Object();
1555         private boolean mUwbExpectedState = false;
1556 
toString(int state)1557         public String toString(int state) {
1558             switch (state) {
1559                 case UwbManager.AdapterStateCallback.STATE_DISABLED:
1560                     return "Disabled";
1561 
1562                 case UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE:
1563                     return "Inactive";
1564 
1565                 case UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE:
1566                     return "Active";
1567 
1568                 default:
1569                     return "";
1570             }
1571         }
1572 
1573         @Override
onStateChanged(int state, int reason)1574         public void onStateChanged(int state, int reason) {
1575             logd("UwbAdapterStateCallback onStateChanged() called, state = " + toString(state));
1576             logd("Adapter state changed reason " + String.valueOf(reason));
1577 
1578             synchronized (mUwbExpectedStateLock) {
1579                 if (mUwbExpectedState == mUwbManager.isUwbEnabled()) {
1580                     try {
1581                         mUwbSemaphore.release();
1582                     } catch (Exception e) {
1583                         loge("UwbAdapterStateCallback onStateChanged(): Got exception, ex=" + e);
1584                     }
1585                 }
1586             }
1587         }
1588 
waitUntilOnUwbStateChanged()1589         public boolean waitUntilOnUwbStateChanged() {
1590             synchronized (mUwbExpectedStateLock) {
1591                 if (mUwbExpectedState == mUwbManager.isUwbEnabled()) {
1592                     return true;
1593                 }
1594             }
1595 
1596             try {
1597                 if (!mUwbSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1598                         TimeUnit.MILLISECONDS)) {
1599                     loge("UwbAdapterStateCallback Timeout to receive "
1600                             + "onStateChanged() callback");
1601                     return false;
1602                 }
1603             } catch (Exception ex) {
1604                 loge("UwbAdapterStateCallback waitUntilOnUwbStateChanged: Got exception=" + ex);
1605                 return false;
1606             }
1607             return true;
1608         }
1609 
setUwbExpectedState(boolean expectedState)1610         public void setUwbExpectedState(boolean expectedState) {
1611             synchronized (mUwbExpectedStateLock) {
1612                 mUwbExpectedState = expectedState;
1613                 mUwbSemaphore.drainPermits();
1614             }
1615         }
1616     }
1617 
1618     protected class BTWifiNFCStateReceiver extends BroadcastReceiver {
1619         private final Semaphore mBTSemaphore = new Semaphore(0);
1620         private final Object mBTExpectedStateLock = new Object();
1621         private boolean mBTExpectedState = false;
1622 
1623         private final Semaphore mNfcSemaphore = new Semaphore(0);
1624         private final Object mNfcExpectedStateLock = new Object();
1625         private boolean mNfcExpectedState = false;
1626 
1627         private final Semaphore mWifiSemaphore = new Semaphore(0);
1628         private final Object mWifiExpectedStateLock = new Object();
1629         private boolean mWifiExpectedState = false;
1630 
1631         @Override
onReceive(Context context, Intent intent)1632         public void onReceive(Context context, Intent intent) {
1633             final String action = intent.getAction();
1634             if (action == null) {
1635                 logd("BTWifiNFCStateReceiver NULL action for intent " + intent);
1636                 return;
1637             }
1638             logd("BTWifiNFCStateReceiver onReceive: action = " + action);
1639 
1640             switch (action) {
1641                 case BluetoothAdapter.ACTION_STATE_CHANGED:
1642                     int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
1643                             BluetoothAdapter.ERROR);
1644                     logd("Bluetooth state updated to " + btState);
1645 
1646                     synchronized (mBTExpectedStateLock) {
1647                         if (mBTExpectedState == mBluetoothAdapter.isEnabled()) {
1648                             try {
1649                                 mBTSemaphore.release();
1650                             } catch (Exception e) {
1651                                 loge("BTWifiNFCStateReceiver onReceive(): Got exception, ex=" + e);
1652                             }
1653                         }
1654                     }
1655                     break;
1656 
1657                 case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED:
1658                     int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, -1);
1659                     logd("Nfc state updated to " + nfcState);
1660 
1661                     synchronized (mNfcExpectedStateLock) {
1662                         if (mNfcExpectedState == mNfcAdapter.isEnabled()) {
1663                             try {
1664                                 mNfcSemaphore.release();
1665                             } catch (Exception e) {
1666                                 loge("BTWifiNFCStateReceiver onReceive(): Got exception, ex=" + e);
1667                             }
1668                         }
1669                     }
1670                     break;
1671 
1672                 case WifiManager.WIFI_STATE_CHANGED_ACTION:
1673                     int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1674                             WifiManager.WIFI_STATE_UNKNOWN);
1675                     logd("Wifi state updated to " + wifiState);
1676 
1677                     synchronized (mWifiExpectedStateLock) {
1678                         if (mWifiExpectedState == mWifiManager.isWifiEnabled()) {
1679                             try {
1680                                 mWifiSemaphore.release();
1681                             } catch (Exception e) {
1682                                 loge("BTWifiNFCStateReceiver onReceive(): Got exception, ex=" + e);
1683                             }
1684                         }
1685                     }
1686                     break;
1687                 default:
1688                     break;
1689             }
1690         }
1691 
waitUntilOnBTStateChanged()1692         public boolean waitUntilOnBTStateChanged() {
1693             logd("waitUntilOnBTStateChanged");
1694             synchronized (mBTExpectedStateLock) {
1695                 if (mBTExpectedState == mBluetoothAdapter.isEnabled()) {
1696                     return true;
1697                 }
1698             }
1699 
1700             try {
1701                 if (!mBTSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1702                         TimeUnit.MILLISECONDS)) {
1703                     loge("BTWifiNFCStateReceiver waitUntilOnBTStateChanged: "
1704                             + "Timeout to receive onStateChanged() callback");
1705                     return false;
1706                 }
1707             } catch (Exception ex) {
1708                 loge("BTWifiNFCStateReceiver waitUntilOnBTStateChanged: Got exception=" + ex);
1709                 return false;
1710             }
1711             return true;
1712         }
1713 
waitUntilOnNfcStateChanged()1714         public boolean waitUntilOnNfcStateChanged() {
1715             synchronized (mNfcExpectedStateLock) {
1716                 if (mNfcExpectedState == mNfcAdapter.isEnabled()) {
1717                     return true;
1718                 }
1719             }
1720 
1721             try {
1722                 if (!mNfcSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1723                         TimeUnit.MILLISECONDS)) {
1724                     loge("BTWifiNFCStateReceiver waitUntilOnNfcStateChanged: "
1725                             + "Timeout to receive onStateChanged() callback");
1726                     return false;
1727                 }
1728             } catch (Exception ex) {
1729                 loge("BTWifiNFCStateReceiver waitUntilOnNfcStateChanged: Got exception=" + ex);
1730                 return false;
1731             }
1732             return true;
1733         }
1734 
waitUntilOnWifiStateChanged()1735         public boolean waitUntilOnWifiStateChanged() {
1736             synchronized (mWifiExpectedStateLock) {
1737                 if (mWifiExpectedState == mWifiManager.isWifiEnabled()) {
1738                     return true;
1739                 }
1740             }
1741 
1742             try {
1743                 if (!mWifiSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1744                         TimeUnit.MILLISECONDS)) {
1745                     loge("BTWifiNFCStateReceiver waitUntilOnWifiStateChanged: "
1746                             + "Timeout to receive onStateChanged() callback");
1747                     return false;
1748                 }
1749             } catch (Exception ex) {
1750                 loge("BTWifiNFCStateReceiver waitUntilOnWifiStateChanged: Got exception=" + ex);
1751                 return false;
1752             }
1753             return true;
1754         }
1755 
setBTExpectedState(boolean expectedState)1756         public void setBTExpectedState(boolean expectedState) {
1757             synchronized (mBTExpectedStateLock) {
1758                 mBTExpectedState = expectedState;
1759                 mBTSemaphore.drainPermits();
1760             }
1761         }
1762 
setWifiExpectedState(boolean expectedState)1763         public void setWifiExpectedState(boolean expectedState) {
1764             synchronized (mWifiExpectedStateLock) {
1765                 mWifiExpectedState = expectedState;
1766                 mWifiSemaphore.drainPermits();
1767             }
1768         }
1769 
setNfcExpectedState(boolean expectedState)1770         public void setNfcExpectedState(boolean expectedState) {
1771             synchronized (mNfcExpectedStateLock) {
1772                 mNfcExpectedState = expectedState;
1773                 mNfcSemaphore.drainPermits();
1774             }
1775         }
1776     }
1777 
logd(@onNull String log)1778     protected static void logd(@NonNull String log) {
1779         Rlog.d(TAG, log);
1780     }
1781 
loge(@onNull String log)1782     protected static void loge(@NonNull String log) {
1783         Rlog.e(TAG, log);
1784     }
1785 
assertSatelliteEnabledInSettings(boolean enabled)1786     protected static void assertSatelliteEnabledInSettings(boolean enabled) {
1787         int satelliteModeEnabled = Settings.Global.getInt(getContext().getContentResolver(),
1788                 Settings.Global.SATELLITE_MODE_ENABLED, 0);
1789         if (enabled) {
1790             assertEquals(satelliteModeEnabled, 1);
1791         } else {
1792             assertEquals(satelliteModeEnabled, 0);
1793         }
1794         logd("requestSatelliteEnabled: " + enabled
1795                 + " : satelliteModeEnabled from settings: " + satelliteModeEnabled);
1796     }
1797 
waitFor(long timeoutMillis)1798     protected static void waitFor(long timeoutMillis) {
1799         Object delayTimeout = new Object();
1800         synchronized (delayTimeout) {
1801             try {
1802                 delayTimeout.wait(timeoutMillis);
1803             } catch (InterruptedException ex) {
1804                 // Ignore the exception
1805                 logd("waitFor: delayTimeout ex=" + ex);
1806             }
1807         }
1808     }
1809 
1810     // Get default active subscription ID.
getActiveSubIDForCarrierSatelliteTest()1811     protected static int getActiveSubIDForCarrierSatelliteTest() {
1812         Context context = InstrumentationRegistry.getInstrumentation().getContext();
1813         SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
1814         List<SubscriptionInfo> infos = ShellIdentityUtils.invokeMethodWithShellPermissions(sm,
1815                 SubscriptionManager::getActiveSubscriptionInfoList);
1816 
1817         int defaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
1818         if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
1819                 && isSubIdInInfoList(infos, defaultSubId)) {
1820             return defaultSubId;
1821         }
1822 
1823         defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
1824         if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
1825                 && isSubIdInInfoList(infos, defaultSubId)) {
1826             return defaultSubId;
1827         }
1828 
1829         // Couldn't resolve a default. We can try to resolve a default using the active
1830         // subscriptions.
1831         if (!infos.isEmpty()) {
1832             return infos.get(0).getSubscriptionId();
1833         }
1834         loge("getActiveSubIDForCarrierSatelliteTest: use invalid subscription ID");
1835         // There must be at least one active subscription.
1836         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1837     }
1838 
getNtnOnlySubscriptionId()1839     protected static int getNtnOnlySubscriptionId() {
1840         Context context = InstrumentationRegistry.getInstrumentation().getContext();
1841         SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
1842         List<SubscriptionInfo> infoList = ShellIdentityUtils.invokeMethodWithShellPermissions(sm,
1843                 SubscriptionManager::getAllSubscriptionInfoList);
1844 
1845         int subId = infoList.stream()
1846                 .filter(info -> info.isOnlyNonTerrestrialNetwork())
1847                 .mapToInt(SubscriptionInfo::getSubscriptionId)
1848                 .findFirst()
1849                 .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
1850         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID && !infoList.isEmpty()) {
1851             subId = infoList.get(0).getSubscriptionId();
1852         }
1853         logd("getNtnOnlySubscriptionId: subId=" + subId);
1854         return subId;
1855     }
1856 
isSubIdInInfoList(List<SubscriptionInfo> infos, int subId)1857     private static boolean isSubIdInInfoList(List<SubscriptionInfo> infos, int subId) {
1858         return infos.stream().anyMatch(info -> info.getSubscriptionId() == subId);
1859     }
1860 
1861     protected static Pair<List<SatelliteSubscriberProvisionStatus>, Integer>
requestSatelliteSubscriberProvisionStatus()1862             requestSatelliteSubscriberProvisionStatus() {
1863         final AtomicReference<List<SatelliteSubscriberProvisionStatus>> list =
1864                 new AtomicReference<>();
1865         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1866         CountDownLatch latch = new CountDownLatch(1);
1867         OutcomeReceiver<List<SatelliteSubscriberProvisionStatus>,
1868                 SatelliteManager.SatelliteException>
1869                 receiver =
1870                 new OutcomeReceiver<>() {
1871                     @Override
1872                     public void onResult(List<SatelliteSubscriberProvisionStatus> result) {
1873                         list.set(result);
1874                         latch.countDown();
1875                     }
1876 
1877                     @Override
1878                     public void onError(SatelliteManager.SatelliteException exception) {
1879                         errorCode.set(exception.getErrorCode());
1880                         latch.countDown();
1881                     }
1882                 };
1883 
1884         sSatelliteManager.requestSatelliteSubscriberProvisionStatus(
1885                 getContext().getMainExecutor(), receiver);
1886         try {
1887             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1888         } catch (InterruptedException ex) {
1889             loge("requestSatelliteSubscriberProvisionStatus ex=" + ex);
1890             return null;
1891         }
1892 
1893         Integer error = errorCode.get();
1894         if (error == null) {
1895             assertTrue(list.get().size() > 0);
1896             return new Pair<>(list.get(), error);
1897         } else {
1898             assertFalse(list.get().size() > 0);
1899             return null;
1900         }
1901     }
1902 
requestSelectedNbIotSatelliteSubscriptionId()1903     protected static Pair<Integer, Integer> requestSelectedNbIotSatelliteSubscriptionId() {
1904         final AtomicReference<Integer> selectedSatelliteSubscriptionId =
1905                 new AtomicReference<>();
1906         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1907         CountDownLatch latch = new CountDownLatch(1);
1908         OutcomeReceiver<Integer, SatelliteManager.SatelliteException> receiver =
1909                 new OutcomeReceiver<>() {
1910                     @Override
1911                     public void onResult(Integer result) {
1912                         logd("requestSelectedNbIotSatelliteSubscriptionId.onResult: result=" +
1913                                 result);
1914                         selectedSatelliteSubscriptionId.set(result);
1915                         latch.countDown();
1916                     }
1917 
1918                     @Override
1919                     public void onError(SatelliteManager.SatelliteException exception) {
1920                         logd("requestSelectedNbIotSatelliteSubscriptionId.onError: onError="
1921                                 + exception.getErrorCode());
1922                         errorCode.set(exception.getErrorCode());
1923                         latch.countDown();
1924                     }
1925                 };
1926 
1927         sSatelliteManager.requestSelectedNbIotSatelliteSubscriptionId(
1928                 getContext().getMainExecutor(), receiver);
1929         try {
1930             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1931         } catch (InterruptedException e) {
1932             fail(e.toString());
1933         }
1934         return new Pair<>(selectedSatelliteSubscriptionId.get(), errorCode.get());
1935     }
1936 
requestSatelliteDisplayName()1937     protected static Pair<CharSequence, Integer> requestSatelliteDisplayName() {
1938         final AtomicReference<CharSequence> displayNameForSubscription = new AtomicReference<>();
1939         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1940         CountDownLatch latch = new CountDownLatch(1);
1941         OutcomeReceiver<CharSequence, SatelliteManager.SatelliteException> receiver =
1942                 new OutcomeReceiver<>() {
1943                     @Override
1944                     public void onResult(CharSequence result) {
1945                         logd("requestSatelliteDisplayName.onResult: result=" +
1946                                 result);
1947                         displayNameForSubscription.set(result);
1948                         latch.countDown();
1949                     }
1950 
1951                     @Override
1952                     public void onError(SatelliteManager.SatelliteException exception) {
1953                         logd("requestSatelliteDisplayName.onError: onError="
1954                                 + exception);
1955                         errorCode.set(exception.getErrorCode());
1956                         latch.countDown();
1957                     }
1958                 };
1959 
1960         sSatelliteManager.requestSatelliteDisplayName(
1961                 getContext().getMainExecutor(), receiver);
1962         try {
1963             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1964         } catch (InterruptedException e) {
1965             fail(e.toString());
1966         }
1967         return new Pair<>(displayNameForSubscription.get(), errorCode.get());
1968     }
1969 
provisionSatellite(List<SatelliteSubscriberInfo> list)1970     protected static Pair<Boolean, Integer> provisionSatellite(List<SatelliteSubscriberInfo> list) {
1971         final AtomicReference<Boolean> requestResult = new AtomicReference<>();
1972         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1973         CountDownLatch latch = new CountDownLatch(1);
1974         OutcomeReceiver<Void, SatelliteManager.SatelliteException> receiver =
1975                 new OutcomeReceiver<>() {
1976                     @Override
1977                     public void onResult(Void result) {
1978                         logd("provisionSatellite: onResult");
1979                         requestResult.set(true);
1980                         latch.countDown();
1981                     }
1982 
1983                     @Override
1984                     public void onError(SatelliteManager.SatelliteException exception) {
1985                         logd("provisionSatellite: onError: onError=" + exception);
1986                         errorCode.set(exception.getErrorCode());
1987                         latch.countDown();
1988                     }
1989                 };
1990 
1991         sSatelliteManager.provisionSatellite(list, getContext().getMainExecutor(), receiver);
1992         try {
1993             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1994         } catch (InterruptedException e) {
1995             fail(e.toString());
1996         }
1997         return new Pair<>(requestResult.get(), errorCode.get());
1998     }
1999 
deprovisionSatellite( List<SatelliteSubscriberInfo> list)2000     protected static Pair<Boolean, Integer> deprovisionSatellite(
2001             List<SatelliteSubscriberInfo> list) {
2002         final AtomicReference<Boolean> requestResult = new AtomicReference<>();
2003         final AtomicReference<Integer> errorCode = new AtomicReference<>();
2004         CountDownLatch latch = new CountDownLatch(1);
2005         OutcomeReceiver<Void, SatelliteManager.SatelliteException> receiver =
2006                 new OutcomeReceiver<>() {
2007                     @Override
2008                     public void onResult(Void result) {
2009                         logd("deprovisionSatellite: onResult");
2010                         requestResult.set(true);
2011                         latch.countDown();
2012                     }
2013 
2014                     @Override
2015                     public void onError(SatelliteManager.SatelliteException exception) {
2016                         logd("deprovisionSatellite: onError: onError=" + exception);
2017                         errorCode.set(exception.getErrorCode());
2018                         latch.countDown();
2019                     }
2020                 };
2021 
2022         sSatelliteManager.deprovisionSatellite(list, getContext().getMainExecutor(), receiver);
2023         try {
2024             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
2025         } catch (InterruptedException e) {
2026             fail(e.toString());
2027         }
2028         return new Pair<>(requestResult.get(), errorCode.get());
2029     }
2030 
2031     @NonNull
getConfigForSubId(Context context, int subId, String key)2032     protected static PersistableBundle getConfigForSubId(Context context, int subId, String key) {
2033         PersistableBundle config = null;
2034         CarrierConfigManager carrierConfigManager = context.getSystemService(
2035                 CarrierConfigManager.class);
2036         if (carrierConfigManager != null) {
2037             config = carrierConfigManager.getConfigForSubId(subId, key);
2038         }
2039         if (config == null || config.isEmpty()) {
2040             config = CarrierConfigManager.getDefaultConfig();
2041         }
2042         return config;
2043     }
2044 
setDefaultSmsSubId(Context context, int subId)2045     protected void setDefaultSmsSubId(Context context, int subId) {
2046         SubscriptionManager subscriptionManager = context.getSystemService(
2047                 SubscriptionManager.class);
2048         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(subscriptionManager, (sm) ->
2049                         sm.setDefaultSmsSubId(subId),
2050                 android.Manifest.permission.MODIFY_PHONE_STATE);
2051     }
2052 
2053     protected static class SatelliteReceiverTest extends BroadcastReceiver {
2054         private final Semaphore mSemaphore = new Semaphore(0);
2055 
2056         @Override
onReceive(Context context, Intent intent)2057         public void onReceive(Context context, Intent intent) {
2058             if (SatelliteReceiver.TEST_INTENT.equals(intent.getAction())) {
2059                 logd("SatelliteReceiverTest: receive the "
2060                         + SatelliteManager.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED);
2061                 mSemaphore.release();
2062             }
2063         }
2064 
clearQueue()2065         public void clearQueue() {
2066             logd("SatelliteReceiverTest: clearQueue");
2067             mSemaphore.drainPermits();
2068         }
2069 
waitForReceive()2070         boolean waitForReceive() {
2071             try {
2072                 if (!mSemaphore.tryAcquire(TimeUnit.SECONDS.toMillis(65), TimeUnit.MILLISECONDS)) {
2073                     logd("SatelliteReceiverTest: Timeout to receive");
2074                     return false;
2075                 }
2076             } catch (Exception ex) {
2077                 logd("SatelliteReceiverTest: waitForReceive: Got exception=" + ex);
2078                 return false;
2079             }
2080             return true;
2081         }
2082     }
2083 
getExpectedSatelliteConfiguration()2084     protected List<SatelliteAccessConfiguration> getExpectedSatelliteConfiguration() {
2085         UUID uuid1 = UUID.fromString("0db0312f-d73f-444d-b99b-a893dfb42edf");
2086         SatellitePosition satellitePosition1 = new SatellitePosition(-150.3, 35786000);
2087         List<Integer> bandList1 = new ArrayList<>(List.of(259, 260));
2088         EarfcnRange earfcnRange1 = new EarfcnRange(3000, 4300);
2089         List<Integer> tagIdList1 = new ArrayList<>(List.of(6, 7, 8));
2090 
2091         SatelliteInfo satelliteInfo1 = new SatelliteInfo(uuid1, satellitePosition1, bandList1,
2092                 new ArrayList<>(List.of(earfcnRange1)));
2093 
2094         SatelliteAccessConfiguration configuration1 = new SatelliteAccessConfiguration(
2095                 new ArrayList<>(List.of(satelliteInfo1)), tagIdList1);
2096 
2097         UUID uuid2 = UUID.fromString("1dec24f8-9223-4196-ad7a-a03002db7af7");
2098         SatellitePosition satellitePosition2 = new SatellitePosition(15.5, 35786000);
2099         List<Integer> bandList2 = new ArrayList<>(List.of(257, 258));
2100         EarfcnRange earfcnRange2 = new EarfcnRange(3200, 3200);
2101         List<Integer> tagIdList2 = new ArrayList<>(List.of(9, 10, 11));
2102 
2103         SatelliteInfo satelliteInfo2 = new SatelliteInfo(uuid2, satellitePosition2, bandList2,
2104                 new ArrayList<>(List.of(earfcnRange2)));
2105 
2106         SatelliteAccessConfiguration configuration2 = new SatelliteAccessConfiguration(
2107                 new ArrayList<>(List.of(satelliteInfo2)), tagIdList2);
2108 
2109         UUID uuid3 = UUID.fromString("f60cb479-d85b-4f4e-b050-cc428f5eb4a4");
2110         SatellitePosition satellitePosition3 = new SatellitePosition(-150, 35786000);
2111         List<Integer> bandList3 = new ArrayList<>(List.of(259, 260));
2112         EarfcnRange earfcnRange3 = new EarfcnRange(3300, 3400);
2113         List<Integer> tagIdList3 = new ArrayList<>(List.of(12, 13, 14));
2114 
2115         SatelliteInfo satelliteInfo3 = new SatelliteInfo(uuid3, satellitePosition3, bandList3,
2116                 new ArrayList<>(List.of(earfcnRange3)));
2117 
2118         SatelliteAccessConfiguration configuration3 = new SatelliteAccessConfiguration(
2119                 new ArrayList<>(List.of(satelliteInfo3)), tagIdList3);
2120 
2121         UUID uuid4 = UUID.fromString("c5837d96-9585-46aa-8dd0-a974583737fb");
2122         SatellitePosition satellitePosition4 = new SatellitePosition(-155, 35786000);
2123         List<Integer> bandList4 = new ArrayList<>(List.of(261, 262));
2124         EarfcnRange earfcnRange4 = new EarfcnRange(3500, 3600);
2125         List<Integer> tagIdList4 = new ArrayList<>(List.of(15, 16, 17));
2126 
2127         SatelliteInfo satelliteInfo4 = new SatelliteInfo(uuid4, satellitePosition4, bandList4,
2128                 new ArrayList<>(List.of(earfcnRange4)));
2129 
2130         SatelliteAccessConfiguration configuration4 = new SatelliteAccessConfiguration(
2131                 new ArrayList<>(List.of(satelliteInfo4)), tagIdList4);
2132 
2133         UUID uuid5 = UUID.fromString("6ef2a128-0477-4271-895f-dc4a221d2b23");
2134         SatellitePosition satellitePosition5 = new SatellitePosition(-66, 35786000);
2135         List<Integer> bandList5 = new ArrayList<>(List.of(263, 264));
2136         EarfcnRange earfcnRange5 = new EarfcnRange(3700, 3800);
2137         List<Integer> tagIdList5 = new ArrayList<>(List.of(18, 19, 20));
2138 
2139         SatelliteInfo satelliteInfo5 = new SatelliteInfo(uuid5, satellitePosition5, bandList5,
2140                 new ArrayList<>(List.of(earfcnRange5)));
2141 
2142         SatelliteAccessConfiguration configuration5 = new SatelliteAccessConfiguration(
2143                 new ArrayList<>(List.of(satelliteInfo5)), tagIdList5);
2144 
2145         return new ArrayList<>(
2146                 List.of(configuration1, configuration2, configuration3, configuration4,
2147                         configuration5));
2148     }
2149 
verifySatelliteAccessConfiguration( @onNull SatelliteAccessConfiguration expectedConfiguration, @NonNull SystemSelectionSpecifier actualSystemSelectionSpecifier)2150     protected void verifySatelliteAccessConfiguration(
2151             @NonNull SatelliteAccessConfiguration expectedConfiguration,
2152             @NonNull SystemSelectionSpecifier actualSystemSelectionSpecifier) {
2153 
2154         List<SatelliteInfo> expectedSatelliteInfos =
2155                 expectedConfiguration.getSatelliteInfos();
2156         List<Integer> expectedBandList = new ArrayList<>();
2157         List<Integer> expectedEarfcnList = new ArrayList<>();
2158         for (SatelliteInfo expectedSatelliteInfo : expectedSatelliteInfos) {
2159             expectedBandList.addAll(expectedSatelliteInfo.getBands());
2160             List<EarfcnRange> earfcnRangeList = expectedSatelliteInfo.getEarfcnRanges();
2161             earfcnRangeList.stream().flatMapToInt(
2162                     earfcnRange -> IntStream.of(earfcnRange.getStartEarfcn(),
2163                             earfcnRange.getEndEarfcn())).boxed().forEach(expectedEarfcnList::add);
2164         }
2165 
2166         List<Integer> actualBandList = Arrays.stream(actualSystemSelectionSpecifier.getBands())
2167                 .boxed().collect(Collectors.toList());
2168 
2169         List<Integer> actualEarfcnList = Arrays.stream(actualSystemSelectionSpecifier.getEarfcns())
2170                 .boxed().collect(Collectors.toList());
2171 
2172         SatelliteInfo[] expectedSatelliteInfoArray =
2173                 expectedConfiguration.getSatelliteInfos().toArray(new SatelliteInfo[0]);
2174         SatelliteInfo[] actualSatelliteInfoArray =
2175                 actualSystemSelectionSpecifier.getSatelliteInfos().toArray(new SatelliteInfo[0]);
2176 
2177         List<Integer> expectedTagIdList = expectedConfiguration.getTagIds();
2178         List<Integer> actualTagIdList = Arrays.stream(actualSystemSelectionSpecifier.getTagIds())
2179                 .boxed().collect(Collectors.toList());
2180 
2181         assertEquals(expectedBandList, actualBandList);
2182         assertEquals(expectedEarfcnList, actualEarfcnList);
2183         assertArrayEquals(expectedSatelliteInfoArray, actualSatelliteInfoArray);
2184         assertEquals(expectedTagIdList, actualTagIdList);
2185     }
2186 
2187     /** Get HAL version for the given HAL service. */
getHalVersion(int halService)2188     public int getHalVersion(int halService) {
2189         Pair<Integer, Integer> halVersion = sTelephonyManager.getHalVersion(halService);
2190         return makeRadioVersion(halVersion.first, halVersion.second);
2191     }
2192 
makeRadioVersion(int major, int minor)2193     private static int makeRadioVersion(int major, int minor) {
2194         if (major < 0 || minor < 0) return 0;
2195         return major * 100 + minor;
2196     }
2197 }
2198