• 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 com.android.phone.satellite.accesscontrol;
18 
19 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_ACCESS_CONFIGURATION;
20 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
21 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
22 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
23 import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED;
24 import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION;
25 import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED;
26 import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED;
27 import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP;
28 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED;
29 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
30 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_LOCATION_DISABLED;
31 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_LOCATION_NOT_AVAILABLE;
32 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
33 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
34 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
35 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
36 
37 import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_CONFIG_DATA_UPDATED;
38 import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_EXTERNAL_REQUEST;
39 import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_LOCATION_SETTINGS_DISABLED;
40 import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED;
41 import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_MCC_CHANGED;
42 import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_UNKNOWN;
43 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_SHARED_PREF;
44 
45 import android.annotation.ArrayRes;
46 import android.annotation.NonNull;
47 import android.annotation.Nullable;
48 import android.app.Notification;
49 import android.app.NotificationChannel;
50 import android.app.NotificationManager;
51 import android.content.BroadcastReceiver;
52 import android.content.ComponentName;
53 import android.content.Context;
54 import android.content.Intent;
55 import android.content.IntentFilter;
56 import android.content.SharedPreferences;
57 import android.content.res.Resources;
58 import android.location.Location;
59 import android.location.LocationManager;
60 import android.location.LocationRequest;
61 import android.os.AsyncResult;
62 import android.os.Build;
63 import android.os.Bundle;
64 import android.os.CancellationSignal;
65 import android.os.FileUtils;
66 import android.os.Handler;
67 import android.os.HandlerExecutor;
68 import android.os.HandlerThread;
69 import android.os.IBinder;
70 import android.os.Looper;
71 import android.os.Message;
72 import android.os.RemoteException;
73 import android.os.ResultReceiver;
74 import android.os.SystemClock;
75 import android.os.SystemProperties;
76 import android.os.UserHandle;
77 import android.provider.DeviceConfig;
78 import android.telecom.TelecomManager;
79 import android.telephony.AnomalyReporter;
80 import android.telephony.CarrierConfigManager;
81 import android.telephony.NetworkRegistrationInfo;
82 import android.telephony.PersistentLogger;
83 import android.telephony.Rlog;
84 import android.telephony.SubscriptionInfo;
85 import android.telephony.SubscriptionManager;
86 import android.telephony.satellite.EarfcnRange;
87 import android.telephony.satellite.ISatelliteCommunicationAccessStateCallback;
88 import android.telephony.satellite.ISatelliteDisallowedReasonsCallback;
89 import android.telephony.satellite.ISatelliteProvisionStateCallback;
90 import android.telephony.satellite.SatelliteAccessConfiguration;
91 import android.telephony.satellite.SatelliteInfo;
92 import android.telephony.satellite.SatelliteManager;
93 import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
94 import android.telephony.satellite.SystemSelectionSpecifier;
95 import android.text.TextUtils;
96 import android.util.IntArray;
97 import android.util.Log;
98 import android.util.Pair;
99 
100 import com.android.internal.R;
101 import com.android.internal.annotations.GuardedBy;
102 import com.android.internal.annotations.VisibleForTesting;
103 import com.android.internal.telephony.IBooleanConsumer;
104 import com.android.internal.telephony.Phone;
105 import com.android.internal.telephony.PhoneFactory;
106 import com.android.internal.telephony.SmsApplication;
107 import com.android.internal.telephony.TelephonyCountryDetector;
108 import com.android.internal.telephony.flags.FeatureFlags;
109 import com.android.internal.telephony.satellite.SatelliteConfig;
110 import com.android.internal.telephony.satellite.SatelliteConstants;
111 import com.android.internal.telephony.satellite.SatelliteController;
112 import com.android.internal.telephony.satellite.SatelliteServiceUtils;
113 import com.android.internal.telephony.satellite.metrics.AccessControllerMetricsStats;
114 import com.android.internal.telephony.satellite.metrics.ConfigUpdaterMetricsStats;
115 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
116 import com.android.internal.telephony.subscription.SubscriptionManagerService;
117 import com.android.internal.telephony.util.TelephonyUtils;
118 import com.android.phone.PhoneGlobals;
119 
120 import java.io.File;
121 import java.io.FileInputStream;
122 import java.io.IOException;
123 import java.io.InputStream;
124 import java.nio.file.Files;
125 import java.nio.file.Path;
126 import java.nio.file.StandardCopyOption;
127 import java.util.ArrayList;
128 import java.util.Arrays;
129 import java.util.Collection;
130 import java.util.HashMap;
131 import java.util.HashSet;
132 import java.util.LinkedHashMap;
133 import java.util.List;
134 import java.util.Locale;
135 import java.util.Map;
136 import java.util.Objects;
137 import java.util.Optional;
138 import java.util.Set;
139 import java.util.UUID;
140 import java.util.concurrent.ConcurrentHashMap;
141 import java.util.concurrent.TimeUnit;
142 import java.util.stream.Collectors;
143 import java.util.stream.IntStream;
144 
145 /**
146  * This module is responsible for making sure that satellite communication can be used by devices
147  * in only regions allowed by OEMs.
148  */
149 public class SatelliteAccessController extends Handler {
150     private static final String TAG = "SatelliteAccessController";
151     /**
152      * UUID to report an anomaly when getting an exception in looking up on-device data for the
153      * current location.
154      */
155     private static final String UUID_ON_DEVICE_LOOKUP_EXCEPTION =
156             "dbea1641-630e-4780-9f25-8337ba6c3563";
157     /**
158      * UUID to report an anomaly when getting an exception in creating the on-device access
159      * controller.
160      */
161     private static final String UUID_CREATE_ON_DEVICE_ACCESS_CONTROLLER_EXCEPTION =
162             "3ac767d8-2867-4d60-97c2-ae9d378a5521";
163     protected static final long WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS =
164             TimeUnit.SECONDS.toMillis(180);
165     protected static final long WAIT_UNTIL_CURRENT_LOCATION_QUERY_IS_DONE_MILLIS =
166             TimeUnit.SECONDS.toMillis(90);
167     protected static final long KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS =
168             TimeUnit.MINUTES.toMillis(30);
169     protected static final int DEFAULT_S2_LEVEL = 12;
170     private static final int DEFAULT_LOCATION_FRESH_DURATION_SECONDS = 600;
171     private static final boolean DEFAULT_SATELLITE_ACCESS_ALLOW = true;
172     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
173     private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
174     private static final boolean DEBUG = !"user".equals(Build.TYPE);
175     private static final int MAX_CACHE_SIZE = 50;
176 
177     protected static final int CMD_IS_SATELLITE_COMMUNICATION_ALLOWED = 1;
178     protected static final int EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT = 2;
179     protected static final int EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT = 3;
180     protected static final int CMD_UPDATE_CONFIG_DATA = 4;
181     protected static final int EVENT_COUNTRY_CODE_CHANGED = 5;
182     protected static final int EVENT_LOCATION_SETTINGS_ENABLED = 6;
183     protected static final int CMD_UPDATE_SYSTEM_SELECTION_CHANNELS = 7;
184     protected static final int EVENT_LOCATION_SETTINGS_DISABLED = 8;
185     protected static final int EVENT_SATELLITE_SUBSCRIPTION_CHANGED = 9;
186     protected static final int EVENT_CONFIG_DATA_UPDATED = 10;
187 
188     public static final int DEFAULT_REGIONAL_SATELLITE_CONFIG_ID = 0;
189     public static final int UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID = -1;
190 
191 
192     private static final String KEY_AVAILABLE_NOTIFICATION_SHOWN = "available_notification_shown";
193     private static final String KEY_UNAVAILABLE_NOTIFICATION_SHOWN =
194             "unavailable_notification_shown";
195     private static final String AVAILABLE_NOTIFICATION_TAG = "available_notification_tag";
196     private static final String UNAVAILABLE_NOTIFICATION_TAG = "unavailable_notification_tag";
197     private static final int NOTIFICATION_ID = 1;
198     private static final String NOTIFICATION_CHANNEL = "satelliteChannel";
199     private static final String NOTIFICATION_CHANNEL_ID = "satellite";
200     private static final int SATELLITE_DISALLOWED_REASON_NONE = -1;
201     private static final List<Integer> DISALLOWED_REASONS_TO_BE_RESET =
202             Arrays.asList(SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION,
203                     SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED);
204 
205     private static final HashMap<Integer, Pair<Integer, Integer>>
206             SATELLITE_SOS_UNAVAILABLE_REASONS = new HashMap<>(Map.of(
207             SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED, new Pair<>(
208                     R.string.satellite_sos_not_supported_notification_title,
209                     R.string.satellite_sos_not_supported_notification_summary),
210             SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED, new Pair<>(
211                     R.string.satellite_sos_not_provisioned_notification_title,
212                     R.string.satellite_sos_not_provisioned_notification_summary),
213             SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION, new Pair<>(
214                     R.string.satellite_sos_not_in_allowed_region_notification_title,
215                     R.string.satellite_sos_not_in_allowed_region_notification_summary),
216             SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP, new Pair<>(
217                     R.string.satellite_sos_unsupported_default_sms_app_notification_title,
218                     R.string.satellite_sos_unsupported_default_sms_app_notification_summary),
219             SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED, new Pair<>(
220                     R.string.satellite_sos_location_disabled_notification_title,
221                     R.string.satellite_sos_location_disabled_notification_summary)
222     ));
223 
224     private static final HashMap<Integer, Pair<Integer, Integer>>
225             SATELLITE_MESSAGING_UNAVAILABLE_REASONS = new HashMap<>(Map.of(
226             SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED, new Pair<>(
227                     R.string.satellite_messaging_not_supported_notification_title,
228                     R.string.satellite_messaging_not_supported_notification_summary),
229             SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED, new Pair<>(
230                     R.string.satellite_messaging_not_provisioned_notification_title,
231                     R.string.satellite_messaging_not_provisioned_notification_summary),
232             SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION, new Pair<>(
233                     R.string.satellite_messaging_not_in_allowed_region_notification_title,
234                     R.string.satellite_messaging_not_in_allowed_region_notification_summary),
235             SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP, new Pair<>(
236                     R.string.satellite_messaging_unsupported_default_sms_app_notification_title,
237                     R.string.satellite_messaging_unsupported_default_sms_app_notification_summary),
238             SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED, new Pair<>(
239                     R.string.satellite_messaging_location_disabled_notification_title,
240                     R.string.satellite_messaging_location_disabled_notification_summary)
241     ));
242 
243     private static SatelliteAccessController sInstance;
244 
245     /** Feature flags to control behavior and errors. */
246     @NonNull
247     private final FeatureFlags mFeatureFlags;
248     @NonNull
249     private final Context mContext;
250     @GuardedBy("mLock")
251     @Nullable
252     protected SatelliteOnDeviceAccessController mSatelliteOnDeviceAccessController;
253     @NonNull
254     private final LocationManager mLocationManager;
255     @NonNull
256     private final TelecomManager mTelecomManager;
257     @NonNull
258     private final TelephonyCountryDetector mCountryDetector;
259     @NonNull
260     private final SatelliteController mSatelliteController;
261     @NonNull
262     private final ControllerMetricsStats mControllerMetricsStats;
263     @NonNull
264     private final AccessControllerMetricsStats mAccessControllerMetricsStats;
265     @NonNull
266     private final ResultReceiver mInternalSatelliteSupportedResultReceiver;
267     @NonNull
268     private final ResultReceiver mInternalSatelliteProvisionedResultReceiver;
269     @NonNull
270     private final IBooleanConsumer mInternalSatelliteSupportedStateCallback;
271     @NonNull
272     private final ISatelliteProvisionStateCallback mInternalSatelliteProvisionStateCallback;
273     @NonNull
274     private final ResultReceiver mInternalUpdateSystemSelectionChannelsResultReceiver;
275     @NonNull
276     protected final Object mLock = new Object();
277     @GuardedBy("mLock")
278     @NonNull
279     private final Set<ResultReceiver> mSatelliteAllowResultReceivers = new HashSet<>();
280     @NonNull
281     private final Set<ResultReceiver>
282             mUpdateSystemSelectionChannelsResultReceivers = new HashSet<>();
283     @NonNull
284     private List<String> mSatelliteCountryCodes;
285     private boolean mIsSatelliteAllowAccessControl;
286     protected int mSatelliteAccessConfigVersion;
287     @Nullable
288     private File mSatelliteS2CellFile;
289     @Nullable
290     private File mSatelliteAccessConfigFile;
291     private long mLocationFreshDurationNanos;
292     @GuardedBy("mLock")
293     private boolean mIsOverlayConfigOverridden = false;
294     @NonNull
295     private List<String> mOverriddenSatelliteCountryCodes;
296     private boolean mOverriddenIsSatelliteAllowAccessControl;
297     @Nullable
298     private File mOverriddenSatelliteS2CellFile;
299     @Nullable
300     private File mOverriddenSatelliteAccessConfigFile;
301     @Nullable
302     private String mOverriddenSatelliteConfigurationFileName;
303     private long mOverriddenLocationFreshDurationNanos;
304 
305     @GuardedBy("mLock")
306     @NonNull
307     private final Map<SatelliteOnDeviceAccessController.LocationToken, Integer>
308             mCachedAccessRestrictionMap = new LinkedHashMap<>() {
309         @Override
310         protected boolean removeEldestEntry(
311                 Entry<SatelliteOnDeviceAccessController.LocationToken, Integer> eldest) {
312             return size() > MAX_CACHE_SIZE;
313         }
314     };
315     @GuardedBy("mLock")
316     @Nullable
317     protected CancellationSignal mLocationRequestCancellationSignal = null;
318     private int mS2Level = DEFAULT_S2_LEVEL;
319     @GuardedBy("mLock")
320     @Nullable
321     private Location mFreshLastKnownLocation = null;
322     @GuardedBy("mLock")
323     @Nullable
324     protected Integer mRegionalConfigId = null;
325     @GuardedBy("mLock")
326     @Nullable
327     protected Integer mNewRegionalConfigId = null;
328     @NonNull
329     private final CarrierConfigManager mCarrierConfigManager;
330     @NonNull
331     private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
332     /**
333      * Key: Sub Id, Value: (key: Regional satellite config Id, value: SatelliteRegionalConfig
334      * contains satellite config IDs and set of earfcns in the corresponding regions).
335      */
336     @GuardedBy("mRegionalSatelliteEarfcnsLock")
337     private Map<Integer, Map<Integer, SatelliteRegionalConfig>>
338             mSatelliteRegionalConfigPerSubMap = new HashMap();
339     @NonNull private final Object mRegionalSatelliteEarfcnsLock = new Object();
340 
341     /** Key: Config ID; Value: SatelliteAccessConfiguration */
342     @GuardedBy("mLock")
343     @Nullable
344     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
345     protected Map<Integer, SatelliteAccessConfiguration> mSatelliteAccessConfigMap;
346 
347     /** These are used for CTS test */
348     private Path mCtsSatS2FilePath = null;
349     private Path mCtsSatelliteAccessConfigurationFilePath = null;
350     protected static final String GOOGLE_US_SAN_SAT_S2_FILE_NAME = "google_us_san_sat_s2.dat";
351     protected static final String GOOGLE_US_SAN_SAT_MTV_S2_FILE_NAME =
352             "google_us_san_mtv_sat_s2.dat";
353     protected static final String SATELLITE_ACCESS_CONFIG_FILE_NAME =
354             "satellite_access_config.json";
355 
356     /** These are for config updater config data */
357     private static final String SATELLITE_ACCESS_CONTROL_DATA_DIR = "satellite_access_control";
358     private static final String CONFIG_UPDATER_S2_CELL_FILE_NAME = "config_updater_sat_s2.dat";
359     private static final String CONFIG_UPDATER_SATELLITE_ACCESS_CONFIG_FILE_NAME =
360             "config_updater_satellite_access_config.json";
361     private static final int MIN_S2_LEVEL = 0;
362     private static final int MAX_S2_LEVEL = 30;
363     private static final String CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY =
364             "config_updater_satellite_country_codes";
365     private static final String CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY =
366             "config_updater_satellite_is_allow_access_control";
367     protected static final String CONFIG_UPDATER_SATELLITE_VERSION_KEY =
368             "config_updater_satellite_version";
369 
370     private static final String LATEST_SATELLITE_COMMUNICATION_ALLOWED_SET_TIME_KEY =
371             "latest_satellite_communication_allowed_set_time";
372     private static final String LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY =
373             "latest_satellite_communication_allowed";
374 
375     private SharedPreferences mSharedPreferences;
376     private final ConfigUpdaterMetricsStats mConfigUpdaterMetricsStats;
377     @Nullable
378     private PersistentLogger mPersistentLogger = null;
379 
380     private final Object mPossibleChangeInSatelliteAllowedRegionLock = new Object();
381     @GuardedBy("mPossibleChangeInSatelliteAllowedRegionLock")
382     private boolean mIsSatelliteAllowedRegionPossiblyChanged = false;
383     protected long mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos = 0;
384 
385     protected int mRetryCountForValidatingPossibleChangeInAllowedRegion;
386     protected static final int
387             DEFAULT_DELAY_MINUTES_BEFORE_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION = 10;
388     protected static final int
389             DEFAULT_MAX_RETRY_COUNT_FOR_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION = 3;
390     protected static final int DEFAULT_THROTTLE_INTERVAL_FOR_LOCATION_QUERY_MINUTES = 10;
391     private static final int MAX_EARFCN_ARRAY_LENGTH = 32;
392 
393     private long mRetryIntervalToEvaluateUserInSatelliteAllowedRegion = 0;
394     private int mMaxRetryCountForValidatingPossibleChangeInAllowedRegion = 0;
395     private long mLocationQueryThrottleIntervalNanos = 0;
396 
397     @NonNull
398     protected ResultReceiver mHandlerForSatelliteAllowedResult;
399 
400     /**
401      * Map key: binder of the callback, value: callback to receive the satellite communication
402      * allowed state changed events.
403      */
404     private final ConcurrentHashMap<IBinder, ISatelliteCommunicationAccessStateCallback>
405             mSatelliteCommunicationAccessStateChangedListeners = new ConcurrentHashMap<>();
406     protected final Object mSatelliteCommunicationAllowStateLock = new Object();
407     @GuardedBy("mSatelliteCommunicationAllowStateLock")
408     protected boolean mCurrentSatelliteAllowedState = false;
409 
410     private final ConcurrentHashMap<IBinder, ISatelliteDisallowedReasonsCallback>
411             mSatelliteDisallowedReasonsChangedListeners = new ConcurrentHashMap<>();
412     private final Object mSatelliteDisallowedReasonsLock = new Object();
413 
414     protected static final long ALLOWED_STATE_CACHE_VALID_DURATION_NANOS =
415             TimeUnit.HOURS.toNanos(4);
416 
417     private boolean mLatestSatelliteCommunicationAllowed;
418     protected long mLatestSatelliteCommunicationAllowedSetTime;
419 
420     private long mLocationQueryStartTimeMillis;
421     private long mOnDeviceLookupStartTimeMillis;
422     private long mTotalCheckingStartTimeMillis;
423 
424     private Notification mSatelliteAvailableNotification;
425     // Key: SatelliteManager#SatelliteDisallowedReason; Value: Notification
426     private final Map<Integer, Notification> mSatelliteUnAvailableNotifications = new HashMap<>();
427     private NotificationManager mNotificationManager;
428     @GuardedBy("mSatelliteDisallowedReasonsLock")
429     private final List<Integer> mSatelliteDisallowedReasons = new ArrayList<>();
430 
431     private boolean mIsLocationManagerEnabled = false;
432 
433     protected BroadcastReceiver mLocationModeChangedBroadcastReceiver = new BroadcastReceiver() {
434         @Override
435         public void onReceive(Context context, Intent intent) {
436             // Check whether user has turned on/off location manager from settings menu
437             if (intent.getAction().equals(LocationManager.MODE_CHANGED_ACTION)) {
438                 plogd("LocationManager mode is changed");
439                 if (mLocationManager.isLocationEnabled()) {
440                     plogd("Location settings is just enabled");
441                     sendRequestAsync(EVENT_LOCATION_SETTINGS_ENABLED, null);
442                 } else {
443                     plogd("Location settings is just disabled");
444                     sendRequestAsync(EVENT_LOCATION_SETTINGS_DISABLED, null);
445                 }
446             }
447 
448             // Check whether location manager has been enabled when boot up
449             if (intent.getAction().equals(LocationManager.PROVIDERS_CHANGED_ACTION)) {
450                 plogd("mLocationModeChangedBroadcastReceiver: " + intent.getAction()
451                         + ", mIsLocationManagerEnabled= " + mIsLocationManagerEnabled);
452                 if (!mIsLocationManagerEnabled) {
453                     if (mLocationManager.isLocationEnabled()) {
454                         plogd("Location manager is enabled");
455                         mIsLocationManagerEnabled = true;
456                         boolean isResultReceiverEmpty;
457                         synchronized (mLock) {
458                             isResultReceiverEmpty = mSatelliteAllowResultReceivers.isEmpty();
459                         }
460                         if (isResultReceiverEmpty) {
461                             sendRequestAsync(EVENT_LOCATION_SETTINGS_ENABLED, null);
462                         } else {
463                             plogd("delayed EVENT_LOCATION_SETTINGS_ENABLED due to "
464                                     + "requestIsCommunicationAllowedForCurrentLocation is "
465                                     + "already being processed");
466                             sendDelayedRequestAsync(EVENT_LOCATION_SETTINGS_ENABLED, null,
467                                     WAIT_UNTIL_CURRENT_LOCATION_QUERY_IS_DONE_MILLIS);
468                         }
469                     } else {
470                         plogd("Location manager is still disabled, wait until next enabled event");
471                     }
472                 }
473             }
474         }
475     };
476 
477     private final Object mIsAllowedCheckBeforeEnablingSatelliteLock = new Object();
478     @GuardedBy("mIsAllowedCheckBeforeEnablingSatelliteLock")
479     private boolean mIsAllowedCheckBeforeEnablingSatellite;
480     private boolean mIsCurrentLocationEligibleForNotification = false;
481     private boolean mIsProvisionEligibleForNotification = false;
482 
483     /**
484      * Create a SatelliteAccessController instance.
485      *
486      * @param context                           The context associated with the
487      *                                          {@link SatelliteAccessController} instance.
488      * @param featureFlags                      The FeatureFlags that are supported.
489      * @param locationManager                   The LocationManager for querying current
490      *                                          location of
491      *                                          the device.
492      * @param looper                            The Looper to run the SatelliteAccessController
493      *                                          on.
494      * @param satelliteOnDeviceAccessController The on-device satellite access controller
495      *                                          instance.
496      */
497     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
SatelliteAccessController(@onNull Context context, @NonNull FeatureFlags featureFlags, @NonNull Looper looper, @NonNull LocationManager locationManager, @NonNull TelecomManager telecomManager, @Nullable SatelliteOnDeviceAccessController satelliteOnDeviceAccessController, @Nullable File s2CellFile)498     protected SatelliteAccessController(@NonNull Context context,
499             @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
500             @NonNull LocationManager locationManager, @NonNull TelecomManager telecomManager,
501             @Nullable SatelliteOnDeviceAccessController satelliteOnDeviceAccessController,
502             @Nullable File s2CellFile) {
503         super(looper);
504         mContext = context;
505         mPersistentLogger = SatelliteServiceUtils.getPersistentLogger(context);
506         mFeatureFlags = featureFlags;
507         mLocationManager = locationManager;
508         mTelecomManager = telecomManager;
509         mSatelliteOnDeviceAccessController = satelliteOnDeviceAccessController;
510 
511         mCountryDetector = TelephonyCountryDetector.getInstance(context, mFeatureFlags);
512         mCountryDetector.registerForCountryCodeChanged(this,
513                 EVENT_COUNTRY_CODE_CHANGED, null);
514         initializeHandlerForSatelliteAllowedResult();
515         setIsSatelliteAllowedRegionPossiblyChanged(false);
516 
517         mSatelliteController = SatelliteController.getInstance();
518         mControllerMetricsStats = ControllerMetricsStats.getInstance();
519         mAccessControllerMetricsStats = AccessControllerMetricsStats.getInstance();
520         initSharedPreferences(context);
521         checkSharedPreference();
522 
523         loadOverlayConfigs(context);
524         // loadConfigUpdaterConfigs has to be called after loadOverlayConfigs
525         // since config updater config has higher priority and thus can override overlay config
526         loadConfigUpdaterConfigs();
527         mSatelliteController.registerForConfigUpdateChanged(this, CMD_UPDATE_CONFIG_DATA,
528                 context);
529         mSatelliteController.registerForSatelliteSubIdChanged(this,
530                 EVENT_SATELLITE_SUBSCRIPTION_CHANGED, context);
531         if (s2CellFile != null) {
532             mSatelliteS2CellFile = s2CellFile;
533         }
534         mInternalSatelliteSupportedResultReceiver = new ResultReceiver(this) {
535             @Override
536             protected void onReceiveResult(int resultCode, Bundle resultData) {
537                 handleIsSatelliteSupportedResult(resultCode, resultData);
538             }
539         };
540         mSatelliteController.incrementResultReceiverCount(
541                 "SAC:mInternalSatelliteSupportedResultReceiver");
542 
543         mInternalSatelliteProvisionedResultReceiver = new ResultReceiver(this) {
544             @Override
545             protected void onReceiveResult(int resultCode, Bundle resultData) {
546                 handleIsSatelliteProvisionedResult(resultCode, resultData);
547             }
548         };
549 
550         mConfigUpdaterMetricsStats = ConfigUpdaterMetricsStats.getOrCreateInstance();
551         initializeSatelliteSystemNotification(context);
552         registerDefaultSmsAppChangedBroadcastReceiver(context);
553 
554         mInternalSatelliteSupportedStateCallback = new IBooleanConsumer.Stub() {
555             @Override
556             public void accept(boolean isSupported) {
557                 logd("onSatelliteSupportedStateChanged: isSupported=" + isSupported);
558                 if (isSupported) {
559                     final String caller = "SAC:onSatelliteSupportedStateChanged";
560                     requestIsCommunicationAllowedForCurrentLocation(
561                             new ResultReceiver(null) {
562                                 @Override
563                                 protected void onReceiveResult(int resultCode, Bundle resultData) {
564                                     mSatelliteController.decrementResultReceiverCount(caller);
565                                     // do nothing
566                                 }
567                             }, false);
568                     mSatelliteController.incrementResultReceiverCount(caller);
569                     if (isReasonPresentInSatelliteDisallowedReasons(
570                             SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED)) {
571                         removeReasonFromSatelliteDisallowedReasons(
572                                 SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED);
573                         handleEventDisallowedReasonsChanged();
574                     }
575                 } else {
576                     if (!isReasonPresentInSatelliteDisallowedReasons(
577                             SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED)) {
578                         addReasonToSatelliteDisallowedReasons(
579                                 SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED);
580                         handleEventDisallowedReasonsChanged();
581                     }
582                 }
583             }
584         };
585         int result = mSatelliteController.registerForSatelliteSupportedStateChanged(
586                 mInternalSatelliteSupportedStateCallback);
587         plogd("registerForSatelliteSupportedStateChanged result: " + result);
588 
589         mInternalSatelliteProvisionStateCallback = new ISatelliteProvisionStateCallback.Stub() {
590             @Override
591             public void onSatelliteProvisionStateChanged(boolean isProvisioned) {
592                 logd("onSatelliteProvisionStateChanged: isProvisioned=" + isProvisioned);
593                 if (isProvisioned) {
594                     mIsProvisionEligibleForNotification = true;
595                     final String caller = "SAC:onSatelliteProvisionStateChanged";
596                     requestIsCommunicationAllowedForCurrentLocation(
597                             new ResultReceiver(null) {
598                                 @Override
599                                 protected void onReceiveResult(int resultCode, Bundle resultData) {
600                                     mSatelliteController.decrementResultReceiverCount(caller);
601                                     // do nothing
602                                 }
603                             }, false);
604                     mSatelliteController.incrementResultReceiverCount(caller);
605                     if (isReasonPresentInSatelliteDisallowedReasons(
606                             SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED)) {
607                         removeReasonFromSatelliteDisallowedReasons(
608                                 SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED);
609                         handleEventDisallowedReasonsChanged();
610                     }
611                 } else {
612                     if (!isReasonPresentInSatelliteDisallowedReasons(
613                             SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED)) {
614                         addReasonToSatelliteDisallowedReasons(
615                                 SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED);
616                         handleEventDisallowedReasonsChanged();
617                     }
618                 }
619             }
620 
621             @Override
622             public void onSatelliteSubscriptionProvisionStateChanged(
623                     List<SatelliteSubscriberProvisionStatus> satelliteSubscriberProvisionStatus) {
624                 logd("onSatelliteSubscriptionProvisionStateChanged: "
625                         + satelliteSubscriberProvisionStatus);
626             }
627         };
628         result = mSatelliteController.registerForSatelliteProvisionStateChanged(
629                 mInternalSatelliteProvisionStateCallback);
630         plogd("registerForSatelliteProvisionStateChanged result: " + result);
631 
632         mInternalUpdateSystemSelectionChannelsResultReceiver = new ResultReceiver(this) {
633             @Override
634             protected void onReceiveResult(int resultCode, Bundle resultData) {
635                 plogd("UpdateSystemSelectionChannels.onReceiveResult: resultCode=" + resultCode
636                           + ", resultData=" + resultData);
637                 sendUpdateSystemSelectionChannelsResult(resultCode, resultData);
638             }
639         };
640 
641         // Init the SatelliteOnDeviceAccessController so that the S2 level can be cached
642         initSatelliteOnDeviceAccessController();
643         registerLocationModeChangedBroadcastReceiver(context);
644 
645         mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
646         mCarrierConfigChangeListener =
647                 (slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigChanged(
648                     context, slotIndex, subId, carrierId, specificCarrierId);
649 
650         if (mCarrierConfigManager != null) {
651             mCarrierConfigManager.registerCarrierConfigChangeListener(
652                     new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
653         }
654     }
655 
updateCurrentSatelliteAllowedState(boolean isAllowed)656     private void updateCurrentSatelliteAllowedState(boolean isAllowed) {
657         plogd("updateCurrentSatelliteAllowedState");
658         synchronized (mSatelliteCommunicationAllowStateLock) {
659             if (isAllowed != mCurrentSatelliteAllowedState) {
660                 plogd("updatedValue = " + isAllowed + " | mCurrentSatelliteAllowedState = "
661                         + mCurrentSatelliteAllowedState);
662                 mCurrentSatelliteAllowedState = isAllowed;
663                 notifySatelliteCommunicationAllowedStateChanged(isAllowed);
664                 mControllerMetricsStats.reportAllowedStateChanged();
665                 if (!isAllowed) {
666                     synchronized (mLock) {
667                         plogd("updateCurrentSatelliteAllowedState : set mNewRegionalConfigId null");
668                         mNewRegionalConfigId = null;
669                     }
670                 }
671             }
672             updateRegionalConfigId();
673         }
674     }
675 
676     /** @return the singleton instance of {@link SatelliteAccessController} */
getOrCreateInstance( @onNull Context context, @NonNull FeatureFlags featureFlags)677     public static synchronized SatelliteAccessController getOrCreateInstance(
678             @NonNull Context context, @NonNull FeatureFlags featureFlags) {
679         if (sInstance == null) {
680             HandlerThread handlerThread = new HandlerThread("SatelliteAccessController");
681             handlerThread.start();
682             LocationManager lm = context.createAttributionContext("telephony")
683                     .getSystemService(LocationManager.class);
684             sInstance = new SatelliteAccessController(context, featureFlags,
685                     handlerThread.getLooper(), lm,
686                     context.getSystemService(TelecomManager.class), null, null);
687         }
688         return sInstance;
689     }
690 
691     @Override
handleMessage(Message msg)692     public void handleMessage(Message msg) {
693         switch (msg.what) {
694             case CMD_IS_SATELLITE_COMMUNICATION_ALLOWED:
695                 handleCmdIsSatelliteAllowedForCurrentLocation(
696                         (Pair<Integer, ResultReceiver>) msg.obj);
697                 break;
698             case EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT:
699                 handleWaitForCurrentLocationTimedOutEvent();
700                 break;
701             case EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT:
702                 cleanupOnDeviceAccessControllerResources();
703                 break;
704             case CMD_UPDATE_CONFIG_DATA:
705                 AsyncResult ar = (AsyncResult) msg.obj;
706                 updateSatelliteAccessDataWithConfigUpdaterData((Context) ar.userObj);
707                 break;
708 
709             case EVENT_CONFIG_DATA_UPDATED:
710                 plogd("EVENT_CONFIG_DATA_UPDATED");      // Fall through
711             case EVENT_LOCATION_SETTINGS_ENABLED:
712                 plogd("EVENT_LOCATION_SETTINGS_ENABLED");        // Fall through
713             case EVENT_LOCATION_SETTINGS_DISABLED:
714                 plogd("EVENT_LOCATION_SETTINGS_DISABLED");       // Fall through
715             case EVENT_COUNTRY_CODE_CHANGED:
716                 plogd("EVENT_COUNTRY_CODE_CHANGED");
717                 handleSatelliteAllowedRegionPossiblyChanged(msg.what);
718                 break;
719             case CMD_UPDATE_SYSTEM_SELECTION_CHANNELS:
720                 handleCmdUpdateSystemSelectionChannels((ResultReceiver) msg.obj);
721                 break;
722             case EVENT_SATELLITE_SUBSCRIPTION_CHANGED:
723                 plogd("Event: EVENT_SATELLITE_SUBSCRIPTION_CHANGED");
724                 initializeSatelliteSystemNotification(mContext);
725                 handleEventDisallowedReasonsChanged();
726                 break;
727             default:
728                 plogw("SatelliteAccessControllerHandler: unexpected message code: " + msg.what);
729                 break;
730         }
731     }
732 
733     /**
734      * Request to get whether satellite communication is allowed for the current location.
735      *
736      * @param result The result receiver that returns whether satellite communication is allowed
737      *               for the current location if the request is successful or an error code
738      *               if the request failed.
739      */
requestIsCommunicationAllowedForCurrentLocation( @onNull ResultReceiver result, boolean enablingSatellite)740     public void requestIsCommunicationAllowedForCurrentLocation(
741             @NonNull ResultReceiver result, boolean enablingSatellite) {
742         plogd("requestIsCommunicationAllowedForCurrentLocation : "
743                 + "enablingSatellite is " + enablingSatellite);
744         synchronized (mIsAllowedCheckBeforeEnablingSatelliteLock) {
745             mIsAllowedCheckBeforeEnablingSatellite = enablingSatellite;
746         }
747         mAccessControllerMetricsStats.setTriggeringEvent(TRIGGERING_EVENT_EXTERNAL_REQUEST);
748         sendRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED,
749                 new Pair<>(mSatelliteController.getSelectedSatelliteSubId(), result));
750         mSatelliteController.incrementResultReceiverCount(
751                 "SAC:requestIsCommunicationAllowedForCurrentLocation");
752     }
753 
754     /**
755      * Request to get satellite access configuration for the current location.
756      *
757      * @param result The result receiver that returns satellite access configuration
758      *               for the current location if the request is successful or an error code
759      *               if the request failed.
760      */
requestSatelliteAccessConfigurationForCurrentLocation( @onNull ResultReceiver result)761     public void requestSatelliteAccessConfigurationForCurrentLocation(
762             @NonNull ResultReceiver result) {
763         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
764             plogd("carrierRoamingNbIotNtnFlag is disabled");
765             result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
766             return;
767         }
768         plogd("requestSatelliteAccessConfigurationForCurrentLocation");
769         ResultReceiver internalResultReceiver = new ResultReceiver(this) {
770             @Override
771             protected void onReceiveResult(int resultCode, Bundle resultData) {
772                 plogd("requestSatelliteAccessConfigurationForCurrentLocation: resultCode="
773                         + resultCode + ", resultData=" + resultData);
774                 boolean isSatelliteCommunicationAllowed = false;
775                 if (resultCode == SATELLITE_RESULT_SUCCESS) {
776                     if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
777                         isSatelliteCommunicationAllowed =
778                                 resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
779                     } else {
780                         loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
781                         result.send(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
782                         return;
783                     }
784                 } else {
785                     loge("resultCode is not SATELLITE_RESULT_SUCCESS.");
786                     result.send(resultCode, null);
787                     return;
788                 }
789 
790                 SatelliteAccessConfiguration satelliteAccessConfig = null;
791                 synchronized (mLock) {
792                     if (isSatelliteCommunicationAllowed && SatelliteAccessConfigurationParser
793                             .isRegionalConfigIdValid(mRegionalConfigId)) {
794                         plogd("requestSatelliteAccessConfigurationForCurrentLocation : "
795                                 + "mRegionalConfigId is " + mRegionalConfigId);
796                         satelliteAccessConfig = Optional.ofNullable(mSatelliteAccessConfigMap)
797                                 .map(map -> map.get(mRegionalConfigId))
798                                 .orElse(null);
799                     }
800                 }
801                 plogd("requestSatelliteAccessConfigurationForCurrentLocation : "
802                         + "satelliteAccessConfig is " + satelliteAccessConfig);
803                 if (satelliteAccessConfig == null) {
804                     result.send(SATELLITE_RESULT_NO_RESOURCES, null);
805                 } else {
806                     Bundle bundle = new Bundle();
807                     bundle.putParcelable(KEY_SATELLITE_ACCESS_CONFIGURATION, satelliteAccessConfig);
808                     result.send(resultCode, bundle);
809                 }
810             }
811         };
812         requestIsCommunicationAllowedForCurrentLocation(internalResultReceiver, false);
813     }
814 
815     /**
816      * This API should be used by only CTS tests to override the overlay configs of satellite
817      * access controller.
818      */
setSatelliteAccessControlOverlayConfigs(boolean reset, boolean isAllowed, @Nullable String s2CellFile, long locationFreshDurationNanos, @Nullable List<String> satelliteCountryCodes, @Nullable String satelliteConfigurationFile)819     public boolean setSatelliteAccessControlOverlayConfigs(boolean reset, boolean isAllowed,
820             @Nullable String s2CellFile, long locationFreshDurationNanos,
821             @Nullable List<String> satelliteCountryCodes,
822             @Nullable String satelliteConfigurationFile) {
823         if (!isMockModemAllowed()) {
824             plogd("setSatelliteAccessControllerOverlayConfigs: mock modem is not allowed");
825             return false;
826         }
827         plogd("setSatelliteAccessControlOverlayConfigs: reset=" + reset
828                 + ", isAllowed" + isAllowed + ", s2CellFile=" + s2CellFile
829                 + ", locationFreshDurationNanos=" + locationFreshDurationNanos
830                 + ", satelliteCountryCodes=" + ((satelliteCountryCodes != null)
831                 ? String.join(", ", satelliteCountryCodes) : null)
832                 + ", satelliteConfigurationFile=" + satelliteConfigurationFile);
833         synchronized (mLock) {
834             if (reset) {
835                 mIsOverlayConfigOverridden = false;
836                 cleanUpCtsResources();
837                 cleanUpTelephonyConfigs();
838                 cleanUpSatelliteAccessConfigOtaResources();
839                 cleanupSatelliteConfigOtaResources();
840             } else {
841                 mIsOverlayConfigOverridden = true;
842                 mOverriddenIsSatelliteAllowAccessControl = isAllowed;
843                 if (!TextUtils.isEmpty(s2CellFile)) {
844                     mOverriddenSatelliteS2CellFile = getTestSatelliteS2File(s2CellFile);
845                     if (!mOverriddenSatelliteS2CellFile.exists()) {
846                         plogd("The overriding file "
847                                 + mOverriddenSatelliteS2CellFile.getAbsolutePath()
848                                 + " does not exist");
849                         mOverriddenSatelliteS2CellFile = null;
850                     }
851                     mCachedAccessRestrictionMap.clear();
852                 } else {
853                     mOverriddenSatelliteS2CellFile = null;
854                 }
855                 if (!TextUtils.isEmpty(satelliteConfigurationFile)) {
856                     mOverriddenSatelliteAccessConfigFile = getTestSatelliteConfiguration(
857                             satelliteConfigurationFile);
858                     if (!mOverriddenSatelliteAccessConfigFile.exists()) {
859                         plogd("The overriding file "
860                                 + mOverriddenSatelliteAccessConfigFile.getAbsolutePath()
861                                 + " does not exist");
862                         mOverriddenSatelliteAccessConfigFile = null;
863                     }
864                 } else {
865                     mOverriddenSatelliteAccessConfigFile = null;
866                 }
867                 mOverriddenLocationFreshDurationNanos = locationFreshDurationNanos;
868                 if (satelliteCountryCodes != null) {
869                     mOverriddenSatelliteCountryCodes = satelliteCountryCodes;
870                 } else {
871                     mOverriddenSatelliteCountryCodes = new ArrayList<>();
872                 }
873             }
874             cleanupOnDeviceAccessControllerResources();
875             initSatelliteOnDeviceAccessController();
876         }
877         return true;
878     }
879 
880     /**
881      * Report updated system selection to modem and report the update result.
882      */
updateSystemSelectionChannels(@onNull ResultReceiver result)883     public void updateSystemSelectionChannels(@NonNull ResultReceiver result) {
884         plogd("updateSystemSelectionChannels");
885         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
886             plogd("updateSystemSelectionChannels: "
887                     + "carrierRoamingNbIotNtn flag is disabled");
888             result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
889             return;
890         }
891         synchronized (mLock) {
892             if (mRegionalConfigId == null) {
893                 plogd("updateSystemSelectionChannels: Invalid Regional config ID."
894                         + " System Selection channels can not be passed down to modem");
895                 result.send(SATELLITE_RESULT_ACCESS_BARRED, null);
896                 return;
897             }
898         }
899         sendRequestAsync(CMD_UPDATE_SYSTEM_SELECTION_CHANNELS, result);
900     }
901 
getTestSatelliteS2File(String fileName)902     protected File getTestSatelliteS2File(String fileName) {
903         plogd("getTestSatelliteS2File: fileName=" + fileName);
904         if (TextUtils.equals(fileName, GOOGLE_US_SAN_SAT_S2_FILE_NAME)
905                 || TextUtils.equals(fileName, GOOGLE_US_SAN_SAT_MTV_S2_FILE_NAME)) {
906             mCtsSatS2FilePath = copyTestAssetFileToPhoneDirectory(fileName);
907             if (mCtsSatS2FilePath != null) {
908                 return mCtsSatS2FilePath.toFile();
909             } else {
910                 ploge("getTestSatelliteS2File: mCtsSatS2FilePath is null");
911             }
912         }
913         return new File(fileName);
914     }
915 
getTestSatelliteConfiguration(String fileName)916     protected File getTestSatelliteConfiguration(String fileName) {
917         plogd("getTestSatelliteConfiguration: fileName=" + fileName);
918         if (TextUtils.equals(fileName, SATELLITE_ACCESS_CONFIG_FILE_NAME)) {
919             mCtsSatelliteAccessConfigurationFilePath = copyTestAssetFileToPhoneDirectory(fileName);
920             if (mCtsSatelliteAccessConfigurationFilePath != null) {
921                 return mCtsSatelliteAccessConfigurationFilePath.toFile();
922             } else {
923                 ploge("getTestSatelliteConfiguration: mCtsSatelliteConfigurationFilePath is null");
924             }
925         }
926         return new File(fileName);
927     }
928 
929     @Nullable
copyTestAssetFileToPhoneDirectory(String sourceFileName)930     private static Path copyTestAssetFileToPhoneDirectory(String sourceFileName) {
931         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
932         File ctsFile = phoneGlobals.getDir("cts", Context.MODE_PRIVATE);
933         if (!ctsFile.exists()) {
934             ctsFile.mkdirs();
935         }
936 
937         Path targetDir = ctsFile.toPath();
938         Path targetFilePath = targetDir.resolve(sourceFileName);
939         try {
940             var assetManager = phoneGlobals.getAssets();
941             if (assetManager == null) {
942                 loge("copyTestAssetFileToPhoneDirectory: no assets");
943                 return null;
944             }
945             InputStream inputStream = assetManager.open(sourceFileName);
946             if (inputStream == null) {
947                 loge("copyTestAssetFileToPhoneDirectory: Resource=" + sourceFileName
948                         + " not found");
949                 return null;
950             } else {
951                 Files.copy(inputStream, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
952             }
953         } catch (IOException ex) {
954             loge("copyTestAssetFileToPhoneDirectory: ex=" + ex);
955             return null;
956         }
957         return targetFilePath;
958     }
959 
960     @Nullable
copyFileToLocalDirectory(@onNull File sourceFile, @NonNull String targetFileName)961     private static File copyFileToLocalDirectory(@NonNull File sourceFile,
962             @NonNull String targetFileName) {
963         logd(
964                 "copyFileToLocalDirectory: Copying sourceFile:"
965                         + sourceFile.getAbsolutePath()
966                         + " to targetFileName:"
967                         + targetFileName);
968         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
969         File satelliteAccessControlDir = phoneGlobals.getDir(
970                 SATELLITE_ACCESS_CONTROL_DATA_DIR, Context.MODE_PRIVATE);
971         if (!satelliteAccessControlDir.exists()) {
972             satelliteAccessControlDir.mkdirs();
973         }
974 
975         Path targetDir = satelliteAccessControlDir.toPath();
976         Path targetFilePath = targetDir.resolve(targetFileName);
977         logd(
978                 "copyFileToLocalDirectory: Copying from sourceFile="
979                         + sourceFile.getAbsolutePath()
980                         + " to targetFilePath="
981                         + targetFilePath);
982         try {
983             InputStream inputStream = new FileInputStream(sourceFile);
984             if (inputStream == null) {
985                 loge("copyFileToLocalDirectory: Resource=" + sourceFile.getAbsolutePath()
986                         + " not found");
987                 return null;
988             } else {
989                 Files.copy(inputStream, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
990             }
991         } catch (IOException ex) {
992             loge("copyFileToLocalDirectory: ex=" + ex);
993             return null;
994         }
995 
996         File targetFile = targetFilePath.toFile();
997         if (targetFile == null || !targetFile.exists()) {
998             loge("copyFileToLocalDirectory: targetFile is null or not exist");
999             return null;
1000         }
1001         logd(
1002                 "copyFileToLocalDirectory: Copied from sourceFile="
1003                         + sourceFile.getAbsolutePath()
1004                         + " to targetFilePath="
1005                         + targetFilePath);
1006         return targetFile;
1007     }
1008 
1009     @Nullable
getConfigUpdaterSatelliteConfigFileFromLocalDirectory(@onNull String fileName)1010     private File getConfigUpdaterSatelliteConfigFileFromLocalDirectory(@NonNull String fileName) {
1011         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
1012         File satelliteAccessControlDataDir = phoneGlobals.getDir(
1013                 SATELLITE_ACCESS_CONTROL_DATA_DIR, Context.MODE_PRIVATE);
1014         if (!satelliteAccessControlDataDir.exists()) {
1015             ploge("getConfigUpdaterSatelliteConfigFileFromLocalDirectory: "
1016                     + "Directory: " + satelliteAccessControlDataDir.getAbsoluteFile()
1017                     + " is not exist");
1018             return null;
1019         }
1020 
1021         Path satelliteAccessControlFileDir = satelliteAccessControlDataDir.toPath();
1022         Path configUpdaterSatelliteConfigFilePath = satelliteAccessControlFileDir.resolve(fileName);
1023         File configUpdaterSatelliteConfigFile = configUpdaterSatelliteConfigFilePath.toFile();
1024         if (!configUpdaterSatelliteConfigFile.exists()) {
1025             ploge("getConfigUpdaterSatelliteConfigFileFromLocalDirectory: "
1026                     + "File: " + fileName + " is not exist");
1027             return null;
1028         }
1029         return configUpdaterSatelliteConfigFile;
1030     }
1031 
isS2CellFileValid(@onNull File s2CellFile)1032     private boolean isS2CellFileValid(@NonNull File s2CellFile) {
1033         try {
1034             SatelliteOnDeviceAccessController satelliteOnDeviceAccessController =
1035                     SatelliteOnDeviceAccessController.create(s2CellFile, mFeatureFlags);
1036             int s2Level = satelliteOnDeviceAccessController.getS2Level();
1037             if (s2Level < MIN_S2_LEVEL || s2Level > MAX_S2_LEVEL) {
1038                 ploge("isS2CellFileValid: invalid s2 level = " + s2Level);
1039                 satelliteOnDeviceAccessController.close();
1040                 return false;
1041             }
1042             satelliteOnDeviceAccessController.close();
1043         } catch (Exception ex) {
1044             ploge("isS2CellFileValid: Got exception in reading the file, ex=" + ex);
1045             return false;
1046         }
1047         return true;
1048     }
1049 
cleanUpCtsResources()1050     private void cleanUpCtsResources() {
1051         if (mCtsSatS2FilePath != null) {
1052             try {
1053                 Files.delete(mCtsSatS2FilePath);
1054             } catch (IOException ex) {
1055                 ploge("cleanUpCtsResources: ex=" + ex);
1056             }
1057         }
1058     }
1059 
cleanUpTelephonyConfigs()1060     private void cleanUpTelephonyConfigs() {
1061         mSatelliteController.cleanUpTelephonyConfigs();
1062     }
1063 
cleanUpSatelliteAccessConfigOtaResources()1064     private void cleanUpSatelliteAccessConfigOtaResources() {
1065         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
1066         File satelliteAccessControlDir =
1067                 phoneGlobals.getDir(SATELLITE_ACCESS_CONTROL_DATA_DIR, Context.MODE_PRIVATE);
1068         if (satelliteAccessControlDir == null || !satelliteAccessControlDir.exists()) {
1069             plogd(
1070                     "cleanUpSatelliteAccessConfigOtaResources: "
1071                             + SATELLITE_ACCESS_CONTROL_DATA_DIR
1072                             + " does not exist");
1073             return;
1074         }
1075         plogd(
1076                 "cleanUpSatelliteAccessConfigOtaResources: Deleting contents under "
1077                         + SATELLITE_ACCESS_CONTROL_DATA_DIR);
1078         FileUtils.deleteContents(satelliteAccessControlDir);
1079     }
1080 
cleanupSatelliteConfigOtaResources()1081     private void cleanupSatelliteConfigOtaResources() {
1082         SatelliteConfig satelliteConfig = mSatelliteController.getSatelliteConfig();
1083         if (satelliteConfig == null) {
1084             plogd(
1085                     "cleanupSatelliteConfigOtaResources: satelliteConfig is null. Cannot or Not"
1086                         + " needed to delete satellite config OTA files");
1087             return;
1088         }
1089         plogd("cleanupSatelliteConfigOtaResources: Deleting satellite config OTA files");
1090         satelliteConfig.cleanOtaResources(mContext);
1091     }
1092 
1093     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getElapsedRealtimeNanos()1094     protected long getElapsedRealtimeNanos() {
1095         return SystemClock.elapsedRealtimeNanos();
1096     }
1097 
1098     /**
1099      * @param countryCodes list of country code (two letters based on the ISO 3166-1).
1100      * @return {@code true} if the countryCode is valid {@code false} otherwise.
1101      */
isValidCountryCodes(@ullable List<String> countryCodes)1102     private boolean isValidCountryCodes(@Nullable List<String> countryCodes) {
1103         if (countryCodes == null || countryCodes.isEmpty()) {
1104             return false;
1105         }
1106         for (String countryCode : countryCodes) {
1107             if (!TelephonyUtils.isValidCountryCode(countryCode)) {
1108                 ploge("invalid country code : " + countryCode);
1109                 return false;
1110             }
1111         }
1112         return true;
1113     }
1114 
updateSharedPreferencesCountryCodes( @onNull Context context, @NonNull List<String> value)1115     private boolean updateSharedPreferencesCountryCodes(
1116             @NonNull Context context, @NonNull List<String> value) {
1117         if (mSharedPreferences == null) {
1118             plogd("updateSharedPreferencesCountryCodes: mSharedPreferences is null");
1119             initSharedPreferences(context);
1120         }
1121         if (mSharedPreferences == null) {
1122             ploge("updateSharedPreferencesCountryCodes: mSharedPreferences is still null");
1123             return false;
1124         }
1125         try {
1126             mSharedPreferences.edit().putStringSet(
1127                     CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY, new HashSet<>(value)).apply();
1128             return true;
1129         } catch (Exception ex) {
1130             ploge("updateSharedPreferencesCountryCodes error : " + ex);
1131             return false;
1132         }
1133     }
1134 
deleteSharedPreferencesbyKey( @onNull Context context, @NonNull String key)1135     private void deleteSharedPreferencesbyKey(
1136             @NonNull Context context, @NonNull String key) {
1137         plogd("deletedeleteSharedPreferencesbyKey: " + key);
1138         if (mSharedPreferences == null) {
1139             plogd("deleteSharedPreferencesbyKey: mSharedPreferences is null");
1140             initSharedPreferences(context);
1141         }
1142         if (mSharedPreferences == null) {
1143             plogd("deleteSharedPreferencesbyKey: mSharedPreferences is still null");
1144             return;
1145         }
1146         try {
1147             mSharedPreferences.edit().remove(key).apply();
1148         } catch (Exception ex) {
1149             ploge("deleteSharedPreferencesbyKey error : " + ex);
1150         }
1151     }
1152 
updateSharedPreferencesIsAllowAccessControl( @onNull Context context, boolean value)1153     private boolean updateSharedPreferencesIsAllowAccessControl(
1154             @NonNull Context context, boolean value) {
1155         if (mSharedPreferences == null) {
1156             plogd("updateSharedPreferencesIsAllowAccessControl: mSharedPreferences is null");
1157             initSharedPreferences(context);
1158         }
1159         if (mSharedPreferences == null) {
1160             ploge("updateSharedPreferencesIsAllowAccessControl: mSharedPreferences is null");
1161             return false;
1162         }
1163         try {
1164             mSharedPreferences.edit().putBoolean(
1165                     CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY,
1166                     value).apply();
1167             return true;
1168         } catch (Exception ex) {
1169             ploge("updateSharedPreferencesIsAllowAccessControl error: " + ex);
1170             return false;
1171         }
1172     }
1173 
updateSharedPreferencesSatelliteAccessConfigVersion( @onNull Context context, int version)1174     private boolean updateSharedPreferencesSatelliteAccessConfigVersion(
1175             @NonNull Context context, int version) {
1176         if (mSharedPreferences == null) {
1177             plogd("updateSharedPreferencesSatelliteAccessConfigVersion: "
1178                     + "mSharedPreferences is null");
1179             initSharedPreferences(context);
1180         }
1181         if (mSharedPreferences == null) {
1182             ploge("updateSharedPreferencesSatelliteAccessConfigVersion: "
1183                     + "mSharedPreferences is null");
1184             return false;
1185         }
1186         try {
1187             mSharedPreferences.edit().putInt(CONFIG_UPDATER_SATELLITE_VERSION_KEY, version).apply();
1188             return true;
1189         } catch (Exception ex) {
1190             ploge("updateSharedPreferencesSatelliteAccessConfigVersion error: " + ex);
1191             return false;
1192         }
1193     }
1194 
persistLatestSatelliteCommunicationAllowedState()1195     private void persistLatestSatelliteCommunicationAllowedState() {
1196         if (mSharedPreferences == null) {
1197             ploge("persistLatestSatelliteCommunicationAllowedState: mSharedPreferences is null");
1198             return;
1199         }
1200 
1201         try {
1202             mSharedPreferences.edit().putLong(LATEST_SATELLITE_COMMUNICATION_ALLOWED_SET_TIME_KEY,
1203                     mLatestSatelliteCommunicationAllowedSetTime).apply();
1204             mSharedPreferences.edit().putBoolean(LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY,
1205                     mLatestSatelliteCommunicationAllowed).apply();
1206         } catch (Exception ex) {
1207             ploge("persistLatestSatelliteCommunicationAllowedState error : " + ex);
1208         }
1209     }
1210 
1211     /**
1212      * Update satellite access config data when ConfigUpdater updates with the new config data.
1213      * - country codes, satellite allow access, sats2.dat, satellite_access_config.json
1214      */
updateSatelliteAccessDataWithConfigUpdaterData(Context context)1215     private void updateSatelliteAccessDataWithConfigUpdaterData(Context context) {
1216         plogd("updateSatelliteAccessDataWithConfigUpdaterData");
1217         SatelliteConfig satelliteConfig = mSatelliteController.getSatelliteConfig();
1218         if (satelliteConfig == null) {
1219             ploge("updateSatelliteAccessDataWithConfigUpdaterData: satelliteConfig is null");
1220             mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
1221                     SatelliteConstants.CONFIG_UPDATE_RESULT_NO_SATELLITE_DATA);
1222             return;
1223         }
1224 
1225         // satellite access config version
1226         int satelliteAccessConfigVersion = satelliteConfig.getSatelliteConfigDataVersion();
1227         if (satelliteAccessConfigVersion <= 0) {
1228             plogd("updateSatelliteAccessDataWithConfigUpdaterData: version is invalid: "
1229                     + satelliteAccessConfigVersion);
1230             return;
1231         }
1232 
1233         // validation check country code
1234         List<String> satelliteCountryCodes = satelliteConfig.getDeviceSatelliteCountryCodes();
1235         if (!isValidCountryCodes(satelliteCountryCodes)) {
1236             plogd("updateSatelliteAccessDataWithConfigUpdaterData: country codes is invalid");
1237             mConfigUpdaterMetricsStats.reportOemConfigError(
1238                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_COUNTRY_CODE);
1239             return;
1240         }
1241 
1242         // validation check allow region
1243         Boolean isSatelliteDataForAllowedRegion = satelliteConfig.isSatelliteDataForAllowedRegion();
1244         if (isSatelliteDataForAllowedRegion == null) {
1245             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1246                     + "Satellite isSatelliteDataForAllowedRegion is null ");
1247             mConfigUpdaterMetricsStats.reportOemConfigError(
1248                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE);
1249             return;
1250         }
1251 
1252         // validation check s2 cell file
1253         File configUpdaterS2CellFile = satelliteConfig.getSatelliteS2CellFile(context);
1254         if (configUpdaterS2CellFile == null || !configUpdaterS2CellFile.exists()) {
1255             plogd("updateSatelliteAccessDataWithConfigUpdaterData: "
1256                     + "configUpdaterS2CellFile is not exist");
1257             mConfigUpdaterMetricsStats.reportOemConfigError(
1258                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE);
1259             return;
1260         }
1261 
1262         if (!isS2CellFileValid(configUpdaterS2CellFile)) {
1263             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1264                     + "the configUpdaterS2CellFile is not valid");
1265             mConfigUpdaterMetricsStats.reportOemConfigError(
1266                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE);
1267             return;
1268         }
1269 
1270         // validation check satellite_access_config file
1271         File configUpdaterSatelliteAccessConfigJsonFile =
1272                 satelliteConfig.getSatelliteAccessConfigJsonFile(context);
1273         if (configUpdaterSatelliteAccessConfigJsonFile == null
1274                 || !configUpdaterSatelliteAccessConfigJsonFile.exists()) {
1275             plogd("updateSatelliteAccessDataWithConfigUpdaterData: "
1276                     + "satellite_access_config.json does not exist");
1277             mConfigUpdaterMetricsStats.reportOemConfigError(SatelliteConstants
1278                             .CONFIG_UPDATE_RESULT_INVALID_SATELLITE_ACCESS_CONFIG_FILE);
1279             return;
1280         }
1281 
1282         try {
1283             if (SatelliteAccessConfigurationParser.parse(
1284                     configUpdaterSatelliteAccessConfigJsonFile.getAbsolutePath()) == null) {
1285                 ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1286                         + "the satellite_access_config.json is not valid");
1287                 mConfigUpdaterMetricsStats.reportOemConfigError(SatelliteConstants
1288                         .CONFIG_UPDATE_RESULT_INVALID_SATELLITE_ACCESS_CONFIG_FILE);
1289                 return;
1290             }
1291         } catch (Exception e) {
1292             loge("updateSatelliteAccessDataWithConfigUpdaterData: "
1293                     + "the satellite_access_config.json parse error " + e);
1294         }
1295 
1296         // copy s2 cell data into the phone internal directory
1297         File localS2CellFile = copyFileToLocalDirectory(
1298                 configUpdaterS2CellFile, CONFIG_UPDATER_S2_CELL_FILE_NAME);
1299         if (localS2CellFile == null) {
1300             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1301                     + "fail to copy localS2CellFile");
1302             mConfigUpdaterMetricsStats.reportOemConfigError(
1303                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
1304             return;
1305         }
1306 
1307         // copy satellite_access_config file into the phone internal directory
1308         File localSatelliteAccessConfigFile = copyFileToLocalDirectory(
1309                 configUpdaterSatelliteAccessConfigJsonFile,
1310                 CONFIG_UPDATER_SATELLITE_ACCESS_CONFIG_FILE_NAME);
1311 
1312         if (localSatelliteAccessConfigFile == null) {
1313             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1314                     + "fail to copy localSatelliteAccessConfigFile");
1315             mConfigUpdaterMetricsStats.reportOemConfigError(
1316                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
1317             localS2CellFile.delete();
1318             return;
1319         }
1320 
1321         // copy country codes into the shared preferences of phoen
1322         if (!updateSharedPreferencesCountryCodes(context, satelliteCountryCodes)) {
1323             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1324                     + "fail to copy country coeds into shared preferences");
1325             localS2CellFile.delete();
1326             localSatelliteAccessConfigFile.delete();
1327             mConfigUpdaterMetricsStats.reportOemConfigError(
1328                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
1329             return;
1330         }
1331 
1332         // copy allow access into the shared preferences of phone
1333         if (!updateSharedPreferencesIsAllowAccessControl(
1334                 context, isSatelliteDataForAllowedRegion.booleanValue())) {
1335             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1336                     + "fail to copy isSatelliteDataForAllowedRegion"
1337                     + " into shared preferences");
1338             localS2CellFile.delete();
1339             localSatelliteAccessConfigFile.delete();
1340             deleteSharedPreferencesbyKey(
1341                     context, CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY);
1342             mConfigUpdaterMetricsStats.reportOemConfigError(
1343                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
1344             return;
1345         }
1346 
1347         // copy version of satellite access config into the shared preferences of phone
1348         if (!updateSharedPreferencesSatelliteAccessConfigVersion(
1349                 context, satelliteAccessConfigVersion)) {
1350             ploge("updateSatelliteAccessDataWithConfigUpdaterData: "
1351                     + "fail to copy satelliteAccessConfigVersion"
1352                     + " into shared preferences");
1353             localS2CellFile.delete();
1354             localSatelliteAccessConfigFile.delete();
1355             deleteSharedPreferencesbyKey(
1356                     context, CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY);
1357             deleteSharedPreferencesbyKey(
1358                     context, CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY);
1359             mConfigUpdaterMetricsStats.reportOemConfigError(
1360                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
1361             return;
1362         }
1363 
1364         mSatelliteAccessConfigVersion = satelliteAccessConfigVersion;
1365         mSatelliteS2CellFile = localS2CellFile;
1366         mSatelliteAccessConfigFile = localSatelliteAccessConfigFile;
1367         mSatelliteCountryCodes = satelliteCountryCodes;
1368         mIsSatelliteAllowAccessControl = satelliteConfig.isSatelliteDataForAllowedRegion();
1369         plogd("mSatelliteAccessConfigVersion=" + mSatelliteAccessConfigVersion
1370                 + ", Use s2 cell file=" + mSatelliteS2CellFile.getAbsolutePath()
1371                 + ", mSatelliteAccessConfigFile=" + mSatelliteAccessConfigFile.getAbsolutePath()
1372                 + ", country codes=" + String.join(",", mSatelliteCountryCodes)
1373                 + ", mIsSatelliteAllowAccessControl=" + mIsSatelliteAllowAccessControl
1374                 + " from ConfigUpdater");
1375 
1376         // Clean up resources so that the new config data will be used when serving new requests
1377         cleanupOnDeviceAccessControllerResources();
1378 
1379         // Clean up cached data based on previous geofence data
1380         synchronized (mLock) {
1381             plogd("clear mCachedAccessRestrictionMap");
1382             mCachedAccessRestrictionMap.clear();
1383         }
1384 
1385         mConfigUpdaterMetricsStats.reportConfigUpdateSuccess();
1386         // We need to re-evaluate if satellite is allowed at the current location and if
1387         // satellite access configuration has changed with the config data received from config
1388         // server, and then notify listeners accordingly.
1389         sendRequestAsync(EVENT_CONFIG_DATA_UPDATED, null);
1390     }
1391 
1392     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
loadOverlayConfigs(@onNull Context context)1393     protected void loadOverlayConfigs(@NonNull Context context) {
1394         plogd("loadOverlayConfigs");
1395         mSatelliteCountryCodes = getSatelliteCountryCodesFromOverlayConfig(context);
1396         mIsSatelliteAllowAccessControl = getSatelliteAccessAllowFromOverlayConfig(context);
1397         String satelliteS2CellFileName = getSatelliteS2CellFileFromOverlayConfig(context);
1398         mSatelliteS2CellFile = TextUtils.isEmpty(satelliteS2CellFileName)
1399                 ? null : new File(satelliteS2CellFileName);
1400         if (mSatelliteS2CellFile != null && !mSatelliteS2CellFile.exists()) {
1401             ploge("The satellite S2 cell file " + satelliteS2CellFileName + " does not exist");
1402             mSatelliteS2CellFile = null;
1403         }
1404 
1405         String satelliteAccessConfigFileName =
1406                 getSatelliteConfigurationFileNameFromOverlayConfig(context);
1407         mSatelliteAccessConfigFile = TextUtils.isEmpty(satelliteAccessConfigFileName)
1408                 ? null : new File(satelliteAccessConfigFileName);
1409         if (mSatelliteAccessConfigFile != null && !mSatelliteAccessConfigFile.exists()) {
1410             ploge("The satellite access config file " + satelliteAccessConfigFileName
1411                     + " does not exist");
1412             mSatelliteAccessConfigFile = null;
1413         }
1414 
1415         mLocationFreshDurationNanos = getSatelliteLocationFreshDurationFromOverlayConfig(context);
1416         mAccessControllerMetricsStats.setConfigDataSource(
1417                 SatelliteConstants.CONFIG_DATA_SOURCE_DEVICE_CONFIG);
1418         mRetryIntervalToEvaluateUserInSatelliteAllowedRegion =
1419                 getDelayBeforeRetryValidatingPossibleChangeInSatelliteAllowedRegionMillis(context);
1420         mMaxRetryCountForValidatingPossibleChangeInAllowedRegion =
1421                 getMaxRetryCountForValidatingPossibleChangeInAllowedRegion(context);
1422         mLocationQueryThrottleIntervalNanos = getLocationQueryThrottleIntervalNanos(context);
1423     }
1424 
loadSatelliteAccessConfiguration()1425     protected void loadSatelliteAccessConfiguration() {
1426         logd("loadSatelliteAccessConfiguration");
1427         String satelliteConfigurationFileName;
1428         File satelliteAccessConfigFile = getSatelliteAccessConfigFile();
1429         synchronized (mLock) {
1430             if (satelliteAccessConfigFile != null) {
1431                 satelliteConfigurationFileName = satelliteAccessConfigFile.getAbsolutePath();
1432             } else {
1433                 logd("loadSatelliteAccessConfiguration:");
1434                 satelliteConfigurationFileName = getSatelliteConfigurationFileNameFromOverlayConfig(
1435                         mContext);
1436             }
1437         }
1438 
1439         loadSatelliteAccessConfigurationFileToMap(satelliteConfigurationFileName);
1440     }
1441 
loadSatelliteAccessConfigurationFileToMap(String fileName)1442     protected void loadSatelliteAccessConfigurationFileToMap(String fileName) {
1443         logd("loadSatelliteAccessConfigurationFileToMap: " + fileName);
1444         if (!TextUtils.isEmpty(fileName)) {
1445             try {
1446                 synchronized (mLock) {
1447                     mSatelliteAccessConfigMap =
1448                             SatelliteAccessConfigurationParser.parse(fileName);
1449                 }
1450             } catch (Exception e) {
1451                 loge("loadSatelliteAccessConfigurationFileToMap: failed load json file: " + e);
1452             }
1453         } else {
1454             loge("loadSatelliteAccessConfigurationFileToMap: fileName is empty");
1455         }
1456     }
1457 
loadConfigUpdaterConfigs()1458     private void loadConfigUpdaterConfigs() {
1459         plogd("loadConfigUpdaterConfigs");
1460         if (mSharedPreferences == null) {
1461             ploge("loadConfigUpdaterConfigs : mSharedPreferences is null");
1462             return;
1463         }
1464 
1465         int satelliteConfigVersion = mSharedPreferences.getInt(
1466                 CONFIG_UPDATER_SATELLITE_VERSION_KEY, 0);
1467         if (satelliteConfigVersion <= 0) {
1468             ploge("loadConfigUpdaterConfigs: satelliteConfigVersion is invalid: "
1469                     + satelliteConfigVersion);
1470             return;
1471         }
1472 
1473         Set<String> countryCodes =
1474                 mSharedPreferences.getStringSet(CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY, null);
1475 
1476         if (countryCodes == null || countryCodes.isEmpty()) {
1477             ploge("loadConfigUpdaterConfigs: configupdater country codes are either null or empty");
1478             return;
1479         }
1480 
1481         boolean isSatelliteAllowAccessControl =
1482                 mSharedPreferences.getBoolean(
1483                         CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY, true);
1484 
1485         File s2CellFile = getConfigUpdaterSatelliteConfigFileFromLocalDirectory(
1486                 CONFIG_UPDATER_S2_CELL_FILE_NAME);
1487         if (s2CellFile == null) {
1488             ploge("loadConfigUpdaterConfigs: s2CellFile is null");
1489             return;
1490         }
1491 
1492         File satelliteAccessConfigJsonFile = getConfigUpdaterSatelliteConfigFileFromLocalDirectory(
1493                 CONFIG_UPDATER_SATELLITE_ACCESS_CONFIG_FILE_NAME);
1494         if (satelliteAccessConfigJsonFile == null) {
1495             ploge("satelliteAccessConfigJsonFile is null");
1496             return;
1497         }
1498 
1499         mSatelliteAccessConfigVersion = satelliteConfigVersion;
1500         mSatelliteS2CellFile = s2CellFile;
1501         mSatelliteAccessConfigFile = satelliteAccessConfigJsonFile;
1502         mSatelliteCountryCodes = countryCodes.stream().collect(Collectors.toList());
1503         mIsSatelliteAllowAccessControl = isSatelliteAllowAccessControl;
1504         plogd("loadConfigUpdaterConfigs: use satellite config data from configupdater: "
1505                 + " mSatelliteAccessConfigVersion=" + mSatelliteAccessConfigVersion
1506                 + ", Use s2 cell file=" + mSatelliteS2CellFile.getAbsolutePath()
1507                 + ", mSatelliteAccessConfigFile=" + mSatelliteAccessConfigFile.getAbsolutePath()
1508                 + ", country codes=" + String.join(",", mSatelliteCountryCodes)
1509                 + ", mIsSatelliteAllowAccessControl=" + mIsSatelliteAllowAccessControl
1510                 + " from ConfigUpdater");
1511         mAccessControllerMetricsStats.setConfigDataSource(
1512                 SatelliteConstants.CONFIG_DATA_SOURCE_CONFIG_UPDATER);
1513     }
1514 
loadCachedLatestSatelliteCommunicationAllowedState()1515     private void loadCachedLatestSatelliteCommunicationAllowedState() {
1516         if (mSharedPreferences == null) {
1517             ploge("loadCachedLatestSatelliteCommunicationAllowedState: mSharedPreferences is null");
1518             return;
1519         }
1520 
1521         try {
1522             mLatestSatelliteCommunicationAllowedSetTime =
1523                     mSharedPreferences.getLong(LATEST_SATELLITE_COMMUNICATION_ALLOWED_SET_TIME_KEY,
1524                             0);
1525             mLatestSatelliteCommunicationAllowed =
1526                     mSharedPreferences.getBoolean(LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY,
1527                             false);
1528         } catch (Exception ex) {
1529             ploge("loadCachedLatestSatelliteCommunicationAllowedState: ex=" + ex);
1530         }
1531         plogd("mLatestSatelliteCommunicationAllowedSetTime="
1532                 + mLatestSatelliteCommunicationAllowedSetTime
1533                 + ", mLatestSatelliteCommunicationAllowed=" + mLatestSatelliteCommunicationAllowed);
1534     }
1535 
getLocationFreshDurationNanos()1536     private long getLocationFreshDurationNanos() {
1537         synchronized (mLock) {
1538             if (mIsOverlayConfigOverridden) {
1539                 return mOverriddenLocationFreshDurationNanos;
1540             }
1541             return mLocationFreshDurationNanos;
1542         }
1543     }
1544 
1545     /**
1546      * Returns a list of satellite country codes.
1547      *
1548      * @return The list of satellite country codes.
1549      */
1550     @NonNull
getSatelliteCountryCodes()1551     public List<String> getSatelliteCountryCodes() {
1552         synchronized (mLock) {
1553             if (mIsOverlayConfigOverridden) {
1554                 return mOverriddenSatelliteCountryCodes;
1555             }
1556             return mSatelliteCountryCodes;
1557         }
1558     }
1559 
1560     /**
1561      * Returns a satellite s2 cell file
1562      *
1563      * @return The file of satellite s2 cell
1564      */
1565     @Nullable
getSatelliteS2CellFile()1566     public File getSatelliteS2CellFile() {
1567         synchronized (mLock) {
1568             if (mIsOverlayConfigOverridden) {
1569                 return mOverriddenSatelliteS2CellFile;
1570             }
1571             return mSatelliteS2CellFile;
1572         }
1573     }
1574 
1575     /**
1576      * Returns a satellite access config file
1577      *
1578      * @return The file of satellite access config
1579      */
1580     @Nullable
getSatelliteAccessConfigFile()1581     public File getSatelliteAccessConfigFile() {
1582         synchronized (mLock) {
1583             if (mIsOverlayConfigOverridden) {
1584                 logd("mIsOverlayConfigOverridden: " + mIsOverlayConfigOverridden);
1585                 return mOverriddenSatelliteAccessConfigFile;
1586             }
1587             if (mSatelliteAccessConfigFile != null) {
1588                 logd("getSatelliteAccessConfigFile path: "
1589                         + mSatelliteAccessConfigFile.getAbsoluteFile());
1590             }
1591             return mSatelliteAccessConfigFile;
1592         }
1593     }
1594 
1595     /**
1596      * Checks if satellite access control is allowed.
1597      *
1598      * @return {@code true} if satellite access control is allowed, {@code false} otherwise.
1599      */
isSatelliteAllowAccessControl()1600     public boolean isSatelliteAllowAccessControl() {
1601         synchronized (mLock) {
1602             if (mIsOverlayConfigOverridden) {
1603                 return mOverriddenIsSatelliteAllowAccessControl;
1604             }
1605             return mIsSatelliteAllowAccessControl;
1606         }
1607     }
1608 
handleCmdIsSatelliteAllowedForCurrentLocation( @onNull Pair<Integer, ResultReceiver> requestArguments)1609     private void handleCmdIsSatelliteAllowedForCurrentLocation(
1610             @NonNull Pair<Integer, ResultReceiver> requestArguments) {
1611         synchronized (mLock) {
1612             mSatelliteAllowResultReceivers.add(requestArguments.second);
1613             if (mSatelliteAllowResultReceivers.size() > 1) {
1614                 plogd("requestIsCommunicationAllowedForCurrentLocation is already being "
1615                         + "processed");
1616                 return;
1617             }
1618             mTotalCheckingStartTimeMillis = System.currentTimeMillis();
1619             mSatelliteController.requestIsSatelliteSupported(
1620                     mInternalSatelliteSupportedResultReceiver);
1621         }
1622     }
1623 
handleWaitForCurrentLocationTimedOutEvent()1624     private void handleWaitForCurrentLocationTimedOutEvent() {
1625         plogd("Timed out to wait for current location");
1626         synchronized (mLock) {
1627             if (mLocationRequestCancellationSignal != null) {
1628                 mLocationRequestCancellationSignal.cancel();
1629                 mLocationRequestCancellationSignal = null;
1630                 onCurrentLocationAvailable(null);
1631             } else {
1632                 ploge("handleWaitForCurrentLocationTimedOutEvent: "
1633                         + "mLocationRequestCancellationSignal is null");
1634             }
1635         }
1636     }
1637 
registerDefaultSmsAppChangedBroadcastReceiver(Context context)1638     private void registerDefaultSmsAppChangedBroadcastReceiver(Context context) {
1639         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
1640             plogd("registerDefaultSmsAppChangedBroadcastReceiver: Flag "
1641                     + "carrierRoamingNbIotNtn is disabled");
1642             return;
1643         }
1644         IntentFilter intentFilter = new IntentFilter();
1645         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1646         intentFilter.addDataScheme("package");
1647         context.registerReceiver(mDefaultSmsAppChangedBroadcastReceiver, intentFilter);
1648     }
1649 
registerLocationModeChangedBroadcastReceiver(Context context)1650     private void registerLocationModeChangedBroadcastReceiver(Context context) {
1651         IntentFilter intentFilter = new IntentFilter();
1652         intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
1653         intentFilter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
1654         context.registerReceiver(mLocationModeChangedBroadcastReceiver, intentFilter);
1655     }
1656 
1657     /**
1658      * At country borders, a multi-SIM device might connect to multiple cellular base
1659      * stations and thus might have multiple different MCCs.
1660      * In such cases, framework is not sure whether the region should be disallowed or not,
1661      * and thus the geofence data will be used to decide whether to allow satellite.
1662      */
isRegionDisallowed(List<String> networkCountryIsoList)1663     private boolean isRegionDisallowed(List<String> networkCountryIsoList) {
1664         if (networkCountryIsoList.isEmpty()) {
1665             plogd("isRegionDisallowed : false : network country code is not available");
1666             return false;
1667         }
1668 
1669         for (String countryCode : networkCountryIsoList) {
1670             if (isSatelliteAccessAllowedForLocation(List.of(countryCode))) {
1671                 plogd("isRegionDisallowed : false : Country Code " + countryCode
1672                         + " is allowed but not sure if current location should be allowed.");
1673                 return false;
1674             }
1675         }
1676 
1677         plogd("isRegionDisallowed : true : " + networkCountryIsoList);
1678         return true;
1679     }
1680 
1681     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
handleIsSatelliteSupportedResult(int resultCode, Bundle resultData)1682     protected void handleIsSatelliteSupportedResult(int resultCode, Bundle resultData) {
1683         plogd("handleIsSatelliteSupportedResult: resultCode=" + resultCode);
1684         synchronized (mLock) {
1685             if (resultCode == SATELLITE_RESULT_SUCCESS) {
1686                 if (resultData.containsKey(KEY_SATELLITE_SUPPORTED)) {
1687                     boolean isSatelliteSupported = resultData.getBoolean(KEY_SATELLITE_SUPPORTED);
1688                     if (!isSatelliteSupported) {
1689                         plogd("Satellite is not supported");
1690                         Bundle bundle = new Bundle();
1691                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
1692                                 false);
1693                         sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_NOT_SUPPORTED, bundle,
1694                                 false);
1695                     } else {
1696                         plogd("Satellite is supported");
1697                         List<String> networkCountryIsoList =
1698                                 mCountryDetector.getCurrentNetworkCountryIso();
1699                         if (isRegionDisallowed(networkCountryIsoList)) {
1700                             Bundle bundle = new Bundle();
1701                             bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
1702                             mAccessControllerMetricsStats.setAccessControlType(SatelliteConstants
1703                                             .ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE)
1704                                     .setCountryCodes(networkCountryIsoList);
1705                             sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
1706                                     false);
1707                         } else {
1708                             checkSatelliteAccessRestrictionUsingGPS();
1709                         }
1710                     }
1711                 } else {
1712                     ploge("KEY_SATELLITE_SUPPORTED does not exist.");
1713                     sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
1714                 }
1715             } else {
1716                 sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
1717             }
1718         }
1719     }
1720 
handleIsSatelliteProvisionedResult(int resultCode, Bundle resultData)1721     private void handleIsSatelliteProvisionedResult(int resultCode, Bundle resultData) {
1722         plogd("handleIsSatelliteProvisionedResult: resultCode=" + resultCode);
1723         synchronized (mLock) {
1724             if (resultCode == SATELLITE_RESULT_SUCCESS) {
1725                 if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
1726                     boolean isSatelliteProvisioned =
1727                             resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
1728                     if (!isSatelliteProvisioned) {
1729                         plogd("Satellite is not provisioned");
1730                         Bundle bundle = new Bundle();
1731                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
1732                                 false);
1733                         sendSatelliteAllowResultToReceivers(resultCode, bundle, false);
1734                     } else {
1735                         plogd("Satellite is provisioned");
1736                         checkSatelliteAccessRestrictionUsingGPS();
1737                     }
1738                 } else {
1739                     ploge("KEY_SATELLITE_PROVISIONED does not exist.");
1740                     sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
1741                 }
1742             } else {
1743                 sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
1744             }
1745         }
1746     }
1747 
sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData, boolean allowed)1748     private void sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData,
1749             boolean allowed) {
1750         plogd("sendSatelliteAllowResultToReceivers : resultCode is " + resultCode);
1751         switch(resultCode) {
1752             case SATELLITE_RESULT_SUCCESS:
1753                 updateCurrentSatelliteAllowedState(allowed);
1754                 mIsCurrentLocationEligibleForNotification = true;
1755                 break;
1756 
1757             case SATELLITE_RESULT_LOCATION_DISABLED:
1758                 updateCurrentSatelliteAllowedState(allowed);
1759                 break;
1760             default:
1761                 break;
1762         }
1763 
1764         synchronized (mLock) {
1765             for (ResultReceiver resultReceiver : mSatelliteAllowResultReceivers) {
1766                 resultReceiver.send(resultCode, resultData);
1767                 mSatelliteController.decrementResultReceiverCount(
1768                         "SAC:requestIsCommunicationAllowedForCurrentLocation");
1769             }
1770             mSatelliteAllowResultReceivers.clear();
1771         }
1772         if (!shouldRetryValidatingPossibleChangeInAllowedRegion(resultCode)) {
1773             setIsSatelliteAllowedRegionPossiblyChanged(false);
1774         }
1775         Integer disallowedReason = getDisallowedReason(resultCode, allowed);
1776         boolean isChanged = false;
1777         if (disallowedReason != SATELLITE_DISALLOWED_REASON_NONE) {
1778             if (!isReasonPresentInSatelliteDisallowedReasons(disallowedReason)) {
1779                 isChanged = true;
1780             }
1781         } else {
1782             if (isSatelliteDisallowedReasonsEmpty()) {
1783                 if (!hasAlreadyNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN)) {
1784                     isChanged = true;
1785                 }
1786             }
1787             if (isReasonPresentInSatelliteDisallowedReasons(
1788                     SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION)
1789                     || isReasonPresentInSatelliteDisallowedReasons(
1790                     SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED)) {
1791                 isChanged = true;
1792             }
1793         }
1794         removeAllReasonsFromSatelliteDisallowedReasons(DISALLOWED_REASONS_TO_BE_RESET);
1795         if (disallowedReason != SATELLITE_DISALLOWED_REASON_NONE) {
1796             addReasonToSatelliteDisallowedReasons(disallowedReason);
1797         }
1798         if (isChanged) {
1799             handleEventDisallowedReasonsChanged();
1800         }
1801         synchronized (mIsAllowedCheckBeforeEnablingSatelliteLock) {
1802             mIsAllowedCheckBeforeEnablingSatellite = false;
1803         }
1804         reportMetrics(resultCode, allowed);
1805     }
1806 
getDisallowedReason(int resultCode, boolean allowed)1807     private int getDisallowedReason(int resultCode, boolean allowed) {
1808         if (resultCode == SATELLITE_RESULT_SUCCESS) {
1809             if (!allowed) {
1810                 return SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION;
1811             }
1812         } else if (resultCode == SATELLITE_RESULT_LOCATION_DISABLED) {
1813             return SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED;
1814         }
1815         return SATELLITE_DISALLOWED_REASON_NONE;
1816     }
1817 
handleEventDisallowedReasonsChanged()1818     private void handleEventDisallowedReasonsChanged() {
1819         if (mNotificationManager == null) {
1820             logd("showSatelliteSystemNotification: NotificationManager is null");
1821             return;
1822         }
1823 
1824         List<Integer> satelliteDisallowedReasons = getSatelliteDisallowedReasonsCopy();
1825         plogd("getSatelliteDisallowedReasons: satelliteDisallowedReasons:"
1826                 + String.join(", ", satelliteDisallowedReasons.toString()));
1827 
1828         notifySatelliteDisallowedReasonsChanged();
1829         if (mSatelliteController.isSatelliteSystemNotificationsEnabled(
1830                 CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL)
1831                 && mIsCurrentLocationEligibleForNotification
1832                 && mIsProvisionEligibleForNotification) {
1833             showSatelliteSystemNotification();
1834         } else {
1835             logd("mSatelliteDisallowedReasons:"
1836                     + " CurrentLocationAvailable: " + mIsCurrentLocationEligibleForNotification
1837                     + " SatelliteProvision: " + mIsProvisionEligibleForNotification);
1838             // If subId does not support satellite, remove the notification currently shown.
1839             if (hasAlreadyNotified(KEY_UNAVAILABLE_NOTIFICATION_SHOWN)) {
1840                 mNotificationManager.cancel(UNAVAILABLE_NOTIFICATION_TAG, NOTIFICATION_ID);
1841             }
1842             if (hasAlreadyNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN)) {
1843                 mNotificationManager.cancel(AVAILABLE_NOTIFICATION_TAG, NOTIFICATION_ID);
1844             }
1845         }
1846     }
1847 
showSatelliteSystemNotification()1848     private void showSatelliteSystemNotification() {
1849         if (mNotificationManager == null) {
1850             logd("showSatelliteSystemNotification: NotificationManager is null");
1851             return;
1852         }
1853 
1854         if (isSatelliteDisallowedReasonsEmpty()) {
1855             mNotificationManager.cancel(UNAVAILABLE_NOTIFICATION_TAG, NOTIFICATION_ID);
1856             if (!hasAlreadyNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN)) {
1857                 mNotificationManager.notifyAsUser(
1858                         AVAILABLE_NOTIFICATION_TAG,
1859                         NOTIFICATION_ID,
1860                         mSatelliteAvailableNotification,
1861                         UserHandle.ALL
1862                 );
1863                 markAsNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN, true);
1864                 markAsNotified(KEY_UNAVAILABLE_NOTIFICATION_SHOWN, false);
1865                 logd("showSatelliteSystemNotification: Notification is shown "
1866                         + KEY_AVAILABLE_NOTIFICATION_SHOWN);
1867             } else {
1868                 logd("showSatelliteSystemNotification: Notification is not shown "
1869                         + KEY_AVAILABLE_NOTIFICATION_SHOWN + " = "
1870                         + hasAlreadyNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN));
1871             }
1872         } else {
1873             mNotificationManager.cancel(AVAILABLE_NOTIFICATION_TAG, NOTIFICATION_ID);
1874             for (Integer reason : mSatelliteDisallowedReasons) {
1875                 if (!hasAlreadyNotified(KEY_UNAVAILABLE_NOTIFICATION_SHOWN)) {
1876                     mNotificationManager.notifyAsUser(
1877                             UNAVAILABLE_NOTIFICATION_TAG,
1878                             NOTIFICATION_ID,
1879                             mSatelliteUnAvailableNotifications.get(reason),
1880                             UserHandle.ALL
1881                     );
1882                     markAsNotified(KEY_UNAVAILABLE_NOTIFICATION_SHOWN, true);
1883                     markAsNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN, false);
1884                     logd("showSatelliteSystemNotification: Notification is shown "
1885                             + KEY_UNAVAILABLE_NOTIFICATION_SHOWN);
1886                     break;
1887                 } else {
1888                     logd("showSatelliteSystemNotification: Notification is not shown "
1889                             + KEY_UNAVAILABLE_NOTIFICATION_SHOWN);
1890                 }
1891             }
1892         }
1893     }
1894 
hasAlreadyNotified(String key)1895     private boolean hasAlreadyNotified(String key) {
1896         return mSharedPreferences.getBoolean(key, false);
1897     }
1898 
markAsNotified(String key, boolean notified)1899     private void markAsNotified(String key, boolean notified) {
1900         mSharedPreferences.edit().putBoolean(key, notified).apply();
1901     }
1902 
checkSharedPreference()1903     private void checkSharedPreference() {
1904         String[] keys = {
1905                 CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY,
1906                 LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY,
1907                 KEY_AVAILABLE_NOTIFICATION_SHOWN,
1908                 KEY_UNAVAILABLE_NOTIFICATION_SHOWN
1909         };
1910         // An Exception may occur if the initial value is set to HashSet while attempting to obtain
1911         // a boolean value. If an exception occurs, the SharedPreferences will be removed with Keys.
1912         Arrays.stream(keys).forEach(key -> {
1913             try {
1914                 mSharedPreferences.getBoolean(key, false);
1915             } catch (ClassCastException e) {
1916                 mSharedPreferences.edit().remove(key).apply();
1917             }
1918         });
1919     }
1920 
1921     /**
1922      * Telephony-internal logic to verify if satellite access is restricted at the current
1923      * location.
1924      */
checkSatelliteAccessRestrictionForCurrentLocation()1925     private void checkSatelliteAccessRestrictionForCurrentLocation() {
1926         synchronized (mLock) {
1927             List<String> networkCountryIsoList = mCountryDetector.getCurrentNetworkCountryIso();
1928             if (!networkCountryIsoList.isEmpty()) {
1929                 plogd("Use current network country codes=" + String.join(", ",
1930                         networkCountryIsoList));
1931 
1932                 boolean allowed = isSatelliteAccessAllowedForLocation(networkCountryIsoList);
1933                 Bundle bundle = new Bundle();
1934                 bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, allowed);
1935                 mAccessControllerMetricsStats
1936                         .setAccessControlType(
1937                                 SatelliteConstants.ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE)
1938                         .setCountryCodes(networkCountryIsoList);
1939                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, allowed);
1940             } else {
1941                 if (shouldUseOnDeviceAccessController()) {
1942                     // This will be an asynchronous check when it needs to wait for the current
1943                     // location from location service
1944                     checkSatelliteAccessRestrictionUsingOnDeviceData();
1945                 } else {
1946                     // This is always a synchronous check
1947                     checkSatelliteAccessRestrictionUsingCachedCountryCodes();
1948                 }
1949             }
1950         }
1951     }
1952 
shouldRetryValidatingPossibleChangeInAllowedRegion(int resultCode)1953     private boolean shouldRetryValidatingPossibleChangeInAllowedRegion(int resultCode) {
1954         return (resultCode == SATELLITE_RESULT_LOCATION_NOT_AVAILABLE);
1955     }
1956 
initializeHandlerForSatelliteAllowedResult()1957     private void initializeHandlerForSatelliteAllowedResult() {
1958         mHandlerForSatelliteAllowedResult = new ResultReceiver(null) {
1959             @Override
1960             protected void onReceiveResult(int resultCode, Bundle resultData) {
1961                 plogd("query satellite allowed for current "
1962                         + "location, resultCode=" + resultCode + ", resultData=" + resultData);
1963                 synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
1964                     if (shouldRetryValidatingPossibleChangeInAllowedRegion(resultCode)
1965                             && (mRetryCountForValidatingPossibleChangeInAllowedRegion
1966                             < mMaxRetryCountForValidatingPossibleChangeInAllowedRegion)) {
1967                         mRetryCountForValidatingPossibleChangeInAllowedRegion++;
1968                         plogd("mRetryCountForValidatingPossibleChangeInAllowedRegion is "
1969                                 + mRetryCountForValidatingPossibleChangeInAllowedRegion);
1970                         sendDelayedRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED,
1971                                 new Pair<>(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
1972                                         mHandlerForSatelliteAllowedResult),
1973                                 mRetryIntervalToEvaluateUserInSatelliteAllowedRegion);
1974                     } else {
1975                         mRetryCountForValidatingPossibleChangeInAllowedRegion = 0;
1976                         plogd("Stop retry validating the possible change in satellite allowed "
1977                                 + "region");
1978                     }
1979                 }
1980             }
1981         };
1982     }
1983 
initializeSatelliteSystemNotification(@onNull Context context)1984     private void initializeSatelliteSystemNotification(@NonNull Context context) {
1985         final NotificationChannel notificationChannel = new NotificationChannel(
1986                 NOTIFICATION_CHANNEL_ID,
1987                 NOTIFICATION_CHANNEL,
1988                 NotificationManager.IMPORTANCE_DEFAULT
1989         );
1990         notificationChannel.setSound(null, null);
1991         mNotificationManager = context.getSystemService(NotificationManager.class);
1992         if(mNotificationManager == null) {
1993             ploge("initializeSatelliteSystemNotification: notificationManager is null");
1994             return;
1995         }
1996         mNotificationManager.createNotificationChannel(notificationChannel);
1997 
1998         createAvailableNotifications(context);
1999         createUnavailableNotifications(context);
2000     }
2001 
createNotification(@onNull Context context, String title, String content)2002     private Notification createNotification(@NonNull Context context, String title,
2003             String content) {
2004         Notification.Builder notificationBuilder = new Notification.Builder(context)
2005                 .setContentTitle(title)
2006                 .setContentText(content)
2007                 .setSmallIcon(R.drawable.ic_android_satellite_24px)
2008                 .setChannelId(NOTIFICATION_CHANNEL_ID)
2009                 .setAutoCancel(true)
2010                 .setColor(context.getColor(
2011                         com.android.internal.R.color.system_notification_accent_color))
2012                 .setVisibility(Notification.VISIBILITY_PUBLIC)
2013                 .setLocalOnly(true);
2014 
2015         return notificationBuilder.build();
2016     }
2017 
createAvailableNotifications(Context context)2018     private void createAvailableNotifications(Context context) {
2019         int subId = mSatelliteController.getSelectedSatelliteSubId();
2020         int titleId;
2021         int summaryId;
2022 
2023         if (mSatelliteController.isSatelliteServiceSupportedByCarrier(
2024                 subId, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
2025             titleId = R.string.satellite_messaging_available_notification_title;
2026             summaryId = R.string.satellite_messaging_available_notification_summary;
2027         } else {
2028             titleId = R.string.satellite_sos_available_notification_title;
2029             summaryId = R.string.satellite_sos_available_notification_summary;
2030         }
2031 
2032         mSatelliteAvailableNotification = createNotification(
2033                 context,
2034                 context.getResources().getString(titleId),
2035                 context.getResources().getString(summaryId));
2036     }
2037 
createUnavailableNotifications(Context context)2038     private void createUnavailableNotifications(Context context) {
2039         int subId = mSatelliteController.getSelectedSatelliteSubId();
2040 
2041         HashMap<Integer, Pair<Integer, Integer>> unavailableReasons;
2042         if (mSatelliteController.isSatelliteServiceSupportedByCarrier(
2043                 subId, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
2044             unavailableReasons = SATELLITE_MESSAGING_UNAVAILABLE_REASONS;
2045         } else {
2046             unavailableReasons = SATELLITE_SOS_UNAVAILABLE_REASONS;
2047         }
2048 
2049         for (int reason : unavailableReasons.keySet()) {
2050             Pair<Integer, Integer> notificationString =
2051                     unavailableReasons.getOrDefault(reason, null);
2052             if (notificationString != null) {
2053                 mSatelliteUnAvailableNotifications.put(reason,
2054                         createNotification(
2055                                 context,
2056                                 context.getResources().getString(notificationString.first),
2057                                 context.getResources().getString(notificationString.second)));
2058             }
2059         }
2060     }
2061 
2062     private final BroadcastReceiver mDefaultSmsAppChangedBroadcastReceiver =
2063             new BroadcastReceiver() {
2064                 @Override
2065                 public void onReceive(Context context, Intent intent) {
2066                     if (intent.getAction()
2067                             .equals(Intent.ACTION_PACKAGE_CHANGED)) {
2068                         evaluatePossibleChangeInDefaultSmsApp(context);
2069                     }
2070                 }
2071             };
2072 
evaluatePossibleChangeInDefaultSmsApp(@onNull Context context)2073     private void evaluatePossibleChangeInDefaultSmsApp(@NonNull Context context) {
2074         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
2075             plogd("evaluatePossibleChangeInDefaultSmsApp: Flag "
2076                     + "carrierRoamingNbIotNtn is disabled");
2077             return;
2078         }
2079 
2080         boolean isDefaultMsgAppSupported = false;
2081         ComponentName componentName = SmsApplication.getDefaultSmsApplicationAsUser(
2082                         context, true, context.getUser());
2083         plogd("Current default SMS app:" + componentName);
2084         if (componentName != null) {
2085             String packageName = componentName.getPackageName();
2086             List<String> supportedMsgApps =
2087                     mSatelliteController.getSatelliteSupportedMsgApps(
2088                             mSatelliteController.getSelectedSatelliteSubId());
2089             plogd("supportedMsgApps:" + String.join(", ", supportedMsgApps));
2090             if (supportedMsgApps.contains(packageName)) {
2091                 isDefaultMsgAppSupported = true;
2092             }
2093         } else {
2094             plogd("No default SMS app");
2095         }
2096 
2097         if (isDefaultMsgAppSupported) {
2098             if (isReasonPresentInSatelliteDisallowedReasons(
2099                     SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP)) {
2100                 removeReasonFromSatelliteDisallowedReasons(
2101                         SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP);
2102                 handleEventDisallowedReasonsChanged();
2103             }
2104         } else {
2105             if (!isReasonPresentInSatelliteDisallowedReasons(
2106                     SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP)) {
2107                 addReasonToSatelliteDisallowedReasons(
2108                         SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP);
2109                 handleEventDisallowedReasonsChanged();
2110             }
2111         }
2112     }
2113 
handleSatelliteAllowedRegionPossiblyChanged(int handleEvent)2114     private void handleSatelliteAllowedRegionPossiblyChanged(int handleEvent) {
2115         synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
2116             logd("handleSatelliteAllowedRegionPossiblyChanged");
2117             setIsSatelliteAllowedRegionPossiblyChanged(true);
2118             requestIsCommunicationAllowedForCurrentLocation(
2119                     mHandlerForSatelliteAllowedResult, false);
2120             int triggeringEvent = TRIGGERING_EVENT_UNKNOWN;
2121             if (handleEvent == EVENT_LOCATION_SETTINGS_ENABLED) {
2122                 triggeringEvent = TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED;
2123             } else if (handleEvent == EVENT_COUNTRY_CODE_CHANGED) {
2124                 triggeringEvent = TRIGGERING_EVENT_MCC_CHANGED;
2125             } else if (handleEvent == EVENT_LOCATION_SETTINGS_DISABLED) {
2126                 triggeringEvent = TRIGGERING_EVENT_LOCATION_SETTINGS_DISABLED;
2127             } else if (handleEvent == EVENT_CONFIG_DATA_UPDATED) {
2128                 triggeringEvent = TRIGGERING_EVENT_CONFIG_DATA_UPDATED;
2129             }
2130             mAccessControllerMetricsStats.setTriggeringEvent(triggeringEvent);
2131         }
2132     }
2133 
allowLocationQueryForSatelliteAllowedCheck()2134     protected boolean allowLocationQueryForSatelliteAllowedCheck() {
2135         synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
2136             if (!isCommunicationAllowedCacheValid()) {
2137                 logd("allowLocationQueryForSatelliteAllowedCheck: cache is not valid");
2138                 return true;
2139             }
2140 
2141             if (isSatelliteAllowedRegionPossiblyChanged() && !isLocationQueryThrottled()) {
2142                 logd("allowLocationQueryForSatelliteAllowedCheck: location query is not throttled");
2143                 return true;
2144             }
2145         }
2146         logd("allowLocationQueryForSatelliteAllowedCheck: false");
2147         return false;
2148     }
2149 
isLocationQueryThrottled()2150     private boolean isLocationQueryThrottled() {
2151         if (mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos == 0) {
2152             plogv("isLocationQueryThrottled: "
2153                     + "mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos is 0, return "
2154                     + "false");
2155             return false;
2156         }
2157 
2158         long currentTime = getElapsedRealtimeNanos();
2159         if (currentTime - mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos
2160                 > mLocationQueryThrottleIntervalNanos) {
2161             plogv("isLocationQueryThrottled: currentTime - "
2162                     + "mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos is "
2163                     + "bigger than " + mLocationQueryThrottleIntervalNanos + " so return false");
2164             return false;
2165         }
2166 
2167         plogd("isLocationQueryThrottled : true");
2168         return true;
2169     }
2170 
2171     /**
2172      * Telephony-internal logic to verify if satellite access is restricted from the location query.
2173      */
2174     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
checkSatelliteAccessRestrictionUsingGPS()2175     public void checkSatelliteAccessRestrictionUsingGPS() {
2176         logv("checkSatelliteAccessRestrictionUsingGPS:");
2177         synchronized (mIsAllowedCheckBeforeEnablingSatelliteLock) {
2178             if (isInEmergency()) {
2179                 executeLocationQuery();
2180             } else {
2181                 if (mLocationManager.isLocationEnabled()) {
2182                     plogd("location query is allowed");
2183                     if (allowLocationQueryForSatelliteAllowedCheck()
2184                             || mIsAllowedCheckBeforeEnablingSatellite) {
2185                         executeLocationQuery();
2186                     } else {
2187                         Bundle bundle = new Bundle();
2188                         bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
2189                                 mLatestSatelliteCommunicationAllowed);
2190                         sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
2191                                 mLatestSatelliteCommunicationAllowed);
2192                     }
2193                 } else {
2194                     plogv("location query is not allowed");
2195                     Bundle bundle = new Bundle();
2196                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
2197                     sendSatelliteAllowResultToReceivers(
2198                             SATELLITE_RESULT_LOCATION_DISABLED, bundle, false);
2199                 }
2200             }
2201         }
2202     }
2203 
2204     /**
2205      * @return {@code true} if the latest query was executed within the predefined valid duration,
2206      * {@code false} otherwise.
2207      */
isCommunicationAllowedCacheValid()2208     private boolean isCommunicationAllowedCacheValid() {
2209         if (mLatestSatelliteCommunicationAllowedSetTime > 0) {
2210             long currentTime = getElapsedRealtimeNanos();
2211             if ((currentTime - mLatestSatelliteCommunicationAllowedSetTime)
2212                     <= ALLOWED_STATE_CACHE_VALID_DURATION_NANOS) {
2213                 logv("isCommunicationAllowedCacheValid: cache is valid");
2214                 return true;
2215             }
2216         }
2217         logv("isCommunicationAllowedCacheValid: cache is expired");
2218         return false;
2219     }
2220 
executeLocationQuery()2221     private void executeLocationQuery() {
2222         plogd("executeLocationQuery");
2223         synchronized (mLock) {
2224             mFreshLastKnownLocation = getFreshLastKnownLocation();
2225             checkSatelliteAccessRestrictionUsingOnDeviceData();
2226         }
2227     }
2228 
2229     /**
2230      * This function synchronously checks if satellite is allowed at current location using cached
2231      * country codes.
2232      */
checkSatelliteAccessRestrictionUsingCachedCountryCodes()2233     private void checkSatelliteAccessRestrictionUsingCachedCountryCodes() {
2234         Pair<String, Long> locationCountryCodeInfo =
2235                 mCountryDetector.getCachedLocationCountryIsoInfo();
2236         Map<String, Long> networkCountryCodeInfoMap =
2237                 mCountryDetector.getCachedNetworkCountryIsoInfo();
2238         List<String> countryCodeList;
2239 
2240         // Check if the cached location country code's timestamp is newer than all cached network
2241         // country codes
2242         if (!TextUtils.isEmpty(locationCountryCodeInfo.first) && isGreaterThanAll(
2243                 locationCountryCodeInfo.second, networkCountryCodeInfoMap.values())) {
2244             // Use cached location country code
2245             countryCodeList = Arrays.asList(locationCountryCodeInfo.first);
2246         } else {
2247             // Use cached network country codes
2248             countryCodeList = networkCountryCodeInfoMap.keySet().stream().toList();
2249         }
2250         plogd("Use cached country codes=" + String.join(", ", countryCodeList));
2251         mAccessControllerMetricsStats.setAccessControlType(
2252                 SatelliteConstants.ACCESS_CONTROL_TYPE_CACHED_COUNTRY_CODE);
2253 
2254         boolean allowed = isSatelliteAccessAllowedForLocation(countryCodeList);
2255         Bundle bundle = new Bundle();
2256         bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, allowed);
2257         sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, allowed);
2258     }
2259 
2260     /**
2261      * This function asynchronously checks if satellite is allowed at the current location using
2262      * on-device data. Asynchronous check happens when it needs to wait for the current location
2263      * from location service.
2264      */
checkSatelliteAccessRestrictionUsingOnDeviceData()2265     private void checkSatelliteAccessRestrictionUsingOnDeviceData() {
2266         mOnDeviceLookupStartTimeMillis = System.currentTimeMillis();
2267         synchronized (mLock) {
2268             plogd("Use on-device data");
2269             if (mFreshLastKnownLocation != null) {
2270                 mAccessControllerMetricsStats.setAccessControlType(
2271                         SatelliteConstants.ACCESS_CONTROL_TYPE_LAST_KNOWN_LOCATION);
2272                 checkSatelliteAccessRestrictionForLocation(mFreshLastKnownLocation);
2273                 mFreshLastKnownLocation = null;
2274             } else {
2275                 Location freshLastKnownLocation = getFreshLastKnownLocation();
2276                 if (freshLastKnownLocation != null) {
2277                     mAccessControllerMetricsStats.setAccessControlType(
2278                             SatelliteConstants.ACCESS_CONTROL_TYPE_LAST_KNOWN_LOCATION);
2279                     checkSatelliteAccessRestrictionForLocation(freshLastKnownLocation);
2280                 } else {
2281                     queryCurrentLocation();
2282                 }
2283             }
2284         }
2285     }
2286 
queryCurrentLocation()2287     private void queryCurrentLocation() {
2288         synchronized (mLock) {
2289             if (mLocationRequestCancellationSignal != null) {
2290                 plogd("queryCurrentLocation : "
2291                         + "Request for current location was already sent to LocationManager");
2292                 return;
2293             }
2294 
2295             synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
2296                 if (isSatelliteAllowedRegionPossiblyChanged()) {
2297                     mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos =
2298                             getElapsedRealtimeNanos();
2299                     plogd("mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos is set "
2300                             + mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
2301                 }
2302             }
2303 
2304             mLocationRequestCancellationSignal = new CancellationSignal();
2305             mLocationQueryStartTimeMillis = System.currentTimeMillis();
2306             mLocationManager.getCurrentLocation(LocationManager.FUSED_PROVIDER,
2307                     new LocationRequest.Builder(0)
2308                             .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
2309                             .setLocationSettingsIgnored(isInEmergency())
2310                             .build(),
2311                     mLocationRequestCancellationSignal, this::post,
2312                     this::onCurrentLocationAvailable);
2313             startWaitForCurrentLocationTimer();
2314         }
2315     }
2316 
onCurrentLocationAvailable(@ullable Location location)2317     private void onCurrentLocationAvailable(@Nullable Location location) {
2318         plogd("onCurrentLocationAvailable " + (location != null));
2319         synchronized (mLock) {
2320             stopWaitForCurrentLocationTimer();
2321             mLocationRequestCancellationSignal = null;
2322             mAccessControllerMetricsStats.setLocationQueryTime(mLocationQueryStartTimeMillis);
2323             Bundle bundle = new Bundle();
2324             if (location != null) {
2325                 plogd("onCurrentLocationAvailable: lat=" + Rlog.pii(TAG, location.getLatitude())
2326                         + ", long=" + Rlog.pii(TAG, location.getLongitude()));
2327                 if (location.isMock() && !isMockModemAllowed()) {
2328                     logd("location is mock");
2329                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
2330                     sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, false);
2331                     return;
2332                 }
2333                 mAccessControllerMetricsStats.setAccessControlType(
2334                         SatelliteConstants.ACCESS_CONTROL_TYPE_CURRENT_LOCATION);
2335                 mControllerMetricsStats.reportLocationQuerySuccessful(true);
2336                 checkSatelliteAccessRestrictionForLocation(location);
2337                 mIsCurrentLocationEligibleForNotification = true;
2338             } else {
2339                 plogd("current location is not available");
2340                 if (isCommunicationAllowedCacheValid()) {
2341                     plogd("onCurrentLocationAvailable: cache is still valid, using it");
2342                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
2343                             mLatestSatelliteCommunicationAllowed);
2344                     sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
2345                             mLatestSatelliteCommunicationAllowed);
2346                     mIsCurrentLocationEligibleForNotification = true;
2347                 } else {
2348                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
2349                     sendSatelliteAllowResultToReceivers(
2350                             SATELLITE_RESULT_LOCATION_NOT_AVAILABLE, bundle, false);
2351                 }
2352                 mControllerMetricsStats.reportLocationQuerySuccessful(false);
2353             }
2354         }
2355     }
2356 
checkSatelliteAccessRestrictionForLocation(@onNull Location location)2357     protected void checkSatelliteAccessRestrictionForLocation(@NonNull Location location) {
2358         synchronized (mLock) {
2359             try {
2360                 plogd(
2361                         "checkSatelliteAccessRestrictionForLocation: "
2362                                 + "checking satellite access restriction for location: lat - "
2363                                 + Rlog.pii(TAG, location.getLatitude())
2364                                 + ", long - "
2365                                 + Rlog.pii(TAG, location.getLongitude())
2366                                 + ", mS2Level - "
2367                                 + mS2Level);
2368                 SatelliteOnDeviceAccessController.LocationToken locationToken =
2369                         SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
2370                                 location.getLatitude(),
2371                                 location.getLongitude(), mS2Level);
2372                 boolean satelliteAllowed;
2373 
2374                 if (mCachedAccessRestrictionMap.containsKey(locationToken)) {
2375                     mNewRegionalConfigId = mCachedAccessRestrictionMap.get(locationToken);
2376                     satelliteAllowed = (mNewRegionalConfigId != null);
2377                     plogd("mNewRegionalConfigId from mCachedAccessRestrictionMap is "
2378                             + mNewRegionalConfigId);
2379                 } else {
2380                     if (!initSatelliteOnDeviceAccessController()) {
2381                         ploge("Failed to init SatelliteOnDeviceAccessController");
2382                         Bundle bundle = new Bundle();
2383                         bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
2384                         sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
2385                                 false);
2386                         return;
2387                     }
2388 
2389                     if (mFeatureFlags.carrierRoamingNbIotNtn()) {
2390                         synchronized (mLock) {
2391                             mNewRegionalConfigId = mSatelliteOnDeviceAccessController
2392                                     .getRegionalConfigIdForLocation(locationToken);
2393                             plogd(
2394                                     "mNewRegionalConfigId from geofence file lookup is "
2395                                             + mNewRegionalConfigId);
2396                             satelliteAllowed = (mNewRegionalConfigId != null);
2397                         }
2398                     } else {
2399                         plogd("checkSatelliteAccessRestrictionForLocation: "
2400                                 + "carrierRoamingNbIotNtn is disabled");
2401                         satelliteAllowed = mSatelliteOnDeviceAccessController
2402                                 .isSatCommunicationAllowedAtLocation(locationToken);
2403                         plogd(
2404                                 "checkSatelliteAccessRestrictionForLocation: satelliteAllowed from "
2405                                         + "geofence file lookup: "
2406                                         + satelliteAllowed);
2407                         mNewRegionalConfigId =
2408                                 satelliteAllowed ? UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID : null;
2409                     }
2410                     updateCachedAccessRestrictionMap(locationToken, mNewRegionalConfigId);
2411                 }
2412                 mAccessControllerMetricsStats.setOnDeviceLookupTime(mOnDeviceLookupStartTimeMillis);
2413                 plogd(
2414                         "checkSatelliteAccessRestrictionForLocation: "
2415                                 + (satelliteAllowed ? "Satellite Allowed" : "Satellite NOT Allowed")
2416                                 + " for location: lat - "
2417                                 + Rlog.pii(TAG, location.getLatitude())
2418                                 + ", long - "
2419                                 + Rlog.pii(TAG, location.getLongitude())
2420                                 + ", mS2Level - "
2421                                 + mS2Level);
2422                 Bundle bundle = new Bundle();
2423                 bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, satelliteAllowed);
2424                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
2425                         satelliteAllowed);
2426                 mLatestSatelliteCommunicationAllowed = satelliteAllowed;
2427                 mLatestSatelliteCommunicationAllowedSetTime = getElapsedRealtimeNanos();
2428                 persistLatestSatelliteCommunicationAllowedState();
2429             } catch (Exception ex) {
2430                 ploge("checkSatelliteAccessRestrictionForLocation: ex=" + ex);
2431                 reportAnomaly(UUID_ON_DEVICE_LOOKUP_EXCEPTION,
2432                         "On-device satellite lookup exception");
2433                 Bundle bundle = new Bundle();
2434                 if (isCommunicationAllowedCacheValid()) {
2435                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
2436                             mLatestSatelliteCommunicationAllowed);
2437                     plogd(
2438                             "checkSatelliteAccessRestrictionForLocation: cache is still valid, "
2439                                     + "allowing satellite communication");
2440                 } else {
2441                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
2442                     plogd("satellite communication not allowed");
2443                 }
2444                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
2445                         mLatestSatelliteCommunicationAllowed);
2446             }
2447         }
2448     }
2449 
updateRegionalConfigId()2450     private void updateRegionalConfigId() {
2451         synchronized (mLock) {
2452             plogd("mNewRegionalConfigId: updatedValue = " + mNewRegionalConfigId
2453                     + " | mRegionalConfigId: beforeValue = " + mRegionalConfigId);
2454             if (!Objects.equals(mRegionalConfigId, mNewRegionalConfigId)) {
2455                 mRegionalConfigId = mNewRegionalConfigId;
2456                 notifyRegionalSatelliteConfigurationChanged(
2457                         Optional.ofNullable(mSatelliteAccessConfigMap)
2458                                 .map(map -> map.get(mRegionalConfigId))
2459                                 .orElse(null));
2460             }
2461         }
2462     }
2463 
updateCachedAccessRestrictionMap( @onNull SatelliteOnDeviceAccessController.LocationToken locationToken, Integer regionalConfigId)2464     private void updateCachedAccessRestrictionMap(
2465             @NonNull SatelliteOnDeviceAccessController.LocationToken locationToken,
2466             Integer regionalConfigId) {
2467         synchronized (mLock) {
2468             mCachedAccessRestrictionMap.put(locationToken, regionalConfigId);
2469         }
2470     }
2471 
isGreaterThanAll( long comparedItem, @NonNull Collection<Long> itemCollection)2472     private boolean isGreaterThanAll(
2473             long comparedItem, @NonNull Collection<Long> itemCollection) {
2474         for (long item : itemCollection) {
2475             if (comparedItem <= item) return false;
2476         }
2477         return true;
2478     }
2479 
2480     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
isSatelliteAccessAllowedForLocation( @onNull List<String> networkCountryIsoList)2481     protected boolean isSatelliteAccessAllowedForLocation(
2482             @NonNull List<String> networkCountryIsoList) {
2483         if (isSatelliteAllowAccessControl()) {
2484             // The current country is unidentified, we're uncertain and thus returning false
2485             if (networkCountryIsoList.isEmpty()) {
2486                 return false;
2487             }
2488 
2489             // In case of allowed list, satellite is allowed if all country codes are be in the
2490             // allowed list
2491             return getSatelliteCountryCodes().containsAll(networkCountryIsoList);
2492         } else {
2493             // No country is barred, thus returning true
2494             if (getSatelliteCountryCodes().isEmpty()) {
2495                 return true;
2496             }
2497 
2498             // The current country is unidentified, we're uncertain and thus returning false
2499             if (networkCountryIsoList.isEmpty()) {
2500                 return false;
2501             }
2502 
2503             // In case of disallowed list, if any country code is in the list, satellite will be
2504             // disallowed
2505             for (String countryCode : networkCountryIsoList) {
2506                 if (getSatelliteCountryCodes().contains(countryCode)) {
2507                     return false;
2508                 }
2509             }
2510             return true;
2511         }
2512     }
2513 
shouldUseOnDeviceAccessController()2514     private boolean shouldUseOnDeviceAccessController() {
2515         if (getSatelliteS2CellFile() == null) {
2516             return false;
2517         }
2518 
2519         if (isInEmergency() || mLocationManager.isLocationEnabled()) {
2520             return true;
2521         }
2522 
2523         Location freshLastKnownLocation = getFreshLastKnownLocation();
2524         if (freshLastKnownLocation != null) {
2525             synchronized (mLock) {
2526                 mFreshLastKnownLocation = freshLastKnownLocation;
2527             }
2528             return true;
2529         } else {
2530             synchronized (mLock) {
2531                 mFreshLastKnownLocation = null;
2532             }
2533         }
2534         return false;
2535     }
2536 
2537     @Nullable
getFreshLastKnownLocation()2538     private Location getFreshLastKnownLocation() {
2539         Location lastKnownLocation = getLastKnownLocation();
2540         if (lastKnownLocation != null) {
2541             long lastKnownLocationAge =
2542                     getElapsedRealtimeNanos() - lastKnownLocation.getElapsedRealtimeNanos();
2543             if (lastKnownLocationAge <= getLocationFreshDurationNanos()) {
2544                 plogd("getFreshLastKnownLocation: lat=" + Rlog.pii(TAG,
2545                         lastKnownLocation.getLatitude())
2546                         + ", long=" + Rlog.pii(TAG, lastKnownLocation.getLongitude()));
2547                 return lastKnownLocation;
2548             }
2549         }
2550         return null;
2551     }
2552 
isInEmergency()2553     private boolean isInEmergency() {
2554         // Check if emergency call is ongoing
2555         if (mTelecomManager.isInEmergencyCall()) {
2556             plogd("In emergency call");
2557             return true;
2558         }
2559 
2560         // Check if the device is in emergency callback mode
2561         for (Phone phone : PhoneFactory.getPhones()) {
2562             if (phone.isInEcm()) {
2563                 plogd("In emergency callback mode");
2564                 return true;
2565             }
2566         }
2567 
2568         // Check if satellite is in emergency mode
2569         if (mSatelliteController.isInEmergencyMode()) {
2570             plogd("In satellite emergency mode");
2571             return true;
2572         }
2573         return false;
2574     }
2575 
2576     @Nullable
getLastKnownLocation()2577     private Location getLastKnownLocation() {
2578         Location result = null;
2579         for (String provider : mLocationManager.getProviders(true)) {
2580             Location location = mLocationManager.getLastKnownLocation(provider);
2581             if (location != null && (result == null
2582                     || result.getElapsedRealtimeNanos() < location.getElapsedRealtimeNanos())) {
2583                 result = location;
2584             }
2585         }
2586 
2587         if (result == null || isMockModemAllowed()) {
2588             return result;
2589         }
2590 
2591         return result.isMock() ? null : result;
2592     }
2593 
initSharedPreferences(@onNull Context context)2594     private void initSharedPreferences(@NonNull Context context) {
2595         try {
2596             mSharedPreferences =
2597                     context.getSharedPreferences(SATELLITE_SHARED_PREF, Context.MODE_PRIVATE);
2598         } catch (Exception e) {
2599             ploge("Cannot get default shared preferences: " + e);
2600         }
2601     }
2602 
2603     /**
2604      * @return {@code true} if successfully initialize the {@link SatelliteOnDeviceAccessController}
2605      * instance, {@code false} otherwise.
2606      * @throws IllegalStateException in case of getting any exception in creating the
2607      *                               {@link SatelliteOnDeviceAccessController} instance and the
2608      *                               device is using a user build.
2609      */
initSatelliteOnDeviceAccessController()2610     private boolean initSatelliteOnDeviceAccessController()
2611             throws IllegalStateException {
2612         plogd("initSatelliteOnDeviceAccessController");
2613 
2614         synchronized (mLock) {
2615             if (getSatelliteS2CellFile() == null) return false;
2616 
2617             // mSatelliteOnDeviceAccessController was already initialized successfully
2618             if (mSatelliteOnDeviceAccessController != null) {
2619                 restartKeepOnDeviceAccessControllerResourcesTimer();
2620                 return true;
2621             }
2622 
2623             try {
2624                 mSatelliteOnDeviceAccessController =
2625                         SatelliteOnDeviceAccessController.create(
2626                                 getSatelliteS2CellFile(), mFeatureFlags);
2627 
2628                 plogd(
2629                         "initSatelliteOnDeviceAccessController: initialized"
2630                             + " SatelliteOnDeviceAccessController");
2631                 restartKeepOnDeviceAccessControllerResourcesTimer();
2632                 mS2Level = mSatelliteOnDeviceAccessController.getS2Level();
2633                 plogd("mS2Level=" + mS2Level);
2634                 loadSatelliteAccessConfiguration();
2635             } catch (Exception ex) {
2636                 ploge("Got exception in creating an instance of SatelliteOnDeviceAccessController,"
2637                         + " ex=" + ex + ", sat s2 file="
2638                         + getSatelliteS2CellFile().getAbsolutePath());
2639                 reportAnomaly(UUID_CREATE_ON_DEVICE_ACCESS_CONTROLLER_EXCEPTION,
2640                         "Exception in creating on-device satellite access controller");
2641                 mSatelliteOnDeviceAccessController = null;
2642                 mSatelliteAccessConfigMap = null;
2643                 if (!mIsOverlayConfigOverridden) {
2644                     mSatelliteS2CellFile = null;
2645                 }
2646                 return false;
2647             }
2648             return true;
2649         }
2650     }
2651 
cleanupOnDeviceAccessControllerResources()2652     private void cleanupOnDeviceAccessControllerResources() {
2653         synchronized (mLock) {
2654             plogd("cleanupOnDeviceAccessControllerResources="
2655                     + (mSatelliteOnDeviceAccessController != null));
2656             if (mSatelliteOnDeviceAccessController != null) {
2657                 try {
2658                     mSatelliteOnDeviceAccessController.close();
2659                 } catch (Exception ex) {
2660                     ploge("cleanupOnDeviceAccessControllerResources: ex=" + ex);
2661                 }
2662                 mSatelliteOnDeviceAccessController = null;
2663                 stopKeepOnDeviceAccessControllerResourcesTimer();
2664             }
2665         }
2666     }
2667 
handleCmdUpdateSystemSelectionChannels( @onNull ResultReceiver resultReceiver)2668     private void handleCmdUpdateSystemSelectionChannels(
2669             @NonNull ResultReceiver resultReceiver) {
2670         synchronized (mLock) {
2671             mUpdateSystemSelectionChannelsResultReceivers.add(resultReceiver);
2672             if (mUpdateSystemSelectionChannelsResultReceivers.size() > 1) {
2673                 plogd("updateSystemSelectionChannels is already being processed");
2674                 return;
2675             }
2676             int subId =  mSatelliteController.getSelectedSatelliteSubId();
2677             plogd("handleCmdUpdateSystemSelectionChannels: SatellitePhone subId: " + subId);
2678             if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
2679                 sendUpdateSystemSelectionChannelsResult(
2680                         SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
2681                 return;
2682             }
2683 
2684             String mccmnc = "";
2685             final SubscriptionInfo subInfo = SubscriptionManagerService.getInstance()
2686                     .getSubscriptionInfo(subId);
2687             if (subInfo != null) {
2688                 mccmnc = subInfo.getMccString() + subInfo.getMncString();
2689             }
2690 
2691             final Integer[] regionalConfigId = new Integer[1];
2692             regionalConfigId[0] = getSelectedRegionalConfigId();
2693             if (regionalConfigId[0] != null
2694                     && regionalConfigId[0] == UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID) {
2695                 // The geofence file with old format return UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID
2696                 // for an S2 cell present in the file.
2697                 // For backward compatibility, we will use DEFAULT_REGIONAL_SATELLITE_CONFIG_ID
2698                 // for such cases.
2699                 regionalConfigId[0] = DEFAULT_REGIONAL_SATELLITE_CONFIG_ID;
2700             }
2701             if (!SatelliteAccessConfigurationParser.isRegionalConfigIdValid(regionalConfigId[0])) {
2702                 plogd("handleCmdUpdateSystemSelectionChannels: mRegionalConfigId is not valid, "
2703                         + "mRegionalConfig=" + getSelectedRegionalConfigId());
2704                 sendUpdateSystemSelectionChannelsResult(
2705                         SATELLITE_RESULT_ACCESS_BARRED, null);
2706                 return;
2707             }
2708 
2709             SatelliteAccessConfiguration satelliteAccessConfiguration;
2710             synchronized (mLock) {
2711                 satelliteAccessConfiguration = Optional.ofNullable(mSatelliteAccessConfigMap)
2712                         .map(map -> map.get(regionalConfigId[0]))
2713                         .orElse(null);
2714             }
2715             if (satelliteAccessConfiguration == null) {
2716                 plogd("handleCmdUpdateSystemSelectionChannels: satelliteAccessConfiguration "
2717                         + "is not valid");
2718                 sendUpdateSystemSelectionChannelsResult(
2719                         SATELLITE_RESULT_ACCESS_BARRED, null);
2720                 return;
2721             }
2722 
2723             List<SatelliteInfo> satelliteInfos =
2724                     satelliteAccessConfiguration.getSatelliteInfos();
2725             List<Integer> bandList = new ArrayList<>();
2726             List<Integer> earfcnList = new ArrayList<>();
2727             for (SatelliteInfo satelliteInfo : satelliteInfos) {
2728                 bandList.addAll(satelliteInfo.getBands());
2729                 List<EarfcnRange> earfcnRangeList = satelliteInfo.getEarfcnRanges();
2730                 earfcnRangeList.stream().flatMapToInt(
2731                         earfcnRange -> IntStream.of(earfcnRange.getStartEarfcn(),
2732                                 earfcnRange.getEndEarfcn())).boxed().forEach(earfcnList::add);
2733             }
2734 
2735             IntArray bands = new IntArray(bandList.size());
2736             bands.addAll(bandList.stream().mapToInt(Integer::intValue).toArray());
2737             IntArray earfcns = new IntArray(
2738                     Math.min(earfcnList.size(), MAX_EARFCN_ARRAY_LENGTH));
2739             for (int i = 0; i < Math.min(earfcnList.size(), MAX_EARFCN_ARRAY_LENGTH); i++) {
2740                 earfcns.add(earfcnList.get(i));
2741             }
2742             IntArray tagIds = new IntArray(satelliteAccessConfiguration.getTagIds().size());
2743             tagIds.addAll(satelliteAccessConfiguration.getTagIds().stream().mapToInt(
2744                     Integer::intValue).toArray());
2745 
2746             List<SystemSelectionSpecifier> selectionSpecifiers = new ArrayList<>();
2747             selectionSpecifiers.add(new SystemSelectionSpecifier(mccmnc, bands, earfcns,
2748                     satelliteInfos.toArray(new SatelliteInfo[0]), tagIds));
2749             mSatelliteController.updateSystemSelectionChannels(selectionSpecifiers,
2750                     mInternalUpdateSystemSelectionChannelsResultReceiver);
2751         }
2752     }
2753 
sendUpdateSystemSelectionChannelsResult(int resultCode, Bundle resultData)2754     private void sendUpdateSystemSelectionChannelsResult(int resultCode, Bundle resultData) {
2755         plogd("sendUpdateSystemSelectionChannelsResult: resultCode=" + resultCode);
2756 
2757         synchronized (mLock) {
2758             for (ResultReceiver resultReceiver : mUpdateSystemSelectionChannelsResultReceivers) {
2759                 resultReceiver.send(resultCode, resultData);
2760             }
2761             mUpdateSystemSelectionChannelsResultReceivers.clear();
2762         }
2763     }
2764 
getSatelliteAccessAllowFromOverlayConfig(@onNull Context context)2765     private static boolean getSatelliteAccessAllowFromOverlayConfig(@NonNull Context context) {
2766         Boolean accessAllowed = null;
2767         try {
2768             accessAllowed = context.getResources().getBoolean(
2769                     com.android.internal.R.bool.config_oem_enabled_satellite_access_allow);
2770         } catch (Resources.NotFoundException ex) {
2771             loge("getSatelliteAccessAllowFromOverlayConfig: got ex=" + ex);
2772         }
2773         if (accessAllowed == null && isMockModemAllowed()) {
2774             logd("getSatelliteAccessAllowFromOverlayConfig: Read "
2775                     + "config_oem_enabled_satellite_access_allow from device config");
2776             accessAllowed = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
2777                     "config_oem_enabled_satellite_access_allow", DEFAULT_SATELLITE_ACCESS_ALLOW);
2778         }
2779         if (accessAllowed == null) {
2780             logd("Use default satellite access allow=true control");
2781             accessAllowed = true;
2782         }
2783         return accessAllowed;
2784     }
2785 
2786     @Nullable
getSatelliteConfigurationFileNameFromOverlayConfig( @onNull Context context)2787     protected String getSatelliteConfigurationFileNameFromOverlayConfig(
2788             @NonNull Context context) {
2789         String satelliteAccessControlInfoFile = null;
2790 
2791         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
2792             logd("mFeatureFlags: carrierRoamingNbIotNtn is disabled");
2793             return satelliteAccessControlInfoFile;
2794         }
2795 
2796         try {
2797             satelliteAccessControlInfoFile = context.getResources().getString(
2798                     com.android.internal.R.string.satellite_access_config_file);
2799         } catch (Resources.NotFoundException ex) {
2800             loge("getSatelliteConfigurationFileNameFromOverlayConfig: got ex=" + ex);
2801         }
2802 
2803         logd("satelliteAccessControlInfoFile =" + satelliteAccessControlInfoFile);
2804         return satelliteAccessControlInfoFile;
2805     }
2806 
2807     @Nullable
getSatelliteS2CellFileFromOverlayConfig(@onNull Context context)2808     private static String getSatelliteS2CellFileFromOverlayConfig(@NonNull Context context) {
2809         String s2CellFile = null;
2810         try {
2811             s2CellFile = context.getResources().getString(
2812                     com.android.internal.R.string.config_oem_enabled_satellite_s2cell_file);
2813         } catch (Resources.NotFoundException ex) {
2814             loge("getSatelliteS2CellFileFromOverlayConfig: got ex=" + ex);
2815         }
2816         if (TextUtils.isEmpty(s2CellFile) && isMockModemAllowed()) {
2817             logd("getSatelliteS2CellFileFromOverlayConfig: Read "
2818                     + "config_oem_enabled_satellite_s2cell_file from device config");
2819             s2CellFile = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
2820                     "config_oem_enabled_satellite_s2cell_file", null);
2821         }
2822         logd("s2CellFile=" + s2CellFile);
2823         return s2CellFile;
2824     }
2825 
2826     @NonNull
getSatelliteCountryCodesFromOverlayConfig( @onNull Context context)2827     private static List<String> getSatelliteCountryCodesFromOverlayConfig(
2828             @NonNull Context context) {
2829         String[] countryCodes = readStringArrayFromOverlayConfig(context,
2830                 com.android.internal.R.array.config_oem_enabled_satellite_country_codes);
2831         if (countryCodes.length == 0 && isMockModemAllowed()) {
2832             logd("getSatelliteCountryCodesFromOverlayConfig: Read "
2833                     + "config_oem_enabled_satellite_country_codes from device config");
2834             String countryCodesStr = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
2835                     "config_oem_enabled_satellite_country_codes", "");
2836             countryCodes = countryCodesStr.split(",");
2837         }
2838         return Arrays.stream(countryCodes)
2839                 .map(x -> x.toUpperCase(Locale.US))
2840                 .collect(Collectors.toList());
2841     }
2842 
2843     @NonNull
getDelayBeforeRetryValidatingPossibleChangeInSatelliteAllowedRegionMillis( @onNull Context context)2844     private static long getDelayBeforeRetryValidatingPossibleChangeInSatelliteAllowedRegionMillis(
2845             @NonNull Context context) {
2846         Integer retryDuration = null;
2847         try {
2848             retryDuration = context.getResources().getInteger(com.android.internal.R.integer
2849                     .config_satellite_delay_minutes_before_retry_validating_possible_change_in_allowed_region);
2850         } catch (Resources.NotFoundException ex) {
2851             loge("getDelayBeforeRetryValidatingPossibleChangeInSatelliteAllowedRegionMillis: got "
2852                     + "ex=" + ex);
2853         }
2854         if (retryDuration == null) {
2855             logd("Use default retry duration for possible change satellite allowed region ="
2856                     + DEFAULT_DELAY_MINUTES_BEFORE_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION);
2857             retryDuration =
2858                     DEFAULT_DELAY_MINUTES_BEFORE_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION;
2859         }
2860         return TimeUnit.MINUTES.toMillis(retryDuration);
2861     }
2862 
2863     @NonNull
getMaxRetryCountForValidatingPossibleChangeInAllowedRegion( @onNull Context context)2864     private static int getMaxRetryCountForValidatingPossibleChangeInAllowedRegion(
2865             @NonNull Context context) {
2866         Integer maxRetrycount = null;
2867         try {
2868             maxRetrycount = context.getResources().getInteger(com.android.internal.R.integer
2869                     .config_satellite_max_retry_count_for_validating_possible_change_in_allowed_region);
2870         } catch (Resources.NotFoundException ex) {
2871             loge("getMaxRetryCountForValidatingPossibleChangeInAllowedRegion: got ex= " + ex);
2872         }
2873         if (maxRetrycount == null) {
2874             logd("Use default max retry count for possible change satellite allowed region ="
2875                     + DEFAULT_MAX_RETRY_COUNT_FOR_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION);
2876             maxRetrycount =
2877                     DEFAULT_MAX_RETRY_COUNT_FOR_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION;
2878         }
2879         return maxRetrycount;
2880     }
2881 
2882     @NonNull
getLocationQueryThrottleIntervalNanos(@onNull Context context)2883     private static long getLocationQueryThrottleIntervalNanos(@NonNull Context context) {
2884         Integer throttleInterval = null;
2885         try {
2886             throttleInterval = context.getResources().getInteger(com.android.internal.R.integer
2887                     .config_satellite_location_query_throttle_interval_minutes);
2888         } catch (Resources.NotFoundException ex) {
2889             loge("getLocationQueryThrottleIntervalNanos: got ex=" + ex);
2890         }
2891         if (throttleInterval == null) {
2892             logd("Use default location query throttle interval ="
2893                     + DEFAULT_THROTTLE_INTERVAL_FOR_LOCATION_QUERY_MINUTES);
2894             throttleInterval =
2895                     DEFAULT_THROTTLE_INTERVAL_FOR_LOCATION_QUERY_MINUTES;
2896         }
2897         return TimeUnit.MINUTES.toNanos(throttleInterval);
2898     }
2899 
2900     @NonNull
readStringArrayFromOverlayConfig( @onNull Context context, @ArrayRes int id)2901     private static String[] readStringArrayFromOverlayConfig(
2902             @NonNull Context context, @ArrayRes int id) {
2903         String[] strArray = null;
2904         try {
2905             strArray = context.getResources().getStringArray(id);
2906         } catch (Resources.NotFoundException ex) {
2907             loge("readStringArrayFromOverlayConfig: id= " + id + ", ex=" + ex);
2908         }
2909         if (strArray == null) {
2910             strArray = new String[0];
2911         }
2912         return strArray;
2913     }
2914 
getSatelliteLocationFreshDurationFromOverlayConfig( @onNull Context context)2915     private static long getSatelliteLocationFreshDurationFromOverlayConfig(
2916             @NonNull Context context) {
2917         Integer freshDuration = null;
2918         try {
2919             freshDuration = context.getResources().getInteger(com.android.internal.R.integer
2920                     .config_oem_enabled_satellite_location_fresh_duration);
2921         } catch (Resources.NotFoundException ex) {
2922             loge("getSatelliteLocationFreshDurationFromOverlayConfig: got ex=" + ex);
2923         }
2924         if (freshDuration == null && isMockModemAllowed()) {
2925             logd("getSatelliteLocationFreshDurationFromOverlayConfig: Read "
2926                     + "config_oem_enabled_satellite_location_fresh_duration from device config");
2927             freshDuration = DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
2928                     "config_oem_enabled_satellite_location_fresh_duration",
2929                     DEFAULT_LOCATION_FRESH_DURATION_SECONDS);
2930         }
2931         if (freshDuration == null) {
2932             logd("Use default satellite location fresh duration="
2933                     + DEFAULT_LOCATION_FRESH_DURATION_SECONDS);
2934             freshDuration = DEFAULT_LOCATION_FRESH_DURATION_SECONDS;
2935         }
2936         return TimeUnit.SECONDS.toNanos(freshDuration);
2937     }
2938 
startWaitForCurrentLocationTimer()2939     private void startWaitForCurrentLocationTimer() {
2940         synchronized (mLock) {
2941             if (hasMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT)) {
2942                 plogw("WaitForCurrentLocationTimer is already started");
2943                 removeMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
2944             }
2945             sendEmptyMessageDelayed(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT,
2946                     WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS);
2947         }
2948     }
2949 
stopWaitForCurrentLocationTimer()2950     private void stopWaitForCurrentLocationTimer() {
2951         synchronized (mLock) {
2952             removeMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
2953         }
2954     }
2955 
restartKeepOnDeviceAccessControllerResourcesTimer()2956     private void restartKeepOnDeviceAccessControllerResourcesTimer() {
2957         synchronized (mLock) {
2958             if (hasMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT)) {
2959                 plogd("KeepOnDeviceAccessControllerResourcesTimer is already started. "
2960                         + "Restarting it...");
2961                 removeMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
2962             }
2963             sendEmptyMessageDelayed(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT,
2964                     KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS);
2965         }
2966     }
2967 
stopKeepOnDeviceAccessControllerResourcesTimer()2968     private void stopKeepOnDeviceAccessControllerResourcesTimer() {
2969         synchronized (mLock) {
2970             removeMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
2971         }
2972     }
2973 
reportAnomaly(@onNull String uuid, @NonNull String log)2974     private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
2975         ploge(log);
2976         AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
2977     }
2978 
isMockModemAllowed()2979     private static boolean isMockModemAllowed() {
2980         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)
2981                 || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
2982     }
2983 
2984     /**
2985      * Posts the specified command to be executed on the main thread and returns immediately.
2986      *
2987      * @param command  command to be executed on the main thread
2988      * @param argument additional parameters required to perform of the operation
2989      */
sendDelayedRequestAsync(int command, @Nullable Object argument, long delayMillis)2990     private void sendDelayedRequestAsync(int command, @Nullable Object argument, long delayMillis) {
2991         Message msg = this.obtainMessage(command, argument);
2992         sendMessageDelayed(msg, delayMillis);
2993     }
2994 
2995     /**
2996      * Posts the specified command to be executed on the main thread and returns immediately.
2997      *
2998      * @param command  command to be executed on the main thread
2999      * @param argument additional parameters required to perform of the operation
3000      */
sendRequestAsync(int command, @Nullable Object argument)3001     private void sendRequestAsync(int command, @Nullable Object argument) {
3002         Message msg = this.obtainMessage(command, argument);
3003         msg.sendToTarget();
3004     }
3005 
3006     /**
3007      * Registers for the satellite communication allowed state changed.
3008      *
3009      * @param subId    The subId of the subscription to register for the satellite communication
3010      *                 allowed state changed.
3011      * @param callback The callback to handle the satellite communication allowed state changed
3012      *                 event.
3013      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
3014      */
3015     @SatelliteManager.SatelliteResult
registerForCommunicationAccessStateChanged(int subId, @NonNull ISatelliteCommunicationAccessStateCallback callback)3016     public int registerForCommunicationAccessStateChanged(int subId,
3017             @NonNull ISatelliteCommunicationAccessStateCallback callback) {
3018         mSatelliteCommunicationAccessStateChangedListeners.put(callback.asBinder(), callback);
3019 
3020         this.post(() -> {
3021             try {
3022                 synchronized (mSatelliteCommunicationAllowStateLock) {
3023                     callback.onAccessAllowedStateChanged(mCurrentSatelliteAllowedState);
3024                     logd("registerForCommunicationAccessStateChanged: "
3025                             + "mCurrentSatelliteAllowedState " + mCurrentSatelliteAllowedState);
3026                 }
3027                 synchronized (mLock) {
3028                     SatelliteAccessConfiguration satelliteAccessConfig =
3029                             Optional.ofNullable(mSatelliteAccessConfigMap)
3030                                     .map(map -> map.get(mRegionalConfigId))
3031                                     .orElse(null);
3032                     callback.onAccessConfigurationChanged(satelliteAccessConfig);
3033                     logd("registerForCommunicationAccessStateChanged: satelliteAccessConfig: "
3034                             + satelliteAccessConfig + " of mRegionalConfigId: "
3035                             + mRegionalConfigId);
3036                 }
3037             } catch (RemoteException ex) {
3038                 ploge("registerForCommunicationAccessStateChanged: RemoteException ex=" + ex);
3039             }
3040         });
3041 
3042         return SATELLITE_RESULT_SUCCESS;
3043     }
3044 
3045     /**
3046      * Unregisters for the satellite communication allowed state changed.
3047      * If callback was not registered before, the request will be ignored.
3048      *
3049      * @param subId    The subId of the subscription to unregister for the satellite communication
3050      *                 allowed state changed.
3051      * @param callback The callback that was passed to
3052      *                 {@link #registerForCommunicationAccessStateChanged(int,
3053      *                 ISatelliteCommunicationAccessStateCallback)}.
3054      */
unregisterForCommunicationAccessStateChanged( int subId, @NonNull ISatelliteCommunicationAccessStateCallback callback)3055     public void unregisterForCommunicationAccessStateChanged(
3056             int subId, @NonNull ISatelliteCommunicationAccessStateCallback callback) {
3057         mSatelliteCommunicationAccessStateChangedListeners.remove(callback.asBinder());
3058     }
3059 
3060     /**
3061      * Returns integer array of disallowed reasons of satellite.
3062      *
3063      * @return Integer array of disallowed reasons of satellite.
3064      */
3065     @NonNull
getSatelliteDisallowedReasons()3066     public List<Integer> getSatelliteDisallowedReasons() {
3067         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
3068             plogd("getSatelliteDisallowedReasons: carrierRoamingNbIotNtn is disabled");
3069             return new ArrayList<>();
3070         }
3071 
3072         List<Integer> satelliteDisallowedReasons = getSatelliteDisallowedReasonsCopy();
3073         plogd("getSatelliteDisallowedReasons: satelliteDisallowedReasons:"
3074                 + String.join(", ", satelliteDisallowedReasons.toString()));
3075         return satelliteDisallowedReasons;
3076     }
3077 
3078     /**
3079      * Registers for disallowed reasons change event from satellite service.
3080      *
3081      * @param callback The callback to handle disallowed reasons changed event.
3082      */
registerForSatelliteDisallowedReasonsChanged( @onNull ISatelliteDisallowedReasonsCallback callback)3083     public void registerForSatelliteDisallowedReasonsChanged(
3084             @NonNull ISatelliteDisallowedReasonsCallback callback) {
3085         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
3086             plogd("registerForSatelliteDisallowedReasonsChanged: carrierRoamingNbIotNtn is "
3087                     + "disabled");
3088             return;
3089         }
3090 
3091         mSatelliteDisallowedReasonsChangedListeners.put(callback.asBinder(), callback);
3092 
3093         this.post(() -> {
3094             try {
3095                 List<Integer> satelliteDisallowedReasons = getSatelliteDisallowedReasonsCopy();
3096                 callback.onSatelliteDisallowedReasonsChanged(
3097                         satelliteDisallowedReasons.stream()
3098                                 .mapToInt(Integer::intValue)
3099                                 .toArray());
3100                 logd("registerForSatelliteDisallowedReasonsChanged: "
3101                         + "satelliteDisallowedReasons " + satelliteDisallowedReasons.size());
3102             } catch (RemoteException ex) {
3103                 ploge("registerForSatelliteDisallowedReasonsChanged: RemoteException ex=" + ex);
3104             }
3105         });
3106     }
3107 
3108     /**
3109      * Unregisters for disallowed reasons change event from satellite service.
3110      * If callback was not registered before, the request will be ignored.
3111      *
3112      * @param callback The callback that was passed to
3113      *                 {@link #registerForSatelliteDisallowedReasonsChanged(
3114      *ISatelliteDisallowedReasonsCallback)}.
3115      */
unregisterForSatelliteDisallowedReasonsChanged( @onNull ISatelliteDisallowedReasonsCallback callback)3116     public void unregisterForSatelliteDisallowedReasonsChanged(
3117             @NonNull ISatelliteDisallowedReasonsCallback callback) {
3118         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
3119             plogd("unregisterForSatelliteDisallowedReasonsChanged: "
3120                     + "carrierRoamingNbIotNtn is disabled");
3121             return;
3122         }
3123 
3124         mSatelliteDisallowedReasonsChangedListeners.remove(callback.asBinder());
3125     }
3126 
3127     /**
3128      * This API can be used by only CTS to set the cache whether satellite communication is allowed.
3129      *
3130      * @param state a state indicates whether satellite access allowed state should be cached and
3131      *              the allowed state.
3132      * @return {@code true} if the setting is successful, {@code false} otherwise.
3133      */
setIsSatelliteCommunicationAllowedForCurrentLocationCache(String state)3134     public boolean setIsSatelliteCommunicationAllowedForCurrentLocationCache(String state) {
3135         if (!isMockModemAllowed()) {
3136             logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
3137                     + "mock modem not allowed.");
3138             return false;
3139         }
3140 
3141         logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: state=" + state);
3142 
3143         synchronized (mSatelliteCommunicationAllowStateLock) {
3144             if ("cache_allowed".equalsIgnoreCase(state)) {
3145                 mLatestSatelliteCommunicationAllowedSetTime = getElapsedRealtimeNanos();
3146                 mLatestSatelliteCommunicationAllowed = true;
3147                 updateCurrentSatelliteAllowedState(true);
3148             } else if ("cache_not_allowed".equalsIgnoreCase(state)) {
3149                 mLatestSatelliteCommunicationAllowedSetTime = getElapsedRealtimeNanos();
3150                 mLatestSatelliteCommunicationAllowed = false;
3151                 updateCurrentSatelliteAllowedState(false);
3152             } else if ("cache_clear_and_not_allowed".equalsIgnoreCase(state)) {
3153                 mLatestSatelliteCommunicationAllowedSetTime = 0;
3154                 mLatestSatelliteCommunicationAllowed = false;
3155                 updateCurrentSatelliteAllowedState(false);
3156                 persistLatestSatelliteCommunicationAllowedState();
3157             } else if ("clear_cache_only".equalsIgnoreCase(state)) {
3158                 mLatestSatelliteCommunicationAllowedSetTime = 0;
3159                 mLatestSatelliteCommunicationAllowed = false;
3160                 persistLatestSatelliteCommunicationAllowedState();
3161             } else {
3162                 loge("setIsSatelliteCommunicationAllowedForCurrentLocationCache: invalid state="
3163                         + state);
3164                 return false;
3165             }
3166         }
3167         return true;
3168     }
3169 
notifySatelliteCommunicationAllowedStateChanged(boolean allowState)3170     private void notifySatelliteCommunicationAllowedStateChanged(boolean allowState) {
3171         plogd("notifySatelliteCommunicationAllowedStateChanged: allowState=" + allowState);
3172 
3173         List<ISatelliteCommunicationAccessStateCallback> deadCallersList = new ArrayList<>();
3174         mSatelliteCommunicationAccessStateChangedListeners.values().forEach(listener -> {
3175             try {
3176                 listener.onAccessAllowedStateChanged(allowState);
3177             } catch (RemoteException e) {
3178                 plogd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
3179                 deadCallersList.add(listener);
3180             }
3181         });
3182         deadCallersList.forEach(listener -> {
3183             mSatelliteCommunicationAccessStateChangedListeners.remove(listener.asBinder());
3184         });
3185     }
3186 
notifySatelliteDisallowedReasonsChanged()3187     private void notifySatelliteDisallowedReasonsChanged() {
3188         plogd("notifySatelliteDisallowedReasonsChanged");
3189 
3190         List<Integer> satelliteDisallowedReasons = getSatelliteDisallowedReasonsCopy();
3191         List<ISatelliteDisallowedReasonsCallback> deadCallersList = new ArrayList<>();
3192         mSatelliteDisallowedReasonsChangedListeners.values().forEach(listener -> {
3193             try {
3194                 listener.onSatelliteDisallowedReasonsChanged(
3195                         satelliteDisallowedReasons.stream()
3196                                 .mapToInt(Integer::intValue)
3197                                 .toArray());
3198             } catch (RemoteException e) {
3199                 plogd("notifySatelliteDisallowedReasonsChanged RemoteException: " + e);
3200                 deadCallersList.add(listener);
3201             }
3202         });
3203         deadCallersList.forEach(listener -> {
3204             mSatelliteDisallowedReasonsChangedListeners.remove(listener.asBinder());
3205         });
3206     }
3207 
notifyRegionalSatelliteConfigurationChanged( @ullable SatelliteAccessConfiguration satelliteAccessConfig)3208     protected void notifyRegionalSatelliteConfigurationChanged(
3209             @Nullable SatelliteAccessConfiguration satelliteAccessConfig) {
3210         plogd("notifyRegionalSatelliteConfigurationChanged : satelliteAccessConfig is "
3211                 + satelliteAccessConfig);
3212 
3213         List<ISatelliteCommunicationAccessStateCallback> deadCallersList = new ArrayList<>();
3214         mSatelliteCommunicationAccessStateChangedListeners.values().forEach(listener -> {
3215             try {
3216                 listener.onAccessConfigurationChanged(satelliteAccessConfig);
3217             } catch (RemoteException e) {
3218                 plogd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
3219                 deadCallersList.add(listener);
3220             }
3221         });
3222         deadCallersList.forEach(listener -> {
3223             mSatelliteCommunicationAccessStateChangedListeners.remove(listener.asBinder());
3224         });
3225     }
3226 
reportMetrics(int resultCode, boolean allowed)3227     private void reportMetrics(int resultCode, boolean allowed) {
3228         if (resultCode == SATELLITE_RESULT_SUCCESS) {
3229             mControllerMetricsStats.reportAllowedSatelliteAccessCount(allowed);
3230         } else {
3231             mControllerMetricsStats.reportFailedSatelliteAccessCheckCount();
3232         }
3233 
3234         mControllerMetricsStats.reportCurrentVersionOfSatelliteAccessConfig(
3235                 mSatelliteAccessConfigVersion);
3236 
3237         mAccessControllerMetricsStats
3238                 .setLocationQueryTime(mLocationQueryStartTimeMillis)
3239                 .setTotalCheckingTime(mTotalCheckingStartTimeMillis)
3240                 .setIsAllowed(allowed)
3241                 .setIsEmergency(isInEmergency())
3242                 .setResult(resultCode)
3243                 .setCarrierId(mSatelliteController.getSatelliteCarrierId())
3244                 .setIsNtnOnlyCarrier(mSatelliteController.isNtnOnlyCarrier())
3245                 .reportAccessControllerMetrics();
3246         mLocationQueryStartTimeMillis = 0;
3247         mOnDeviceLookupStartTimeMillis = 0;
3248         mTotalCheckingStartTimeMillis = 0;
3249     }
3250 
isSatelliteAllowedRegionPossiblyChanged()3251     protected boolean isSatelliteAllowedRegionPossiblyChanged() {
3252         synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
3253             return mIsSatelliteAllowedRegionPossiblyChanged;
3254         }
3255     }
3256 
setIsSatelliteAllowedRegionPossiblyChanged(boolean changed)3257     protected void setIsSatelliteAllowedRegionPossiblyChanged(boolean changed) {
3258         synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
3259             plogd("setIsSatelliteAllowedRegionPossiblyChanged : " + changed);
3260             mIsSatelliteAllowedRegionPossiblyChanged = changed;
3261         }
3262     }
3263 
logd(@onNull String log)3264     private static void logd(@NonNull String log) {
3265         Log.d(TAG, log);
3266     }
3267 
logw(@onNull String log)3268     private static void logw(@NonNull String log) {
3269         Log.w(TAG, log);
3270     }
3271 
loge(@onNull String log)3272     protected static void loge(@NonNull String log) {
3273         Log.e(TAG, log);
3274     }
3275 
logv(@onNull String log)3276     private static void logv(@NonNull String log) {
3277         Log.v(TAG, log);
3278     }
3279 
3280     /**
3281      * This API can be used only for test purpose to override the carrier roaming Ntn eligibility
3282      *
3283      * @param state         to update Ntn Eligibility.
3284      * @param resetRequired to reset the overridden flag in satellite controller.
3285      * @return {@code true} if the shell command is successful, {@code false} otherwise.
3286      */
overrideCarrierRoamingNtnEligibilityChanged(boolean state, boolean resetRequired)3287     public boolean overrideCarrierRoamingNtnEligibilityChanged(boolean state,
3288             boolean resetRequired) {
3289         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
3290             logd("overrideCarrierRoamingNtnEligibilityChanged: "
3291                     + "carrierRoamingNbIotNtn is disabled");
3292             return false;
3293         }
3294 
3295         if (!isMockModemAllowed()) {
3296             logd("overrideCarrierRoamingNtnEligibilityChanged: "
3297                     + "mock modem not allowed.");
3298             return false;
3299         }
3300 
3301         logd("calling overrideCarrierRoamingNtnEligibilityChanged");
3302         return mSatelliteController.overrideCarrierRoamingNtnEligibilityChanged(state,
3303                 resetRequired);
3304     }
3305 
3306     private static final class SatelliteRegionalConfig {
3307         /** Regional satellite config IDs */
3308         private final int mConfigId;
3309 
3310         /** Set of earfcns in the corresponding regions */
3311         private final Set<Integer> mEarfcns;
3312 
SatelliteRegionalConfig(int configId, Set<Integer> earfcns)3313         SatelliteRegionalConfig(int configId, Set<Integer> earfcns) {
3314             this.mConfigId = configId;
3315             this.mEarfcns = earfcns;
3316         }
3317 
getEarfcns()3318         public Set<Integer> getEarfcns() {
3319             return mEarfcns;
3320         }
3321     }
3322 
updateSatelliteRegionalConfig(int subId)3323     private void updateSatelliteRegionalConfig(int subId) {
3324         plogd("updateSatelliteRegionalConfig: subId: " + subId);
3325         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
3326             return;
3327         }
3328 
3329         mSatelliteController.updateRegionalSatelliteEarfcns(subId);
3330         //key: regional satellite config Id,
3331         //value: set of earfcns in the corresponding regions
3332         Map<String, Set<Integer>> earfcnsMap = mSatelliteController
3333                 .getRegionalSatelliteEarfcns(subId);
3334         if (earfcnsMap.isEmpty()) {
3335             plogd("updateSatelliteRegionalConfig: Earfcns are not found for subId: "
3336                     + subId);
3337             return;
3338         }
3339 
3340         synchronized (mRegionalSatelliteEarfcnsLock) {
3341             SatelliteRegionalConfig satelliteRegionalConfig;
3342             /* Key: Regional satellite config ID, Value: SatelliteRegionalConfig
3343              * contains satellite config IDs and set of earfcns in the corresponding regions.
3344              */
3345             Map<Integer, SatelliteRegionalConfig> satelliteRegionalConfigMap = new HashMap<>();
3346             for (String configId: earfcnsMap.keySet()) {
3347                 Set<Integer> earfcnsSet = new HashSet<>();
3348                 for (int earfcn : earfcnsMap.get(configId)) {
3349                     earfcnsSet.add(earfcn);
3350                 }
3351                 satelliteRegionalConfig = new SatelliteRegionalConfig(Integer.valueOf(configId),
3352                         earfcnsSet);
3353                 satelliteRegionalConfigMap.put(Integer.valueOf(configId), satelliteRegionalConfig);
3354             }
3355 
3356             mSatelliteRegionalConfigPerSubMap.put(subId, satelliteRegionalConfigMap);
3357         }
3358     }
3359 
handleCarrierConfigChanged(@onNull Context context, int slotIndex, int subId, int carrierId, int specificCarrierId)3360     private void handleCarrierConfigChanged(@NonNull Context context, int slotIndex,
3361             int subId, int carrierId, int specificCarrierId) {
3362         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
3363             plogd("handleCarrierConfigChanged: carrierRoamingNbIotNtn flag is disabled");
3364             return;
3365         }
3366         plogd("handleCarrierConfigChanged: slotIndex=" + slotIndex + ", subId=" + subId
3367                 + ", carrierId=" + carrierId + ", specificCarrierId=" + specificCarrierId);
3368         updateSatelliteRegionalConfig(subId);
3369         evaluatePossibleChangeInDefaultSmsApp(context);
3370     }
3371 
3372     @Nullable
getSelectedRegionalConfigId()3373     private Integer getSelectedRegionalConfigId() {
3374         synchronized (mLock) {
3375             return mRegionalConfigId;
3376         }
3377     }
3378 
isReasonPresentInSatelliteDisallowedReasons(int disallowedReason)3379     private boolean isReasonPresentInSatelliteDisallowedReasons(int disallowedReason) {
3380         synchronized (mSatelliteDisallowedReasonsLock) {
3381             return mSatelliteDisallowedReasons.contains(Integer.valueOf(disallowedReason));
3382         }
3383     }
3384 
addReasonToSatelliteDisallowedReasons(int disallowedReason)3385     private void addReasonToSatelliteDisallowedReasons(int disallowedReason) {
3386         synchronized (mSatelliteDisallowedReasonsLock) {
3387             mSatelliteDisallowedReasons.add(Integer.valueOf(disallowedReason));
3388         }
3389     }
3390 
removeReasonFromSatelliteDisallowedReasons(int disallowedReason)3391     private void removeReasonFromSatelliteDisallowedReasons(int disallowedReason) {
3392         synchronized (mSatelliteDisallowedReasonsLock) {
3393             mSatelliteDisallowedReasons.remove(Integer.valueOf(disallowedReason));
3394         }
3395     }
3396 
isSatelliteDisallowedReasonsEmpty()3397     private boolean isSatelliteDisallowedReasonsEmpty() {
3398         synchronized (mSatelliteDisallowedReasonsLock) {
3399             return mSatelliteDisallowedReasons.isEmpty();
3400         }
3401     }
3402 
removeAllReasonsFromSatelliteDisallowedReasons( List<Integer> disallowedReasonsList)3403     private void removeAllReasonsFromSatelliteDisallowedReasons(
3404             List<Integer> disallowedReasonsList) {
3405         synchronized (mSatelliteDisallowedReasonsLock) {
3406             mSatelliteDisallowedReasons.removeAll(disallowedReasonsList);
3407         }
3408     }
3409 
getSatelliteDisallowedReasonsCopy()3410     private List<Integer> getSatelliteDisallowedReasonsCopy() {
3411         List<Integer> satelliteDisallowedReasons;
3412         synchronized (mSatelliteDisallowedReasonsLock) {
3413             satelliteDisallowedReasons = new ArrayList<>(mSatelliteDisallowedReasons);
3414         }
3415         return satelliteDisallowedReasons;
3416     }
3417 
3418     /**
3419      * Returns the satellite access configuration version.
3420      *
3421      * If the satellite config data hasn't been updated by configUpdater,
3422      * it returns 0. If it has been updated, it returns the updated version information.
3423      */
3424     @NonNull
getSatelliteAccessConfigVersion()3425     public int getSatelliteAccessConfigVersion() {
3426         return mSatelliteAccessConfigVersion;
3427     }
3428 
plogv(@onNull String log)3429     private void plogv(@NonNull String log) {
3430         Log.v(TAG, log);
3431         if (mPersistentLogger != null) {
3432             mPersistentLogger.debug(TAG, log);
3433         }
3434     }
3435 
plogd(@onNull String log)3436     private void plogd(@NonNull String log) {
3437         Log.d(TAG, log);
3438         if (mPersistentLogger != null) {
3439             mPersistentLogger.debug(TAG, log);
3440         }
3441     }
3442 
plogw(@onNull String log)3443     private void plogw(@NonNull String log) {
3444         Log.w(TAG, log);
3445         if (mPersistentLogger != null) {
3446             mPersistentLogger.warn(TAG, log);
3447         }
3448     }
3449 
ploge(@onNull String log)3450     private void ploge(@NonNull String log) {
3451         Log.e(TAG, log);
3452         if (mPersistentLogger != null) {
3453             mPersistentLogger.error(TAG, log);
3454         }
3455     }
3456 }
3457