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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.Manifest;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.bluetooth.BluetoothAdapter;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.PackageManager;
33 import android.database.ContentObserver;
34 import android.net.wifi.WifiManager;
35 import android.nfc.NfcAdapter;
36 import android.os.Handler;
37 import android.os.Looper;
38 import android.os.OutcomeReceiver;
39 import android.provider.Settings;
40 import android.telephony.Rlog;
41 import android.telephony.SubscriptionInfo;
42 import android.telephony.SubscriptionManager;
43 import android.telephony.TelephonyManager;
44 import android.telephony.cts.TelephonyManagerTest.ServiceStateRadioStateListener;
45 import android.telephony.satellite.EnableRequestAttributes;
46 import android.telephony.satellite.NtnSignalStrength;
47 import android.telephony.satellite.NtnSignalStrengthCallback;
48 import android.telephony.satellite.PointingInfo;
49 import android.telephony.satellite.SatelliteCapabilities;
50 import android.telephony.satellite.SatelliteCapabilitiesCallback;
51 import android.telephony.satellite.SatelliteCommunicationAllowedStateCallback;
52 import android.telephony.satellite.SatelliteDatagram;
53 import android.telephony.satellite.SatelliteDatagramCallback;
54 import android.telephony.satellite.SatelliteManager;
55 import android.telephony.satellite.SatelliteModemStateCallback;
56 import android.telephony.satellite.SatelliteProvisionStateCallback;
57 import android.telephony.satellite.SatelliteSupportedStateCallback;
58 import android.telephony.satellite.SatelliteTransmissionUpdateCallback;
59 import android.text.TextUtils;
60 import android.util.Log;
61 import android.uwb.UwbManager;
62 
63 import androidx.test.InstrumentationRegistry;
64 
65 import com.android.compatibility.common.util.ShellIdentityUtils;
66 
67 import java.util.ArrayList;
68 import java.util.List;
69 import java.util.concurrent.CountDownLatch;
70 import java.util.concurrent.LinkedBlockingQueue;
71 import java.util.concurrent.Semaphore;
72 import java.util.concurrent.TimeUnit;
73 import java.util.concurrent.atomic.AtomicReference;
74 import java.util.function.Consumer;
75 
76 public class SatelliteManagerTestBase {
77     protected static String TAG = "SatelliteManagerTestBase";
78 
79     protected static final String TOKEN = "TEST_TOKEN";
80     protected static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
81     /**
82      * Since SST sets waiting time up to 10 seconds for the power off radio, the timer waiting for
83      * radio power state change should be greater than 10 seconds.
84      */
85     protected static final long EXTERNAL_DEPENDENT_TIMEOUT = TimeUnit.SECONDS.toMillis(15);
86 
87     protected static SatelliteManager sSatelliteManager;
88     protected static TelephonyManager sTelephonyManager = null;
89 
90     protected UwbManager mUwbManager = null;
91     protected NfcAdapter mNfcAdapter = null;
92     protected BluetoothAdapter mBluetoothAdapter = null;
93     protected WifiManager mWifiManager = null;
94 
beforeAllTestsBase()95     protected static void beforeAllTestsBase() {
96         sSatelliteManager = getContext().getSystemService(SatelliteManager.class);
97         sTelephonyManager = getContext().getSystemService(TelephonyManager.class);
98         turnRadioOn();
99     }
100 
afterAllTestsBase()101     protected static void afterAllTestsBase() {
102         sSatelliteManager = null;
103         sTelephonyManager = null;
104     }
105 
shouldTestSatellite()106     protected static boolean shouldTestSatellite() {
107         if (!getContext().getPackageManager().hasSystemFeature(
108                 PackageManager.FEATURE_TELEPHONY_SATELLITE)) {
109             logd("Skipping tests because FEATURE_TELEPHONY_SATELLITE is not available");
110             return false;
111         }
112         try {
113             getContext().getSystemService(TelephonyManager.class)
114                     .getHalVersion(TelephonyManager.HAL_SERVICE_RADIO);
115         } catch (IllegalStateException e) {
116             logd("Skipping tests because Telephony service is null, exception=" + e);
117             return false;
118         }
119         return true;
120     }
121 
shouldTestSatelliteWithMockService()122     protected static boolean shouldTestSatelliteWithMockService() {
123         if (!getContext().getPackageManager().hasSystemFeature(
124                 PackageManager.FEATURE_TELEPHONY)) {
125             logd("Skipping tests because FEATURE_TELEPHONY is not available");
126             return false;
127         }
128         try {
129             getContext().getSystemService(TelephonyManager.class)
130                     .getHalVersion(TelephonyManager.HAL_SERVICE_RADIO);
131         } catch (IllegalStateException e) {
132             logd("Skipping tests because Telephony service is null, exception=" + e);
133             return false;
134         }
135         return true;
136     }
137 
getContext()138     protected static Context getContext() {
139         return InstrumentationRegistry.getContext();
140     }
141 
grantSatellitePermission()142     protected static void grantSatellitePermission() {
143         InstrumentationRegistry.getInstrumentation().getUiAutomation()
144                 .adoptShellPermissionIdentity(Manifest.permission.SATELLITE_COMMUNICATION);
145     }
146 
revokeSatellitePermission()147     protected static void revokeSatellitePermission() {
148         InstrumentationRegistry.getInstrumentation().getUiAutomation()
149                 .dropShellPermissionIdentity();
150     }
151 
152     protected static class SatelliteTransmissionUpdateCallbackTest implements
153             SatelliteTransmissionUpdateCallback {
154 
155         protected static final class DatagramStateChangeArgument {
156             protected int state;
157             protected int pendingCount;
158             protected int errorCode;
159 
DatagramStateChangeArgument(int state, int pendingCount, int errorCode)160             DatagramStateChangeArgument(int state, int pendingCount, int errorCode) {
161                 this.state = state;
162                 this.pendingCount = pendingCount;
163                 this.errorCode = errorCode;
164             }
165 
166             @Override
equals(Object other)167             public boolean equals(Object other) {
168                 if (this == other) return true;
169                 if (other == null || getClass() != other.getClass()) return false;
170                 DatagramStateChangeArgument that = (DatagramStateChangeArgument) other;
171                 return state == that.state
172                         && pendingCount  == that.pendingCount
173                         && errorCode == that.errorCode;
174             }
175 
176             @Override
toString()177             public String toString() {
178                 return ("state: " + state + " pendingCount: " + pendingCount
179                         + " errorCode: " + errorCode);
180             }
181         }
182 
183         public PointingInfo mPointingInfo;
184         private final Semaphore mPositionChangeSemaphore = new Semaphore(0);
185         private List<DatagramStateChangeArgument> mSendDatagramStateChanges = new ArrayList<>();
186         private final Object mSendDatagramStateChangesLock = new Object();
187         private final Semaphore mSendSemaphore = new Semaphore(0);
188         private List<DatagramStateChangeArgument> mReceiveDatagramStateChanges = new ArrayList<>();
189         private final Object mReceiveDatagramStateChangesLock = new Object();
190         private final Semaphore mReceiveSemaphore = new Semaphore(0);
191 
192         @Override
onSatellitePositionChanged(PointingInfo pointingInfo)193         public void onSatellitePositionChanged(PointingInfo pointingInfo) {
194             logd("onSatellitePositionChanged: pointingInfo=" + pointingInfo);
195             mPointingInfo = pointingInfo;
196 
197             try {
198                 mPositionChangeSemaphore.release();
199             } catch (Exception e) {
200                 loge("onSatellitePositionChanged: Got exception, ex=" + e);
201             }
202         }
203 
204         @Override
onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode)205         public void onSendDatagramStateChanged(int state, int sendPendingCount, int errorCode) {
206             logd("onSendDatagramStateChanged: state=" + state + ", sendPendingCount="
207                     + sendPendingCount + ", errorCode=" + errorCode);
208             synchronized (mSendDatagramStateChangesLock) {
209                 mSendDatagramStateChanges.add(new DatagramStateChangeArgument(state,
210                         sendPendingCount, errorCode));
211             }
212 
213             try {
214                 mSendSemaphore.release();
215             } catch (Exception e) {
216                 loge("onSendDatagramStateChanged: Got exception, ex=" + e);
217             }
218         }
219 
220         @Override
onSendDatagramStateChanged( int datagramType, int state, int sendPendingCount, int errorCode)221         public void onSendDatagramStateChanged(
222                 int datagramType, int state, int sendPendingCount, int errorCode) {
223             logd("onSendDatagramStateChanged:datagramType=" + datagramType + ", state=" + state
224                     + ", sendPendingCount=" + sendPendingCount + ", errorCode=" + errorCode);
225         }
226 
227         @Override
onReceiveDatagramStateChanged( int state, int receivePendingCount, int errorCode)228         public void onReceiveDatagramStateChanged(
229                 int state, int receivePendingCount, int errorCode) {
230             logd("onReceiveDatagramStateChanged: state=" + state + ", "
231                     + "receivePendingCount=" + receivePendingCount + ", errorCode=" + errorCode);
232             synchronized (mReceiveDatagramStateChangesLock) {
233                 mReceiveDatagramStateChanges.add(new DatagramStateChangeArgument(state,
234                         receivePendingCount, errorCode));
235             }
236 
237             try {
238                 mReceiveSemaphore.release();
239             } catch (Exception e) {
240                 loge("onReceiveDatagramStateChanged: Got exception, ex=" + e);
241             }
242         }
243 
waitUntilOnSatellitePositionChanged(int expectedNumberOfEvents)244         public boolean waitUntilOnSatellitePositionChanged(int expectedNumberOfEvents) {
245             for (int i = 0; i < expectedNumberOfEvents; i++) {
246                 try {
247                     if (!mPositionChangeSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
248                         loge("Timeout to receive onSatellitePositionChanged() callback");
249                         return false;
250                     }
251                 } catch (Exception ex) {
252                     loge("SatelliteTransmissionUpdateCallback "
253                             + "waitUntilOnSatellitePositionChanged: Got exception=" + ex);
254                     return false;
255                 }
256             }
257             return true;
258         }
259 
waitUntilOnSendDatagramStateChanged(int expectedNumberOfEvents)260         public boolean waitUntilOnSendDatagramStateChanged(int expectedNumberOfEvents) {
261             logd("waitUntilOnSendDatagramStateChanged expectedNumberOfEvents:" + expectedNumberOfEvents);
262             for (int i = 0; i < expectedNumberOfEvents; i++) {
263                 try {
264                     if (!mSendSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
265                         loge("Timeout to receive onSendDatagramStateChanged() callback");
266                         return false;
267                     }
268                 } catch (Exception ex) {
269                     loge("SatelliteTransmissionUpdateCallback "
270                             + "waitUntilOnSendDatagramStateChanged: Got exception=" + ex);
271                     return false;
272                 }
273             }
274             return true;
275         }
276 
waitUntilOnReceiveDatagramStateChanged(int expectedNumberOfEvents)277         public boolean waitUntilOnReceiveDatagramStateChanged(int expectedNumberOfEvents) {
278             for (int i = 0; i < expectedNumberOfEvents; i++) {
279                 try {
280                     if (!mReceiveSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
281                         loge("Timeout to receive onReceiveDatagramStateChanged()");
282                         return false;
283                     }
284                 } catch (Exception ex) {
285                     loge("SatelliteTransmissionUpdateCallback "
286                             + "waitUntilOnReceiveDatagramStateChanged: Got exception=" + ex);
287                     return false;
288                 }
289             }
290             return true;
291         }
292 
clearPointingInfo()293         public void clearPointingInfo() {
294             mPointingInfo = null;
295             mPositionChangeSemaphore.drainPermits();
296         }
297 
clearSendDatagramStateChanges()298         public void clearSendDatagramStateChanges() {
299             synchronized (mSendDatagramStateChangesLock) {
300                 logd("clearSendDatagramStateChanges");
301                 mSendDatagramStateChanges.clear();
302                 mSendSemaphore.drainPermits();
303             }
304         }
305 
clearReceiveDatagramStateChanges()306         public void clearReceiveDatagramStateChanges() {
307             synchronized (mReceiveDatagramStateChangesLock) {
308                 logd("clearReceiveDatagramStateChanges");
309                 mReceiveDatagramStateChanges.clear();
310                 mReceiveSemaphore.drainPermits();
311             }
312         }
313 
314         @Nullable
getSendDatagramStateChange(int index)315         public DatagramStateChangeArgument getSendDatagramStateChange(int index) {
316             synchronized (mSendDatagramStateChangesLock) {
317                 if (index < mSendDatagramStateChanges.size()) {
318                     return mSendDatagramStateChanges.get(index);
319                 } else {
320                     Log.e(TAG, "getSendDatagramStateChange: invalid index= " + index
321                             + " mSendDatagramStateChanges.size= "
322                             + mSendDatagramStateChanges.size());
323                     return null;
324                 }
325             }
326         }
327 
328         @Nullable
getReceiveDatagramStateChange(int index)329         public DatagramStateChangeArgument getReceiveDatagramStateChange(int index) {
330             synchronized (mReceiveDatagramStateChangesLock) {
331                 if (index < mReceiveDatagramStateChanges.size()) {
332                     return mReceiveDatagramStateChanges.get(index);
333                 } else {
334                     Log.e(TAG, "getReceiveDatagramStateChange: invalid index= " + index
335                             + " mReceiveDatagramStateChanges.size= "
336                             + mReceiveDatagramStateChanges.size());
337                     return null;
338                 }
339             }
340         }
341 
getNumOfSendDatagramStateChanges()342         public int getNumOfSendDatagramStateChanges() {
343             synchronized (mSendDatagramStateChangesLock) {
344                 logd("getNumOfSendDatagramStateChanges size:" + mSendDatagramStateChanges.size());
345                 return mSendDatagramStateChanges.size();
346             }
347         }
348 
getNumOfReceiveDatagramStateChanges()349         public int getNumOfReceiveDatagramStateChanges() {
350             synchronized (mReceiveDatagramStateChangesLock) {
351                 return mReceiveDatagramStateChanges.size();
352             }
353         }
354     }
355 
356     protected static class SatelliteProvisionStateCallbackTest implements
357             SatelliteProvisionStateCallback {
358         public boolean isProvisioned = false;
359         private List<Boolean> mProvisionedStates = new ArrayList<>();
360         private final Object mProvisionedStatesLock = new Object();
361         private final Semaphore mSemaphore = new Semaphore(0);
362 
363         @Override
onSatelliteProvisionStateChanged(boolean provisioned)364         public void onSatelliteProvisionStateChanged(boolean provisioned) {
365             logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
366             isProvisioned = provisioned;
367             synchronized (mProvisionedStatesLock) {
368                 mProvisionedStates.add(provisioned);
369             }
370             try {
371                 mSemaphore.release();
372             } catch (Exception ex) {
373                 loge("onSatelliteProvisionStateChanged: Got exception, ex=" + ex);
374             }
375         }
376 
waitUntilResult(int expectedNumberOfEvents)377         public boolean waitUntilResult(int expectedNumberOfEvents) {
378             for (int i = 0; i < expectedNumberOfEvents; i++) {
379                 try {
380                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
381                         loge("Timeout to receive onSatelliteProvisionStateChanged");
382                         return false;
383                     }
384                 } catch (Exception ex) {
385                     loge("onSatelliteProvisionStateChanged: Got exception=" + ex);
386                     return false;
387                 }
388             }
389             return true;
390         }
391 
clearProvisionedStates()392         public void clearProvisionedStates() {
393             synchronized (mProvisionedStatesLock) {
394                 mProvisionedStates.clear();
395                 mSemaphore.drainPermits();
396             }
397         }
398 
getTotalCountOfProvisionedStates()399         public int getTotalCountOfProvisionedStates() {
400             synchronized (mProvisionedStatesLock) {
401                 return mProvisionedStates.size();
402             }
403         }
404 
getProvisionedState(int index)405         public boolean getProvisionedState(int index) {
406             synchronized (mProvisionedStatesLock) {
407                 if (index < mProvisionedStates.size()) {
408                     return mProvisionedStates.get(index);
409                 }
410             }
411             loge("getProvisionedState: invalid index=" + index);
412             return false;
413         }
414     }
415 
416     protected static class SatelliteModemStateCallbackTest implements SatelliteModemStateCallback {
417         public int modemState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
418         private List<Integer> mModemStates = new ArrayList<>();
419         private final Object mModemStatesLock = new Object();
420         private final Semaphore mSemaphore = new Semaphore(0);
421         private final Semaphore mModemOffSemaphore = new Semaphore(0);
422 
423         @Override
onSatelliteModemStateChanged(int state)424         public void onSatelliteModemStateChanged(int state) {
425             Log.d(TAG, "onSatelliteModemStateChanged: state=" + state);
426             modemState = state;
427             synchronized (mModemStatesLock) {
428                 mModemStates.add(state);
429             }
430             try {
431                 mSemaphore.release();
432             } catch (Exception ex) {
433                 Log.e(TAG, "onSatelliteModemStateChanged: Got exception, ex=" + ex);
434             }
435 
436             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
437                 try {
438                     mModemOffSemaphore.release();
439                 } catch (Exception ex) {
440                     Log.e(TAG, "onSatelliteModemStateChanged: Got exception in "
441                             + "releasing mModemOffSemaphore, ex=" + ex);
442                 }
443             }
444         }
445 
waitUntilResult(int expectedNumberOfEvents)446         public boolean waitUntilResult(int expectedNumberOfEvents) {
447             for (int i = 0; i < expectedNumberOfEvents; i++) {
448                 try {
449                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
450                         Log.e(TAG, "Timeout to receive onSatelliteModemStateChanged");
451                         return false;
452                     }
453                 } catch (Exception ex) {
454                     Log.e(TAG, "onSatelliteModemStateChanged: Got exception=" + ex);
455                     return false;
456                 }
457             }
458             return true;
459         }
460 
waitUntilModemOff()461         public boolean waitUntilModemOff() {
462             try {
463                 if (!mModemOffSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
464                     Log.e(TAG, "Timeout to receive satellite modem off event");
465                     return false;
466                 }
467             } catch (Exception ex) {
468                 Log.e(TAG, "Waiting for satellite modem off event: Got exception=" + ex);
469                 return false;
470             }
471             return true;
472         }
473 
waitUntilModemOff(long timeoutMillis)474         public boolean waitUntilModemOff(long timeoutMillis) {
475             try {
476                 if (!mModemOffSemaphore.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS)) {
477                     Log.e(TAG, "Timeout to receive satellite modem off event");
478                     return false;
479                 }
480             } catch (Exception ex) {
481                 Log.e(TAG, "Waiting for satellite modem off event: Got exception=" + ex);
482                 return false;
483             }
484             return true;
485         }
486 
clearModemStates()487         public void clearModemStates() {
488             synchronized (mModemStatesLock) {
489                 Log.d(TAG, "onSatelliteModemStateChanged: clearModemStates");
490                 mModemStates.clear();
491                 mSemaphore.drainPermits();
492                 mModemOffSemaphore.drainPermits();
493             }
494         }
495 
getModemState(int index)496         public int getModemState(int index) {
497             synchronized (mModemStatesLock) {
498                 if (index < mModemStates.size()) {
499                     return mModemStates.get(index);
500                 } else {
501                     Log.e(TAG, "getModemState: invalid index=" + index
502                             + ", mModemStates.size=" + mModemStates.size());
503                     return -1;
504                 }
505             }
506         }
507 
getTotalCountOfModemStates()508         public int getTotalCountOfModemStates() {
509             synchronized (mModemStatesLock) {
510                 return mModemStates.size();
511             }
512         }
513     }
514 
515     protected static class SatelliteDatagramCallbackTest implements SatelliteDatagramCallback {
516         public SatelliteDatagram mDatagram;
517         public final List<SatelliteDatagram> mDatagramList = new ArrayList<>();
518         public long mDatagramId;
519         private final Semaphore mSemaphore = new Semaphore(0);
520 
521         @Override
onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram, int pendingCount, Consumer<Void> callback)522         public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
523                 int pendingCount, Consumer<Void> callback) {
524             logd("onSatelliteDatagramReceived: datagramId=" + datagramId + ", datagram="
525                     + datagram + ", pendingCount=" + pendingCount);
526             mDatagram = datagram;
527             mDatagramList.add(datagram);
528             mDatagramId = datagramId;
529             if (callback != null) {
530                 logd("onSatelliteDatagramReceived: callback.accept() datagramId=" + datagramId);
531                 callback.accept(null);
532             } else {
533                 logd("onSatelliteDatagramReceived: callback is null datagramId=" + datagramId);
534             }
535 
536             try {
537                 mSemaphore.release();
538             } catch (Exception e) {
539                 loge("onSatelliteDatagramReceived: Got exception, ex=" + e);
540             }
541         }
542 
waitUntilResult(int expectedNumberOfEvents)543         public boolean waitUntilResult(int expectedNumberOfEvents) {
544             for (int i = 0; i < expectedNumberOfEvents; i++) {
545                 try {
546                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
547                         loge("Timeout to receive onSatelliteDatagramReceived");
548                         return false;
549                     }
550                 } catch (Exception ex) {
551                     loge("onSatelliteDatagramReceived: Got exception=" + ex);
552                     return false;
553                 }
554             }
555             return true;
556         }
557     }
558 
559     protected static class NtnSignalStrengthCallbackTest implements NtnSignalStrengthCallback {
560         public NtnSignalStrength mNtnSignalStrength;
561         private final Semaphore mSemaphore = new Semaphore(0);
562 
563         @Override
onNtnSignalStrengthChanged(@onNull NtnSignalStrength ntnSignalStrength)564         public void onNtnSignalStrengthChanged(@NonNull NtnSignalStrength ntnSignalStrength) {
565             logd("onNtnSignalStrengthChanged: ntnSignalStrength=" + ntnSignalStrength);
566             mNtnSignalStrength = new NtnSignalStrength(ntnSignalStrength);
567 
568             try {
569                 mSemaphore.release();
570             } catch (Exception e) {
571                 loge("onNtnSignalStrengthChanged: Got exception, ex=" + e);
572             }
573         }
574 
waitUntilResult(int expectedNumberOfEvents)575         public boolean waitUntilResult(int expectedNumberOfEvents) {
576             for (int i = 0; i < expectedNumberOfEvents; i++) {
577                 try {
578                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
579                         loge("Timeout to receive onNtnSignalStrengthChanged");
580                         return false;
581                     }
582                 } catch (Exception ex) {
583                     loge("onNtnSignalStrengthChanged: Got exception=" + ex);
584                     return false;
585                 }
586             }
587             return true;
588         }
589     }
590 
591     protected static class SatelliteCapabilitiesCallbackTest implements
592             SatelliteCapabilitiesCallback {
593         public SatelliteCapabilities mSatelliteCapabilities;
594         private final Semaphore mSemaphore = new Semaphore(0);
595 
596         @Override
onSatelliteCapabilitiesChanged( @onNull SatelliteCapabilities satelliteCapabilities)597         public void onSatelliteCapabilitiesChanged(
598                 @NonNull SatelliteCapabilities satelliteCapabilities) {
599             logd("onSatelliteCapabilitiesChanged: satelliteCapabilities=" + satelliteCapabilities);
600             mSatelliteCapabilities = satelliteCapabilities;
601 
602             try {
603                 mSemaphore.release();
604             } catch (Exception e) {
605                 loge("onSatelliteCapabilitiesChanged: Got exception, ex=" + e);
606             }
607         }
608 
waitUntilResult(int expectedNumberOfEvents)609         public boolean waitUntilResult(int expectedNumberOfEvents) {
610             for (int i = 0; i < expectedNumberOfEvents; i++) {
611                 try {
612                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
613                         loge("Timeout to receive onSatelliteCapabilitiesChanged");
614                         return false;
615                     }
616                 } catch (Exception ex) {
617                     loge("onSatelliteCapabilitiesChanged: Got exception=" + ex);
618                     return false;
619                 }
620             }
621             return true;
622         }
623     }
624 
625     protected static class SatelliteSupportedStateCallbackTest implements
626             SatelliteSupportedStateCallback {
627         public boolean isSupported = false;
628         private List<Boolean> mSupportedStates = new ArrayList<>();
629         private final Object mSupportedStatesLock = new Object();
630         private final Semaphore mSemaphore = new Semaphore(0);
631 
632         @Override
onSatelliteSupportedStateChanged(boolean supported)633         public void onSatelliteSupportedStateChanged(boolean supported) {
634             logd("onSatelliteSupportedStateChanged: supported=" + supported);
635             isSupported = supported;
636             synchronized (mSupportedStatesLock) {
637                 mSupportedStates.add(supported);
638             }
639             try {
640                 mSemaphore.release();
641             } catch (Exception ex) {
642                 loge("onSatelliteSupportedStateChanged: Got exception, ex=" + ex);
643             }
644         }
645 
waitUntilResult(int expectedNumberOfEvents)646         public boolean waitUntilResult(int expectedNumberOfEvents) {
647             for (int i = 0; i < expectedNumberOfEvents; i++) {
648                 try {
649                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
650                         loge("Timeout to receive onSatelliteSupportedStateChanged");
651                         return false;
652                     }
653                 } catch (Exception ex) {
654                     loge("onSatelliteSupportedStateChanged: Got exception=" + ex);
655                     return false;
656                 }
657             }
658             return true;
659         }
660 
clearSupportedStates()661         public void clearSupportedStates() {
662             synchronized (mSupportedStatesLock) {
663                 mSupportedStates.clear();
664                 mSemaphore.drainPermits();
665             }
666         }
667 
getTotalCountOfSupportedStates()668         public int getTotalCountOfSupportedStates() {
669             synchronized (mSupportedStatesLock) {
670                 return mSupportedStates.size();
671             }
672         }
673 
getSupportedState(int index)674         public Boolean getSupportedState(int index) {
675             synchronized (mSupportedStatesLock) {
676                 if (index < mSupportedStates.size()) {
677                     return mSupportedStates.get(index);
678                 }
679             }
680             loge("getSupportedState: invalid index=" + index);
681             return null;
682         }
683     }
684 
685     protected static class SatelliteCommunicationAllowedStateCallbackTest implements
686             SatelliteCommunicationAllowedStateCallback {
687         public boolean isAllowed = false;
688         private final Semaphore mSemaphore = new Semaphore(0);
689 
690         @Override
onSatelliteCommunicationAllowedStateChanged(boolean allowed)691         public void onSatelliteCommunicationAllowedStateChanged(boolean allowed) {
692             logd("onSatelliteCommunicationAllowedStateChanged: isAllowed=" + allowed);
693             isAllowed = allowed;
694 
695             try {
696                 mSemaphore.release();
697             } catch (Exception e) {
698                 loge("onSatelliteCommunicationAllowedStateChanged: Got exception, ex=" + e);
699             }
700         }
701 
waitUntilResult(int expectedNumberOfEvents)702         public boolean waitUntilResult(int expectedNumberOfEvents) {
703             for (int i = 0; i < expectedNumberOfEvents; i++) {
704                 try {
705                     if (!mSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
706                         loge("Timeout to receive onSatelliteCommunicationAllowedStateChanged");
707                         return false;
708                     }
709                 } catch (Exception ex) {
710                     loge("onNtnSignalStrengthChanged: Got exception=" + ex);
711                     return false;
712                 }
713             }
714             return true;
715         }
716     }
717 
718     protected static class SatelliteModeRadiosUpdater extends ContentObserver implements
719             AutoCloseable {
720         private final Context mContext;
721         private final Semaphore mSemaphore = new Semaphore(0);
722         private String mExpectedSatelliteModeRadios = "";
723         private final Object mLock = new Object();
724 
SatelliteModeRadiosUpdater(Context context)725         public SatelliteModeRadiosUpdater(Context context) {
726             super(new Handler(Looper.getMainLooper()));
727             mContext = context;
728             mContext.getContentResolver().registerContentObserver(
729                     Settings.Global.getUriFor(Settings.Global.SATELLITE_MODE_RADIOS), false, this);
730             InstrumentationRegistry.getInstrumentation().getUiAutomation()
731                     .adoptShellPermissionIdentity(Manifest.permission.SATELLITE_COMMUNICATION,
732                             Manifest.permission.WRITE_SECURE_SETTINGS,
733                             Manifest.permission.NETWORK_SETTINGS,
734                             Manifest.permission.ACCESS_FINE_LOCATION,
735                             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
736                             Manifest.permission.UWB_PRIVILEGED);
737         }
738 
739         @Override
onChange(boolean selfChange)740         public void onChange(boolean selfChange) {
741             String newSatelliteModeRadios = Settings.Global.getString(
742                     mContext.getContentResolver(), Settings.Global.SATELLITE_MODE_RADIOS);
743             synchronized (mLock) {
744                 if (TextUtils.equals(mExpectedSatelliteModeRadios, newSatelliteModeRadios)) {
745                     logd("SatelliteModeRadiosUpdater: onChange, newSatelliteModeRadios="
746                             + newSatelliteModeRadios);
747                     try {
748                         mSemaphore.release();
749                     } catch (Exception ex) {
750                         loge("SatelliteModeRadiosUpdater: onChange, ex=" + ex);
751                     }
752                 }
753             }
754         }
755 
756         @Override
close()757         public void close() throws Exception {
758             mContext.getContentResolver().unregisterContentObserver(this);
759             InstrumentationRegistry.getInstrumentation().getUiAutomation()
760                     .dropShellPermissionIdentity();
761         }
762 
setSatelliteModeRadios(String expectedSatelliteModeRadios)763         public boolean setSatelliteModeRadios(String expectedSatelliteModeRadios) {
764             logd("setSatelliteModeRadios: expectedSatelliteModeRadios="
765                     + expectedSatelliteModeRadios);
766             String originalSatelliteModeRadios =  Settings.Global.getString(
767                     mContext.getContentResolver(), Settings.Global.SATELLITE_MODE_RADIOS);
768             if (TextUtils.equals(expectedSatelliteModeRadios, originalSatelliteModeRadios)) {
769                 logd("setSatelliteModeRadios: satellite radios mode is already as expected");
770                 return true;
771             }
772 
773             setExpectedSatelliteModeRadios(expectedSatelliteModeRadios);
774             clearSemaphorePermits();
775             Settings.Global.putString(mContext.getContentResolver(),
776                     Settings.Global.SATELLITE_MODE_RADIOS, expectedSatelliteModeRadios);
777             return waitForModeChanged();
778         }
779 
clearSemaphorePermits()780         private void clearSemaphorePermits() {
781             mSemaphore.drainPermits();
782         }
783 
waitForModeChanged()784         private boolean waitForModeChanged() {
785             logd("SatelliteModeRadiosUpdater: waitForModeChanged start");
786             try {
787                 if (!mSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT, TimeUnit.MILLISECONDS)) {
788                     loge("SatelliteModeRadiosUpdater: Timeout to wait for mode changed");
789                     return false;
790                 }
791             } catch (InterruptedException e) {
792                 loge("SatelliteModeRadiosUpdater: waitForModeChanged, e=" + e);
793                 return false;
794             }
795             return true;
796         }
797 
setExpectedSatelliteModeRadios(String expectedSatelliteModeRadios)798         private void setExpectedSatelliteModeRadios(String expectedSatelliteModeRadios) {
799             synchronized (mLock) {
800                 mExpectedSatelliteModeRadios = expectedSatelliteModeRadios;
801             }
802             logd("SatelliteModeRadiosUpdater: mExpectedSatelliteModeRadios="
803                     + mExpectedSatelliteModeRadios);
804         }
805     }
806 
provisionSatellite()807     protected static boolean provisionSatellite() {
808         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
809         String mText = "This is test provision data.";
810         byte[] testProvisionData = mText.getBytes();
811 
812         sSatelliteManager.provisionService(
813                 TOKEN, testProvisionData, null, getContext().getMainExecutor(), error::offer);
814         Integer errorCode;
815         try {
816             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
817         } catch (InterruptedException ex) {
818             loge("provisionSatellite ex=" + ex);
819             return false;
820         }
821         if (errorCode == null || errorCode != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
822             loge("provisionSatellite failed with errorCode=" + errorCode);
823             return false;
824         }
825         return true;
826     }
827 
deprovisionSatellite()828     protected static boolean deprovisionSatellite() {
829         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
830 
831         sSatelliteManager.deprovisionService(
832                 TOKEN, getContext().getMainExecutor(), error::offer);
833         Integer errorCode;
834         try {
835             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
836         } catch (InterruptedException ex) {
837             loge("deprovisionSatellite ex=" + ex);
838             return false;
839         }
840         if (errorCode == null || errorCode != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
841             loge("deprovisionSatellite failed with errorCode=" + errorCode);
842             return false;
843         }
844         return true;
845     }
846 
isSatelliteProvisioned()847     protected static boolean isSatelliteProvisioned() {
848         final AtomicReference<Boolean> provisioned = new AtomicReference<>();
849         final AtomicReference<Integer> errorCode = new AtomicReference<>();
850         CountDownLatch latch = new CountDownLatch(1);
851         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
852                 new OutcomeReceiver<>() {
853                     @Override
854                     public void onResult(Boolean result) {
855                         provisioned.set(result);
856                         latch.countDown();
857                     }
858 
859                     @Override
860                     public void onError(SatelliteManager.SatelliteException exception) {
861                         errorCode.set(exception.getErrorCode());
862                         latch.countDown();
863                     }
864                 };
865 
866         sSatelliteManager.requestIsProvisioned(
867                 getContext().getMainExecutor(), receiver);
868         try {
869             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
870         } catch (InterruptedException ex) {
871             loge("isSatelliteProvisioned ex=" + ex);
872             return false;
873         }
874 
875         Integer error = errorCode.get();
876         Boolean isProvisioned = provisioned.get();
877         if (error == null) {
878             assertNotNull(isProvisioned);
879             return isProvisioned;
880         } else {
881             assertNull(isProvisioned);
882             logd("isSatelliteProvisioned error=" + error);
883             return false;
884         }
885     }
886 
isSatelliteEnabled()887     protected static boolean isSatelliteEnabled() {
888         final AtomicReference<Boolean> enabled = new AtomicReference<>();
889         final AtomicReference<Integer> errorCode = new AtomicReference<>();
890         CountDownLatch latch = new CountDownLatch(1);
891         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
892                 new OutcomeReceiver<>() {
893                     @Override
894                     public void onResult(Boolean result) {
895                         enabled.set(result);
896                         latch.countDown();
897                     }
898 
899                     @Override
900                     public void onError(SatelliteManager.SatelliteException exception) {
901                         errorCode.set(exception.getErrorCode());
902                         latch.countDown();
903                     }
904                 };
905 
906 
907         sSatelliteManager.requestIsEnabled(
908                 getContext().getMainExecutor(), receiver);
909         try {
910             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
911         } catch (InterruptedException ex) {
912             loge("isSatelliteEnabled ex=" + ex);
913             return false;
914         }
915 
916         Integer error = errorCode.get();
917         Boolean isEnabled = enabled.get();
918         if (error == null) {
919             assertNotNull(isEnabled);
920             return isEnabled;
921         } else {
922             assertNull(isEnabled);
923             logd("isSatelliteEnabled error=" + error);
924             return false;
925         }
926     }
927 
isSatelliteDemoModeEnabled()928     protected static boolean isSatelliteDemoModeEnabled() {
929         final AtomicReference<Boolean> enabled = new AtomicReference<>();
930         final AtomicReference<Integer> errorCode = new AtomicReference<>();
931         CountDownLatch latch = new CountDownLatch(1);
932         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
933                 new OutcomeReceiver<>() {
934                     @Override
935                     public void onResult(Boolean result) {
936                         enabled.set(result);
937                         latch.countDown();
938                     }
939 
940                     @Override
941                     public void onError(SatelliteManager.SatelliteException exception) {
942                         errorCode.set(exception.getErrorCode());
943                         latch.countDown();
944                     }
945                 };
946 
947         sSatelliteManager.requestIsDemoModeEnabled(
948                 getContext().getMainExecutor(), receiver);
949         try {
950             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
951         } catch (InterruptedException ex) {
952             loge("isSatelliteDemoModeEnabled ex=" + ex);
953             return false;
954         }
955 
956         Integer error = errorCode.get();
957         Boolean isEnabled = enabled.get();
958         if (error == null) {
959             assertNotNull(isEnabled);
960             return isEnabled;
961         } else {
962             assertNull(isEnabled);
963             logd("isSatelliteEnabled error=" + error);
964             return false;
965         }
966     }
967 
requestSatelliteEnabled(boolean enabled)968     protected static void requestSatelliteEnabled(boolean enabled) {
969         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
970         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled).build(),
971                 getContext().getMainExecutor(), error::offer);
972         Integer errorCode;
973         try {
974             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
975         } catch (InterruptedException ex) {
976             fail("requestSatelliteEnabled failed with ex=" + ex);
977             return;
978         }
979         assertNotNull(errorCode);
980         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
981     }
982 
requestSatelliteEnabled(boolean enabled, long timeoutMillis)983     protected static void requestSatelliteEnabled(boolean enabled, long timeoutMillis) {
984         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
985         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled).build(),
986                 getContext().getMainExecutor(), error::offer);
987         Integer errorCode;
988         try {
989             errorCode = error.poll(timeoutMillis, TimeUnit.MILLISECONDS);
990         } catch (InterruptedException ex) {
991             fail("requestSatelliteEnabled failed with ex=" + ex);
992             return;
993         }
994         assertNotNull(errorCode);
995         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
996     }
997 
requestSatelliteEnabledWithResult(boolean enabled, long timeoutMillis)998     protected static int requestSatelliteEnabledWithResult(boolean enabled, long timeoutMillis) {
999         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1000         sSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(enabled).build(),
1001                 getContext().getMainExecutor(), error::offer);
1002         Integer errorCode = null;
1003         try {
1004             errorCode = error.poll(timeoutMillis, TimeUnit.MILLISECONDS);
1005         } catch (InterruptedException ex) {
1006             fail("requestSatelliteEnabled failed with ex=" + ex);
1007         }
1008         assertNotNull(errorCode);
1009         return errorCode;
1010     }
1011 
requestSatelliteEnabledForDemoMode(boolean enabled)1012     protected static void requestSatelliteEnabledForDemoMode(boolean enabled) {
1013         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1014         sSatelliteManager.requestEnabled(
1015                 new EnableRequestAttributes.Builder(enabled)
1016                         .setDemoMode(true)
1017                         .setEmergencyMode(true)
1018                         .build(),
1019                 getContext().getMainExecutor(), error::offer);
1020         Integer errorCode;
1021         try {
1022             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1023         } catch (InterruptedException ex) {
1024             fail("requestSatelliteEnabled failed with ex=" + ex);
1025             return;
1026         }
1027         assertNotNull(errorCode);
1028         assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (long) errorCode);
1029     }
1030 
requestSatelliteEnabled(boolean enabled, boolean demoEnabled, int expectedError)1031     protected static void requestSatelliteEnabled(boolean enabled, boolean demoEnabled,
1032             int expectedError) {
1033         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
1034         sSatelliteManager.requestEnabled(
1035                 new EnableRequestAttributes.Builder(enabled).setDemoMode(demoEnabled).build(),
1036                 getContext().getMainExecutor(), error::offer);
1037         Integer errorCode;
1038         try {
1039             errorCode = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
1040         } catch (InterruptedException ex) {
1041             fail("requestSatelliteEnabled failed with ex=" + ex);
1042             return;
1043         }
1044         assertNotNull(errorCode);
1045         assertEquals(expectedError, (long) errorCode);
1046     }
1047 
isSatelliteSupported()1048     protected static boolean isSatelliteSupported() {
1049         final AtomicReference<Boolean> supported = new AtomicReference<>();
1050         final AtomicReference<Integer> errorCode = new AtomicReference<>();
1051         CountDownLatch latch = new CountDownLatch(1);
1052         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
1053                 new OutcomeReceiver<>() {
1054                     @Override
1055                     public void onResult(Boolean result) {
1056                         supported.set(result);
1057                         latch.countDown();
1058                     }
1059 
1060                     @Override
1061                     public void onError(SatelliteManager.SatelliteException exception) {
1062                         errorCode.set(exception.getErrorCode());
1063                         latch.countDown();
1064                     }
1065                 };
1066 
1067         sSatelliteManager.requestIsSupported(getContext().getMainExecutor(),
1068                 receiver);
1069         try {
1070             assertTrue(latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
1071         } catch (InterruptedException ex) {
1072             loge("isSatelliteSupported ex=" + ex);
1073             return false;
1074         }
1075 
1076         Integer error = errorCode.get();
1077         Boolean isSupported = supported.get();
1078         if (error == null) {
1079             assertNotNull(isSupported);
1080             logd("isSatelliteSupported isSupported=" + isSupported);
1081             return isSupported;
1082         } else {
1083             assertNull(isSupported);
1084             logd("isSatelliteSupported error=" + error);
1085             return false;
1086         }
1087     }
1088 
turnRadioOff()1089     protected static void turnRadioOff() {
1090         ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener(
1091                 sTelephonyManager.getServiceState(), sTelephonyManager.getRadioPowerState());
1092         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1093                 tm -> tm.registerTelephonyCallback(Runnable::run, callback));
1094         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1095                 tm -> tm.requestRadioPowerOffForReason(TelephonyManager.RADIO_POWER_REASON_USER),
1096                 android.Manifest.permission.MODIFY_PHONE_STATE);
1097         callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_OFF);
1098     }
1099 
turnRadioOn()1100     protected static void turnRadioOn() {
1101         ServiceStateRadioStateListener callback = new ServiceStateRadioStateListener(
1102                 sTelephonyManager.getServiceState(), sTelephonyManager.getRadioPowerState());
1103         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1104                 tm -> tm.registerTelephonyCallback(Runnable::run, callback));
1105         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
1106                 tm -> tm.clearRadioPowerOffForReason(TelephonyManager.RADIO_POWER_REASON_USER),
1107                 android.Manifest.permission.MODIFY_PHONE_STATE);
1108         callback.waitForRadioStateIntent(TelephonyManager.RADIO_POWER_ON);
1109     }
1110 
1111     protected class UwbAdapterStateCallback implements UwbManager.AdapterStateCallback {
1112         private final Semaphore mUwbSemaphore = new Semaphore(0);
1113         private final Object mUwbExpectedStateLock = new Object();
1114         private boolean mUwbExpectedState = false;
1115 
toString(int state)1116         public String toString(int state) {
1117             switch (state) {
1118                 case UwbManager.AdapterStateCallback.STATE_DISABLED:
1119                     return "Disabled";
1120 
1121                 case UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE:
1122                     return "Inactive";
1123 
1124                 case UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE:
1125                     return "Active";
1126 
1127                 default:
1128                     return "";
1129             }
1130         }
1131 
1132         @Override
onStateChanged(int state, int reason)1133         public void onStateChanged(int state, int reason) {
1134             logd("UwbAdapterStateCallback onStateChanged() called, state = " + toString(state));
1135             logd("Adapter state changed reason " + String.valueOf(reason));
1136 
1137             synchronized (mUwbExpectedStateLock) {
1138                 if (mUwbExpectedState == mUwbManager.isUwbEnabled()) {
1139                     try {
1140                         mUwbSemaphore.release();
1141                     } catch (Exception e) {
1142                         loge("UwbAdapterStateCallback onStateChanged(): Got exception, ex=" + e);
1143                     }
1144                 }
1145             }
1146         }
1147 
waitUntilOnUwbStateChanged()1148         public boolean waitUntilOnUwbStateChanged() {
1149             synchronized (mUwbExpectedStateLock) {
1150                 if (mUwbExpectedState == mUwbManager.isUwbEnabled()) {
1151                     return true;
1152                 }
1153             }
1154 
1155             try {
1156                 if (!mUwbSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1157                         TimeUnit.MILLISECONDS)) {
1158                     loge("UwbAdapterStateCallback Timeout to receive "
1159                             + "onStateChanged() callback");
1160                     return false;
1161                 }
1162             } catch (Exception ex) {
1163                 loge("UwbAdapterStateCallback waitUntilOnUwbStateChanged: Got exception=" + ex);
1164                 return false;
1165             }
1166             return true;
1167         }
1168 
setUwbExpectedState(boolean expectedState)1169         public void setUwbExpectedState(boolean expectedState) {
1170             synchronized (mUwbExpectedStateLock) {
1171                 mUwbExpectedState = expectedState;
1172                 mUwbSemaphore.drainPermits();
1173             }
1174         }
1175     }
1176 
1177     protected class BTWifiNFCStateReceiver extends BroadcastReceiver {
1178         private final Semaphore mBTSemaphore = new Semaphore(0);
1179         private final Object mBTExpectedStateLock = new Object();
1180         private boolean mBTExpectedState = false;
1181 
1182         private final Semaphore mNfcSemaphore = new Semaphore(0);
1183         private final Object mNfcExpectedStateLock = new Object();
1184         private boolean mNfcExpectedState = false;
1185 
1186         private final Semaphore mWifiSemaphore = new Semaphore(0);
1187         private final Object mWifiExpectedStateLock = new Object();
1188         private boolean mWifiExpectedState = false;
1189 
1190         @Override
onReceive(Context context, Intent intent)1191         public void onReceive(Context context, Intent intent) {
1192             final String action = intent.getAction();
1193             if (action == null) {
1194                 logd("BTWifiNFCStateReceiver NULL action for intent " + intent);
1195                 return;
1196             }
1197             logd("BTWifiNFCStateReceiver onReceive: action = " + action);
1198 
1199             switch (action) {
1200                 case BluetoothAdapter.ACTION_STATE_CHANGED:
1201                     int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
1202                             BluetoothAdapter.ERROR);
1203                     logd("Bluetooth state updated to " + btState);
1204 
1205                     synchronized (mBTExpectedStateLock) {
1206                         if (mBTExpectedState == mBluetoothAdapter.isEnabled()) {
1207                             try {
1208                                 mBTSemaphore.release();
1209                             } catch (Exception e) {
1210                                 loge("BTWifiNFCStateReceiver onReceive(): Got exception, ex=" + e);
1211                             }
1212                         }
1213                     }
1214                     break;
1215 
1216                 case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED:
1217                     int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, -1);
1218                     logd("Nfc state updated to " + nfcState);
1219 
1220                     synchronized (mNfcExpectedStateLock) {
1221                         if (mNfcExpectedState == mNfcAdapter.isEnabled()) {
1222                             try {
1223                                 mNfcSemaphore.release();
1224                             } catch (Exception e) {
1225                                 loge("BTWifiNFCStateReceiver onReceive(): Got exception, ex=" + e);
1226                             }
1227                         }
1228                     }
1229                     break;
1230 
1231                 case WifiManager.WIFI_STATE_CHANGED_ACTION:
1232                     int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1233                             WifiManager.WIFI_STATE_UNKNOWN);
1234                     logd("Wifi state updated to " + wifiState);
1235 
1236                     synchronized (mWifiExpectedStateLock) {
1237                         if (mWifiExpectedState == mWifiManager.isWifiEnabled()) {
1238                             try {
1239                                 mWifiSemaphore.release();
1240                             } catch (Exception e) {
1241                                 loge("BTWifiNFCStateReceiver onReceive(): Got exception, ex=" + e);
1242                             }
1243                         }
1244                     }
1245                     break;
1246                 default:
1247                     break;
1248             }
1249         }
1250 
waitUntilOnBTStateChanged()1251         public boolean waitUntilOnBTStateChanged() {
1252             logd("waitUntilOnBTStateChanged");
1253             synchronized (mBTExpectedStateLock) {
1254                 if (mBTExpectedState == mBluetoothAdapter.isEnabled()) {
1255                     return true;
1256                 }
1257             }
1258 
1259             try {
1260                 if (!mBTSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1261                         TimeUnit.MILLISECONDS)) {
1262                     loge("BTWifiNFCStateReceiver waitUntilOnBTStateChanged: "
1263                             + "Timeout to receive onStateChanged() callback");
1264                     return false;
1265                 }
1266             } catch (Exception ex) {
1267                 loge("BTWifiNFCStateReceiver waitUntilOnBTStateChanged: Got exception=" + ex);
1268                 return false;
1269             }
1270             return true;
1271         }
1272 
waitUntilOnNfcStateChanged()1273         public boolean waitUntilOnNfcStateChanged() {
1274             synchronized (mNfcExpectedStateLock) {
1275                 if (mNfcExpectedState == mNfcAdapter.isEnabled()) {
1276                     return true;
1277                 }
1278             }
1279 
1280             try {
1281                 if (!mNfcSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1282                         TimeUnit.MILLISECONDS)) {
1283                     loge("BTWifiNFCStateReceiver waitUntilOnNfcStateChanged: "
1284                             + "Timeout to receive onStateChanged() callback");
1285                     return false;
1286                 }
1287             } catch (Exception ex) {
1288                 loge("BTWifiNFCStateReceiver waitUntilOnNfcStateChanged: Got exception=" + ex);
1289                 return false;
1290             }
1291             return true;
1292         }
1293 
waitUntilOnWifiStateChanged()1294         public boolean waitUntilOnWifiStateChanged() {
1295             synchronized (mWifiExpectedStateLock) {
1296                 if (mWifiExpectedState == mWifiManager.isWifiEnabled()) {
1297                     return true;
1298                 }
1299             }
1300 
1301             try {
1302                 if (!mWifiSemaphore.tryAcquire(EXTERNAL_DEPENDENT_TIMEOUT,
1303                         TimeUnit.MILLISECONDS)) {
1304                     loge("BTWifiNFCStateReceiver waitUntilOnWifiStateChanged: "
1305                             + "Timeout to receive onStateChanged() callback");
1306                     return false;
1307                 }
1308             } catch (Exception ex) {
1309                 loge("BTWifiNFCStateReceiver waitUntilOnWifiStateChanged: Got exception=" + ex);
1310                 return false;
1311             }
1312             return true;
1313         }
1314 
setBTExpectedState(boolean expectedState)1315         public void setBTExpectedState(boolean expectedState) {
1316             synchronized (mBTExpectedStateLock) {
1317                 mBTExpectedState = expectedState;
1318                 mBTSemaphore.drainPermits();
1319             }
1320         }
1321 
setWifiExpectedState(boolean expectedState)1322         public void setWifiExpectedState(boolean expectedState) {
1323             synchronized (mWifiExpectedStateLock) {
1324                 mWifiExpectedState = expectedState;
1325                 mWifiSemaphore.drainPermits();
1326             }
1327         }
1328 
setNfcExpectedState(boolean expectedState)1329         public void setNfcExpectedState(boolean expectedState) {
1330             synchronized (mNfcExpectedStateLock) {
1331                 mNfcExpectedState = expectedState;
1332                 mNfcSemaphore.drainPermits();
1333             }
1334         }
1335     }
1336 
logd(@onNull String log)1337     protected static void logd(@NonNull String log) {
1338         Rlog.d(TAG, log);
1339     }
1340 
loge(@onNull String log)1341     protected static void loge(@NonNull String log) {
1342         Rlog.e(TAG, log);
1343     }
1344 
assertSatelliteEnabledInSettings(boolean enabled)1345     protected static void assertSatelliteEnabledInSettings(boolean enabled) {
1346         int satelliteModeEnabled = Settings.Global.getInt(getContext().getContentResolver(),
1347                 Settings.Global.SATELLITE_MODE_ENABLED, 0);
1348         if (enabled) {
1349             assertEquals(satelliteModeEnabled, 1);
1350         } else {
1351             assertEquals(satelliteModeEnabled, 0);
1352         }
1353         logd("requestSatelliteEnabled: " + enabled
1354                 + " : satelliteModeEnabled from settings: " + satelliteModeEnabled);
1355     }
1356 
waitFor(long timeoutMillis)1357     protected static void waitFor(long timeoutMillis) {
1358         Object delayTimeout = new Object();
1359         synchronized (delayTimeout) {
1360             try {
1361                 delayTimeout.wait(timeoutMillis);
1362             } catch (InterruptedException ex) {
1363                 // Ignore the exception
1364                 logd("waitFor: delayTimeout ex=" + ex);
1365             }
1366         }
1367     }
1368 
1369     // Get default active subscription ID.
getActiveSubIDForCarrierSatelliteTest()1370     protected int getActiveSubIDForCarrierSatelliteTest() {
1371         Context context = InstrumentationRegistry.getInstrumentation().getContext();
1372         SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
1373         List<SubscriptionInfo> infos = ShellIdentityUtils.invokeMethodWithShellPermissions(sm,
1374                 SubscriptionManager::getActiveSubscriptionInfoList);
1375 
1376         int defaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
1377         if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
1378                 && isSubIdInInfoList(infos, defaultSubId)) {
1379             return defaultSubId;
1380         }
1381 
1382         defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
1383         if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
1384                 && isSubIdInInfoList(infos, defaultSubId)) {
1385             return defaultSubId;
1386         }
1387 
1388         // Couldn't resolve a default. We can try to resolve a default using the active
1389         // subscriptions.
1390         if (!infos.isEmpty()) {
1391             return infos.get(0).getSubscriptionId();
1392         }
1393         // There must be at least one active subscription.
1394         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1395     }
1396 
isSubIdInInfoList(List<SubscriptionInfo> infos, int subId)1397     private static boolean isSubIdInInfoList(List<SubscriptionInfo> infos, int subId) {
1398         return infos.stream().anyMatch(info -> info.getSubscriptionId() == subId);
1399     }
1400 }
1401