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_COMMUNICATION_ALLOWED;
20 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
21 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
22 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_LOCATION_NOT_AVAILABLE;
23 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
24 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
25 
26 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_SHARED_PREF;
27 
28 import android.annotation.ArrayRes;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.content.Context;
32 import android.content.SharedPreferences;
33 import android.content.res.Resources;
34 import android.location.Location;
35 import android.location.LocationManager;
36 import android.location.LocationRequest;
37 import android.os.AsyncResult;
38 import android.os.Build;
39 import android.os.Bundle;
40 import android.os.CancellationSignal;
41 import android.os.Handler;
42 import android.os.HandlerThread;
43 import android.os.IBinder;
44 import android.os.Looper;
45 import android.os.Message;
46 import android.os.RemoteException;
47 import android.os.ResultReceiver;
48 import android.os.SystemClock;
49 import android.os.SystemProperties;
50 import android.provider.DeviceConfig;
51 import android.telecom.TelecomManager;
52 import android.telephony.AnomalyReporter;
53 import android.telephony.DropBoxManagerLoggerBackend;
54 import android.telephony.PersistentLogger;
55 import android.telephony.Rlog;
56 import android.telephony.SubscriptionManager;
57 import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
58 import android.telephony.satellite.ISatelliteProvisionStateCallback;
59 import android.telephony.satellite.ISatelliteSupportedStateCallback;
60 import android.telephony.satellite.SatelliteManager;
61 import android.text.TextUtils;
62 import android.util.Pair;
63 
64 import com.android.internal.R;
65 import com.android.internal.annotations.GuardedBy;
66 import com.android.internal.annotations.VisibleForTesting;
67 import com.android.internal.telephony.Phone;
68 import com.android.internal.telephony.PhoneFactory;
69 import com.android.internal.telephony.TelephonyCountryDetector;
70 import com.android.internal.telephony.flags.FeatureFlags;
71 import com.android.internal.telephony.satellite.SatelliteConfig;
72 import com.android.internal.telephony.satellite.SatelliteConstants;
73 import com.android.internal.telephony.satellite.SatelliteController;
74 import com.android.internal.telephony.satellite.metrics.AccessControllerMetricsStats;
75 import com.android.internal.telephony.satellite.metrics.ConfigUpdaterMetricsStats;
76 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
77 import com.android.internal.telephony.util.TelephonyUtils;
78 import com.android.phone.PhoneGlobals;
79 
80 import java.io.File;
81 import java.io.FileInputStream;
82 import java.io.IOException;
83 import java.io.InputStream;
84 import java.nio.file.Files;
85 import java.nio.file.Path;
86 import java.nio.file.StandardCopyOption;
87 import java.time.Duration;
88 import java.util.ArrayList;
89 import java.util.Arrays;
90 import java.util.Collection;
91 import java.util.HashSet;
92 import java.util.LinkedHashMap;
93 import java.util.List;
94 import java.util.Locale;
95 import java.util.Map;
96 import java.util.Set;
97 import java.util.UUID;
98 import java.util.concurrent.ConcurrentHashMap;
99 import java.util.concurrent.TimeUnit;
100 import java.util.stream.Collectors;
101 
102 /**
103  * This module is responsible for making sure that satellite communication can be used by devices
104  * in only regions allowed by OEMs.
105  */
106 public class SatelliteAccessController extends Handler {
107     private static final String TAG = "SatelliteAccessController";
108     /**
109      * UUID to report an anomaly when getting an exception in looking up on-device data for the
110      * current location.
111      */
112     private static final String UUID_ON_DEVICE_LOOKUP_EXCEPTION =
113             "dbea1641-630e-4780-9f25-8337ba6c3563";
114     /**
115      * UUID to report an anomaly when getting an exception in creating the on-device access
116      * controller.
117      */
118     private static final String UUID_CREATE_ON_DEVICE_ACCESS_CONTROLLER_EXCEPTION =
119             "3ac767d8-2867-4d60-97c2-ae9d378a5521";
120     protected static final long WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS =
121             TimeUnit.SECONDS.toMillis(180);
122     protected static final long KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS =
123             TimeUnit.MINUTES.toMillis(30);
124     protected static final int DEFAULT_S2_LEVEL = 12;
125     private static final int DEFAULT_LOCATION_FRESH_DURATION_SECONDS = 600;
126     private static final boolean DEFAULT_SATELLITE_ACCESS_ALLOW = true;
127     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
128     private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
129     private static final boolean DEBUG = !"user".equals(Build.TYPE);
130     private static final int MAX_CACHE_SIZE = 50;
131 
132     private static final int CMD_IS_SATELLITE_COMMUNICATION_ALLOWED = 1;
133     protected static final int EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT = 2;
134     protected static final int EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT = 3;
135     protected static final int EVENT_CONFIG_DATA_UPDATED = 4;
136 
137     private static SatelliteAccessController sInstance;
138 
139     /** Feature flags to control behavior and errors. */
140     @NonNull
141     private final FeatureFlags mFeatureFlags;
142     @GuardedBy("mLock")
143     @Nullable
144     protected SatelliteOnDeviceAccessController mSatelliteOnDeviceAccessController;
145     @NonNull
146     private final LocationManager mLocationManager;
147     @NonNull
148     private final TelecomManager mTelecomManager;
149     @NonNull
150     private final TelephonyCountryDetector mCountryDetector;
151     @NonNull
152     private final SatelliteController mSatelliteController;
153     @NonNull
154     private final ControllerMetricsStats mControllerMetricsStats;
155     @NonNull
156     private final AccessControllerMetricsStats mAccessControllerMetricsStats;
157     @NonNull
158     private final ResultReceiver mInternalSatelliteSupportedResultReceiver;
159     @NonNull
160     private final ResultReceiver mInternalSatelliteProvisionedResultReceiver;
161     @NonNull
162     private final ISatelliteSupportedStateCallback mInternalSatelliteSupportedStateCallback;
163     @NonNull
164     private final ISatelliteProvisionStateCallback mInternalSatelliteProvisionStateCallback;
165     @NonNull
166     protected final Object mLock = new Object();
167     @GuardedBy("mLock")
168     @NonNull
169     private final Set<ResultReceiver> mSatelliteAllowResultReceivers = new HashSet<>();
170     @NonNull
171     private List<String> mSatelliteCountryCodes;
172     private boolean mIsSatelliteAllowAccessControl;
173     @Nullable
174     private File mSatelliteS2CellFile;
175     private long mLocationFreshDurationNanos;
176     @GuardedBy("mLock")
177     private boolean mIsOverlayConfigOverridden = false;
178     @NonNull
179     private List<String> mOverriddenSatelliteCountryCodes;
180     private boolean mOverriddenIsSatelliteAllowAccessControl;
181     @Nullable
182     private File mOverriddenSatelliteS2CellFile;
183     private long mOverriddenLocationFreshDurationNanos;
184     @GuardedBy("mLock")
185     @NonNull
186     private final Map<SatelliteOnDeviceAccessController.LocationToken, Boolean>
187             mCachedAccessRestrictionMap = new LinkedHashMap<>() {
188         @Override
189         protected boolean removeEldestEntry(
190                 Entry<SatelliteOnDeviceAccessController.LocationToken, Boolean> eldest) {
191             return size() > MAX_CACHE_SIZE;
192         }
193     };
194     @GuardedBy("mLock")
195     @Nullable
196     CancellationSignal mLocationRequestCancellationSignal = null;
197     private int mS2Level = DEFAULT_S2_LEVEL;
198     @GuardedBy("mLock")
199     @Nullable
200     private Location mFreshLastKnownLocation = null;
201 
202     /** These are used for CTS test */
203     private Path mCtsSatS2FilePath = null;
204     protected static final String GOOGLE_US_SAN_SAT_S2_FILE_NAME = "google_us_san_sat_s2.dat";
205 
206     /** These are for config updater config data */
207     private static final String SATELLITE_ACCESS_CONTROL_DATA_DIR = "satellite_access_control";
208     private static final String CONFIG_UPDATER_S2_CELL_FILE_NAME = "config_updater_sat_s2.dat";
209     private static final int MIN_S2_LEVEL = 0;
210     private static final int MAX_S2_LEVEL = 30;
211     private static final String CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY =
212             "config_updater_satellite_country_codes";
213     private static final String CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY =
214             "config_updater_satellite_is_allow_access_control";
215 
216     private static final String LATEST_SATELLITE_COMMUNICATION_ALLOWED_SET_TIME_KEY =
217             "latest_satellite_communication_allowed_set_time";
218     private static final String LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY =
219             "latest_satellite_communication_allowed";
220 
221     private SharedPreferences mSharedPreferences;
222     private final ConfigUpdaterMetricsStats mConfigUpdaterMetricsStats;
223     @Nullable
224     private PersistentLogger mPersistentLogger = null;
225 
226     /**
227      * Map key: binder of the callback, value: callback to receive the satellite communication
228      * allowed state changed events.
229      */
230     private final ConcurrentHashMap<IBinder, ISatelliteCommunicationAllowedStateCallback>
231             mSatelliteCommunicationAllowedStateChangedListeners = new ConcurrentHashMap<>();
232     private final Object mSatelliteCommunicationAllowStateLock = new Object();
233     @GuardedBy("mSatelliteCommunicationAllowStateLock")
234     private boolean mCurrentSatelliteAllowedState = false;
235 
236     private static final long ALLOWED_STATE_CACHE_VALID_DURATION_HOURS =
237             Duration.ofHours(4).toNanos();
238     private boolean mLatestSatelliteCommunicationAllowed;
239     private long mLatestSatelliteCommunicationAllowedSetTime;
240 
241     private long mLocationQueryStartTimeMillis;
242     private long mOnDeviceLookupStartTimeMillis;
243     private long mTotalCheckingStartTimeMillis;
244 
245     /**
246      * Create a SatelliteAccessController instance.
247      *
248      * @param context                           The context associated with the
249      *                                          {@link SatelliteAccessController} instance.
250      * @param featureFlags                      The FeatureFlags that are supported.
251      * @param locationManager                   The LocationManager for querying current location of
252      *                                          the device.
253      * @param looper                            The Looper to run the SatelliteAccessController on.
254      * @param satelliteOnDeviceAccessController The on-device satellite access controller instance.
255      */
256     @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)257     protected SatelliteAccessController(@NonNull Context context,
258             @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
259             @NonNull LocationManager locationManager, @NonNull TelecomManager telecomManager,
260             @Nullable SatelliteOnDeviceAccessController satelliteOnDeviceAccessController,
261             @Nullable File s2CellFile) {
262         super(looper);
263         if (isSatellitePersistentLoggingEnabled(context, featureFlags)) {
264             mPersistentLogger = new PersistentLogger(
265                     DropBoxManagerLoggerBackend.getInstance(context));
266         }
267         mFeatureFlags = featureFlags;
268         mLocationManager = locationManager;
269         mTelecomManager = telecomManager;
270         mSatelliteOnDeviceAccessController = satelliteOnDeviceAccessController;
271         mCountryDetector = TelephonyCountryDetector.getInstance(context);
272         mSatelliteController = SatelliteController.getInstance();
273         mControllerMetricsStats = ControllerMetricsStats.getInstance();
274         mAccessControllerMetricsStats = AccessControllerMetricsStats.getInstance();
275         initSharedPreferences(context);
276         loadOverlayConfigs(context);
277         // loadConfigUpdaterConfigs has to be called after loadOverlayConfigs
278         // since config updater config has higher priority and thus can override overlay config
279         loadConfigUpdaterConfigs();
280         mSatelliteController.registerForConfigUpdateChanged(this, EVENT_CONFIG_DATA_UPDATED,
281                 context);
282         if (s2CellFile != null) {
283             mSatelliteS2CellFile = s2CellFile;
284         }
285         mInternalSatelliteSupportedResultReceiver = new ResultReceiver(this) {
286             @Override
287             protected void onReceiveResult(int resultCode, Bundle resultData) {
288                 handleIsSatelliteSupportedResult(resultCode, resultData);
289             }
290         };
291         mInternalSatelliteProvisionedResultReceiver = new ResultReceiver(this) {
292             @Override
293             protected void onReceiveResult(int resultCode, Bundle resultData) {
294                 handleIsSatelliteProvisionedResult(resultCode, resultData);
295             }
296         };
297 
298         mConfigUpdaterMetricsStats = ConfigUpdaterMetricsStats.getOrCreateInstance();
299 
300         mInternalSatelliteSupportedStateCallback = new ISatelliteSupportedStateCallback.Stub() {
301             @Override
302             public void onSatelliteSupportedStateChanged(boolean isSupported) {
303                 logd("onSatelliteSupportedStateChanged: isSupported=" + isSupported);
304                 if (isSupported) {
305                     requestIsCommunicationAllowedForCurrentLocation(
306                             SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, new ResultReceiver(null) {
307                                 @Override
308                                 protected void onReceiveResult(int resultCode, Bundle resultData) {
309                                     // do nothing
310                                 }
311                             });
312                 }
313             }
314         };
315         mSatelliteController.registerForSatelliteSupportedStateChanged(
316                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
317                 mInternalSatelliteSupportedStateCallback);
318 
319         mInternalSatelliteProvisionStateCallback = new ISatelliteProvisionStateCallback.Stub() {
320             @Override
321             public void onSatelliteProvisionStateChanged(boolean isProvisioned) {
322                 logd("onSatelliteProvisionStateChanged: isProvisioned=" + isProvisioned);
323                 if (isProvisioned) {
324                     requestIsCommunicationAllowedForCurrentLocation(
325                             SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, new ResultReceiver(null) {
326                                 @Override
327                                 protected void onReceiveResult(int resultCode, Bundle resultData) {
328                                     // do nothing
329                                 }
330                             });
331                 }
332             }
333         };
334         mSatelliteController.registerForSatelliteProvisionStateChanged(
335                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
336                 mInternalSatelliteProvisionStateCallback);
337 
338         // Init the SatelliteOnDeviceAccessController so that the S2 level can be cached
339         initSatelliteOnDeviceAccessController();
340     }
341 
updateCurrentSatelliteAllowedState(boolean isAllowed)342     private void updateCurrentSatelliteAllowedState(boolean isAllowed) {
343         plogd("updateCurrentSatelliteAllowedState");
344         synchronized (mSatelliteCommunicationAllowStateLock) {
345             if (isAllowed != mCurrentSatelliteAllowedState) {
346                 plogd("updatedValue = " + isAllowed + " | mCurrentSatelliteAllowedState = "
347                         + mCurrentSatelliteAllowedState);
348                 mCurrentSatelliteAllowedState = isAllowed;
349                 notifySatelliteCommunicationAllowedStateChanged(isAllowed);
350             }
351         }
352     }
353 
354     /** @return the singleton instance of {@link SatelliteAccessController} */
getOrCreateInstance( @onNull Context context, @NonNull FeatureFlags featureFlags)355     public static synchronized SatelliteAccessController getOrCreateInstance(
356             @NonNull Context context, @NonNull FeatureFlags featureFlags) {
357         if (sInstance == null) {
358             HandlerThread handlerThread = new HandlerThread("SatelliteAccessController");
359             handlerThread.start();
360             LocationManager lm = context.createAttributionContext("telephony")
361                     .getSystemService(LocationManager.class);
362             sInstance = new SatelliteAccessController(context, featureFlags,
363                     handlerThread.getLooper(), lm,
364                     context.getSystemService(TelecomManager.class), null, null);
365         }
366         return sInstance;
367     }
368 
369     @Override
handleMessage(Message msg)370     public void handleMessage(Message msg) {
371         switch (msg.what) {
372             case CMD_IS_SATELLITE_COMMUNICATION_ALLOWED:
373                 handleCmdIsSatelliteAllowedForCurrentLocation(
374                         (Pair<Integer, ResultReceiver>) msg.obj);
375                 break;
376             case EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT:
377                 handleWaitForCurrentLocationTimedOutEvent();
378                 break;
379             case EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT:
380                 cleanupOnDeviceAccessControllerResources();
381                 break;
382             case EVENT_CONFIG_DATA_UPDATED:
383                 AsyncResult ar = (AsyncResult) msg.obj;
384                 updateSatelliteConfigData((Context) ar.userObj);
385                 break;
386             default:
387                 plogw("SatelliteAccessControllerHandler: unexpected message code: " + msg.what);
388                 break;
389         }
390     }
391 
392     /**
393      * Request to get whether satellite communication is allowed for the current location.
394      *
395      * @param subId  The subId of the subscription to check whether satellite communication is
396      *               allowed for the current location for.
397      * @param result The result receiver that returns whether satellite communication is allowed
398      *               for the current location if the request is successful or an error code
399      *               if the request failed.
400      */
requestIsCommunicationAllowedForCurrentLocation(int subId, @NonNull ResultReceiver result)401     public void requestIsCommunicationAllowedForCurrentLocation(int subId,
402             @NonNull ResultReceiver result) {
403         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
404             plogd("oemEnabledSatelliteFlag is disabled");
405             result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
406             return;
407         }
408         sendRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED, new Pair<>(subId, result));
409     }
410 
411     /**
412      * This API should be used by only CTS tests to override the overlay configs of satellite
413      * access controller.
414      */
setSatelliteAccessControlOverlayConfigs(boolean reset, boolean isAllowed, @Nullable String s2CellFile, long locationFreshDurationNanos, @Nullable List<String> satelliteCountryCodes)415     public boolean setSatelliteAccessControlOverlayConfigs(boolean reset, boolean isAllowed,
416             @Nullable String s2CellFile, long locationFreshDurationNanos,
417             @Nullable List<String> satelliteCountryCodes) {
418         if (!isMockModemAllowed()) {
419             plogd("setSatelliteAccessControllerOverlayConfigs: mock modem is not allowed");
420             return false;
421         }
422         plogd("setSatelliteAccessControlOverlayConfigs: reset=" + reset
423                 + ", isAllowed" + isAllowed + ", s2CellFile=" + s2CellFile
424                 + ", locationFreshDurationNanos=" + locationFreshDurationNanos
425                 + ", satelliteCountryCodes=" + ((satelliteCountryCodes != null)
426                 ? String.join(", ", satelliteCountryCodes) : null));
427         synchronized (mLock) {
428             if (reset) {
429                 mIsOverlayConfigOverridden = false;
430                 cleanUpCtsResources();
431             } else {
432                 mIsOverlayConfigOverridden = true;
433                 mOverriddenIsSatelliteAllowAccessControl = isAllowed;
434                 if (!TextUtils.isEmpty(s2CellFile)) {
435                     mOverriddenSatelliteS2CellFile = getTestSatelliteS2File(s2CellFile);
436                     if (!mOverriddenSatelliteS2CellFile.exists()) {
437                         plogd("The overriding file "
438                                 + mOverriddenSatelliteS2CellFile.getAbsolutePath()
439                                 + " does not exist");
440                         mOverriddenSatelliteS2CellFile = null;
441                     }
442                 } else {
443                     mOverriddenSatelliteS2CellFile = null;
444                 }
445                 mOverriddenLocationFreshDurationNanos = locationFreshDurationNanos;
446                 if (satelliteCountryCodes != null) {
447                     mOverriddenSatelliteCountryCodes = satelliteCountryCodes;
448                 } else {
449                     mOverriddenSatelliteCountryCodes = new ArrayList<>();
450                 }
451             }
452             cleanupOnDeviceAccessControllerResources();
453             initSatelliteOnDeviceAccessController();
454         }
455         return true;
456     }
457 
getTestSatelliteS2File(String fileName)458     protected File getTestSatelliteS2File(String fileName) {
459         plogd("getTestSatelliteS2File: fileName=" + fileName);
460         if (TextUtils.equals(fileName, GOOGLE_US_SAN_SAT_S2_FILE_NAME)) {
461             mCtsSatS2FilePath = copyTestSatS2FileToPhoneDirectory(GOOGLE_US_SAN_SAT_S2_FILE_NAME);
462             if (mCtsSatS2FilePath != null) {
463                 return mCtsSatS2FilePath.toFile();
464             } else {
465                 ploge("getTestSatelliteS2File: mCtsSatS2FilePath is null");
466             }
467         }
468         return new File(fileName);
469     }
470 
471     @Nullable
copyTestSatS2FileToPhoneDirectory(String sourceFileName)472     private static Path copyTestSatS2FileToPhoneDirectory(String sourceFileName) {
473         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
474         File ctsFile = phoneGlobals.getDir("cts", Context.MODE_PRIVATE);
475         if (!ctsFile.exists()) {
476             ctsFile.mkdirs();
477         }
478 
479         Path targetDir = ctsFile.toPath();
480         Path targetSatS2FilePath = targetDir.resolve(sourceFileName);
481         try {
482             InputStream inputStream = phoneGlobals.getAssets().open(sourceFileName);
483             if (inputStream == null) {
484                 loge("copyTestSatS2FileToPhoneDirectory: Resource=" + sourceFileName
485                         + " not found");
486             } else {
487                 Files.copy(inputStream, targetSatS2FilePath, StandardCopyOption.REPLACE_EXISTING);
488             }
489         } catch (IOException ex) {
490             loge("copyTestSatS2FileToPhoneDirectory: ex=" + ex);
491         }
492         return targetSatS2FilePath;
493     }
494 
495     @Nullable
copySatS2FileToLocalDirectory(@onNull File sourceFile)496     private static File copySatS2FileToLocalDirectory(@NonNull File sourceFile) {
497         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
498         File satelliteAccessControlFile = phoneGlobals.getDir(
499                 SATELLITE_ACCESS_CONTROL_DATA_DIR, Context.MODE_PRIVATE);
500         if (!satelliteAccessControlFile.exists()) {
501             satelliteAccessControlFile.mkdirs();
502         }
503 
504         Path targetDir = satelliteAccessControlFile.toPath();
505         Path targetSatS2FilePath = targetDir.resolve(CONFIG_UPDATER_S2_CELL_FILE_NAME);
506         try {
507             InputStream inputStream = new FileInputStream(sourceFile);
508             if (inputStream == null) {
509                 loge("copySatS2FileToPhoneDirectory: Resource=" + sourceFile.getAbsolutePath()
510                         + " not found");
511                 return null;
512             } else {
513                 Files.copy(inputStream, targetSatS2FilePath, StandardCopyOption.REPLACE_EXISTING);
514             }
515         } catch (IOException ex) {
516             loge("copySatS2FileToPhoneDirectory: ex=" + ex);
517             return null;
518         }
519         return targetSatS2FilePath.toFile();
520     }
521 
522     @Nullable
getConfigUpdaterSatS2CellFileFromLocalDirectory()523     private File getConfigUpdaterSatS2CellFileFromLocalDirectory() {
524         PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
525         File satelliteAccessControlFile = phoneGlobals.getDir(
526                 SATELLITE_ACCESS_CONTROL_DATA_DIR, Context.MODE_PRIVATE);
527         if (!satelliteAccessControlFile.exists()) {
528             return null;
529         }
530 
531         Path satelliteAccessControlFileDir = satelliteAccessControlFile.toPath();
532         Path configUpdaterSatS2FilePath = satelliteAccessControlFileDir.resolve(
533                 CONFIG_UPDATER_S2_CELL_FILE_NAME);
534         return configUpdaterSatS2FilePath.toFile();
535     }
536 
isS2CellFileValid(@onNull File s2CellFile)537     private boolean isS2CellFileValid(@NonNull File s2CellFile) {
538         try {
539             SatelliteOnDeviceAccessController satelliteOnDeviceAccessController =
540                     SatelliteOnDeviceAccessController.create(s2CellFile);
541             int s2Level = satelliteOnDeviceAccessController.getS2Level();
542             if (s2Level < MIN_S2_LEVEL || s2Level > MAX_S2_LEVEL) {
543                 ploge("isS2CellFileValid: invalid s2 level = " + s2Level);
544                 satelliteOnDeviceAccessController.close();
545                 return false;
546             }
547             satelliteOnDeviceAccessController.close();
548         } catch (Exception ex) {
549             ploge("isS2CellFileValid: Got exception in reading the file, ex=" + ex);
550             return false;
551         }
552         return true;
553     }
554 
cleanUpCtsResources()555     private void cleanUpCtsResources() {
556         if (mCtsSatS2FilePath != null) {
557             try {
558                 Files.delete(mCtsSatS2FilePath);
559             } catch (IOException ex) {
560                 ploge("cleanUpCtsResources: ex=" + ex);
561             }
562         }
563     }
564 
565     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getElapsedRealtimeNanos()566     protected long getElapsedRealtimeNanos() {
567         return SystemClock.elapsedRealtimeNanos();
568     }
569 
570     /**
571      * @param countryCodes list of country code (two letters based on the ISO 3166-1).
572      * @return {@code true} if the countryCode is valid {@code false} otherwise.
573      */
isValidCountryCodes(@ullable List<String> countryCodes)574     private boolean isValidCountryCodes(@Nullable List<String> countryCodes) {
575         if (countryCodes == null || countryCodes.isEmpty()) {
576             return false;
577         }
578         for (String countryCode : countryCodes) {
579             if (!TelephonyUtils.isValidCountryCode(countryCode)) {
580                 ploge("invalid country code : " + countryCode);
581                 return false;
582             }
583         }
584         return true;
585     }
586 
updateSharedPreferencesCountryCodes( @onNull Context context, @NonNull List<String> value)587     private boolean updateSharedPreferencesCountryCodes(
588             @NonNull Context context, @NonNull List<String> value) {
589         if (mSharedPreferences == null) {
590             plogd("updateSharedPreferencesCountryCodes: mSharedPreferences is null");
591             initSharedPreferences(context);
592         }
593         if (mSharedPreferences == null) {
594             ploge("updateSharedPreferencesCountryCodes: mSharedPreferences is null");
595             return false;
596         }
597         try {
598             mSharedPreferences.edit().putStringSet(
599                     CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY, new HashSet<>(value)).apply();
600             return true;
601         } catch (Exception ex) {
602             ploge("updateSharedPreferencesCountryCodes error : " + ex);
603             return false;
604         }
605     }
606 
updateSharedPreferencesIsAllowAccessControl( @onNull Context context, boolean value)607     private boolean updateSharedPreferencesIsAllowAccessControl(
608             @NonNull Context context, boolean value) {
609         if (mSharedPreferences == null) {
610             plogd("updateSharedPreferencesIsAllowAccessControl: mSharedPreferences is null");
611             initSharedPreferences(context);
612         }
613         if (mSharedPreferences == null) {
614             ploge("updateSharedPreferencesIsAllowAccessControl: mSharedPreferences is null");
615             return false;
616         }
617         try {
618             mSharedPreferences.edit().putBoolean(
619                     CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY,
620                     value).apply();
621             return true;
622         } catch (Exception ex) {
623             ploge("updateSharedPreferencesIsAllowAccessControl error: " + ex);
624             return false;
625         }
626     }
627 
persistLatestSatelliteCommunicationAllowedState()628     private void persistLatestSatelliteCommunicationAllowedState() {
629         if (mSharedPreferences == null) {
630             ploge("persistLatestSatelliteCommunicationAllowedState: mSharedPreferences is null");
631             return;
632         }
633 
634         try {
635             mSharedPreferences.edit().putLong(LATEST_SATELLITE_COMMUNICATION_ALLOWED_SET_TIME_KEY,
636                     mLatestSatelliteCommunicationAllowedSetTime).apply();
637             mSharedPreferences.edit().putBoolean(LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY,
638                     mLatestSatelliteCommunicationAllowed).apply();
639         } catch (Exception ex) {
640             ploge("persistLatestSatelliteCommunicationAllowedState error : " + ex);
641         }
642     }
643 
644     /**
645      * Update country codes and S2CellFile with the new data from ConfigUpdater
646      */
updateSatelliteConfigData(Context context)647     private void updateSatelliteConfigData(Context context) {
648         plogd("updateSatelliteConfigData");
649 
650         SatelliteConfig satelliteConfig = mSatelliteController.getSatelliteConfig();
651         if (satelliteConfig == null) {
652             ploge("satelliteConfig is null");
653             mConfigUpdaterMetricsStats.reportOemAndCarrierConfigError(
654                     SatelliteConstants.CONFIG_UPDATE_RESULT_NO_SATELLITE_DATA);
655             return;
656         }
657 
658         List<String> satelliteCountryCodes = satelliteConfig.getDeviceSatelliteCountryCodes();
659         if (!isValidCountryCodes(satelliteCountryCodes)) {
660             plogd("country codes is invalid");
661             mConfigUpdaterMetricsStats.reportOemConfigError(
662                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_COUNTRY_CODE);
663             return;
664         }
665 
666         Boolean isSatelliteDataForAllowedRegion = satelliteConfig.isSatelliteDataForAllowedRegion();
667         if (isSatelliteDataForAllowedRegion == null) {
668             ploge("Satellite allowed is not configured with country codes");
669             mConfigUpdaterMetricsStats.reportOemConfigError(
670                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE);
671             return;
672         }
673 
674         File configUpdaterS2CellFile = satelliteConfig.getSatelliteS2CellFile(context);
675         if (configUpdaterS2CellFile == null || !configUpdaterS2CellFile.exists()) {
676             plogd("No S2 cell file configured or the file does not exist");
677             mConfigUpdaterMetricsStats.reportOemConfigError(
678                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE);
679             return;
680         }
681 
682         if (!isS2CellFileValid(configUpdaterS2CellFile)) {
683             ploge("The configured S2 cell file is not valid");
684             mConfigUpdaterMetricsStats.reportOemConfigError(
685                     SatelliteConstants.CONFIG_UPDATE_RESULT_DEVICE_DATA_INVALID_S2_CELL_FILE);
686             return;
687         }
688 
689         File localS2CellFile = copySatS2FileToLocalDirectory(configUpdaterS2CellFile);
690         if (localS2CellFile == null || !localS2CellFile.exists()) {
691             ploge("Fail to copy S2 cell file to local directory");
692             mConfigUpdaterMetricsStats.reportOemConfigError(
693                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
694             return;
695         }
696 
697         if (!updateSharedPreferencesCountryCodes(context, satelliteCountryCodes)) {
698             ploge("Fail to copy country coeds into shared preferences");
699             localS2CellFile.delete();
700             mConfigUpdaterMetricsStats.reportOemConfigError(
701                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
702             return;
703         }
704 
705         if (!updateSharedPreferencesIsAllowAccessControl(
706                 context, isSatelliteDataForAllowedRegion.booleanValue())) {
707             ploge("Fail to copy allow access control into shared preferences");
708             localS2CellFile.delete();
709             mConfigUpdaterMetricsStats.reportOemConfigError(
710                     SatelliteConstants.CONFIG_UPDATE_RESULT_IO_ERROR);
711             return;
712         }
713 
714         mSatelliteS2CellFile = localS2CellFile;
715         mSatelliteCountryCodes = satelliteCountryCodes;
716         mIsSatelliteAllowAccessControl = satelliteConfig.isSatelliteDataForAllowedRegion();
717         plogd("Use s2 cell file=" + mSatelliteS2CellFile.getAbsolutePath() + ", country codes="
718                 + String.join(",", mSatelliteCountryCodes)
719                 + ", mIsSatelliteAllowAccessControl=" + mIsSatelliteAllowAccessControl
720                 + " from ConfigUpdater");
721 
722         // Clean up resources so that the new config data will be used when serving new requests
723         cleanupOnDeviceAccessControllerResources();
724 
725         // Clean up cached data based on previous geofence data
726         synchronized (mLock) {
727             plogd("clear mCachedAccessRestrictionMap");
728             mCachedAccessRestrictionMap.clear();
729         }
730 
731         mConfigUpdaterMetricsStats.reportConfigUpdateSuccess();
732     }
733 
loadOverlayConfigs(@onNull Context context)734     private void loadOverlayConfigs(@NonNull Context context) {
735         mSatelliteCountryCodes = getSatelliteCountryCodesFromOverlayConfig(context);
736         mIsSatelliteAllowAccessControl = getSatelliteAccessAllowFromOverlayConfig(context);
737         String satelliteS2CellFileName = getSatelliteS2CellFileFromOverlayConfig(context);
738         mSatelliteS2CellFile = TextUtils.isEmpty(satelliteS2CellFileName)
739                 ? null : new File(satelliteS2CellFileName);
740         if (mSatelliteS2CellFile != null && !mSatelliteS2CellFile.exists()) {
741             ploge("The satellite S2 cell file " + satelliteS2CellFileName + " does not exist");
742             mSatelliteS2CellFile = null;
743         }
744         mLocationFreshDurationNanos = getSatelliteLocationFreshDurationFromOverlayConfig(context);
745         mAccessControllerMetricsStats.setConfigDataSource(
746                 SatelliteConstants.CONFIG_DATA_SOURCE_DEVICE_CONFIG);
747     }
748 
loadConfigUpdaterConfigs()749     private void loadConfigUpdaterConfigs() {
750         if (mSharedPreferences == null) {
751             ploge("loadConfigUpdaterConfigs : mSharedPreferences is null");
752             return;
753         }
754 
755         Set<String> countryCodes =
756                 mSharedPreferences.getStringSet(CONFIG_UPDATER_SATELLITE_COUNTRY_CODES_KEY, null);
757 
758         if (countryCodes == null || countryCodes.isEmpty()) {
759             ploge("config updater country codes are either null or empty");
760             return;
761         }
762 
763         boolean isSatelliteAllowAccessControl =
764                 mSharedPreferences.getBoolean(
765                         CONFIG_UPDATER_SATELLITE_IS_ALLOW_ACCESS_CONTROL_KEY, true);
766 
767         File s2CellFile = getConfigUpdaterSatS2CellFileFromLocalDirectory();
768         if (s2CellFile == null) {
769             ploge("s2CellFile is null");
770             return;
771         }
772 
773         plogd("use config updater config data");
774         mSatelliteS2CellFile = s2CellFile;
775         mSatelliteCountryCodes = countryCodes.stream().collect(Collectors.toList());
776         mIsSatelliteAllowAccessControl = isSatelliteAllowAccessControl;
777         mAccessControllerMetricsStats.setConfigDataSource(
778                 SatelliteConstants.CONFIG_DATA_SOURCE_CONFIG_UPDATER);
779     }
780 
loadCachedLatestSatelliteCommunicationAllowedState()781     private void loadCachedLatestSatelliteCommunicationAllowedState() {
782         if (mSharedPreferences == null) {
783             ploge("loadCachedLatestSatelliteCommunicationAllowedState: mSharedPreferences is null");
784             return;
785         }
786 
787         try {
788             mLatestSatelliteCommunicationAllowedSetTime =
789                     mSharedPreferences.getLong(LATEST_SATELLITE_COMMUNICATION_ALLOWED_SET_TIME_KEY,
790                             0);
791             mLatestSatelliteCommunicationAllowed =
792                     mSharedPreferences.getBoolean(LATEST_SATELLITE_COMMUNICATION_ALLOWED_KEY,
793                             false);
794         } catch (Exception ex) {
795             ploge("loadCachedLatestSatelliteCommunicationAllowedState: ex=" + ex);
796         }
797         plogd("mLatestSatelliteCommunicationAllowedSetTime="
798                 + mLatestSatelliteCommunicationAllowedSetTime
799                 + ", mLatestSatelliteCommunicationAllowed=" + mLatestSatelliteCommunicationAllowed);
800     }
801 
getLocationFreshDurationNanos()802     private long getLocationFreshDurationNanos() {
803         synchronized (mLock) {
804             if (mIsOverlayConfigOverridden) {
805                 return mOverriddenLocationFreshDurationNanos;
806             }
807             return mLocationFreshDurationNanos;
808         }
809     }
810 
811     @NonNull
getSatelliteCountryCodes()812     private List<String> getSatelliteCountryCodes() {
813         synchronized (mLock) {
814             if (mIsOverlayConfigOverridden) {
815                 return mOverriddenSatelliteCountryCodes;
816             }
817             return mSatelliteCountryCodes;
818         }
819     }
820 
821     @Nullable
getSatelliteS2CellFile()822     private File getSatelliteS2CellFile() {
823         synchronized (mLock) {
824             if (mIsOverlayConfigOverridden) {
825                 return mOverriddenSatelliteS2CellFile;
826             }
827             return mSatelliteS2CellFile;
828         }
829     }
830 
isSatelliteAllowAccessControl()831     private boolean isSatelliteAllowAccessControl() {
832         synchronized (mLock) {
833             if (mIsOverlayConfigOverridden) {
834                 return mOverriddenIsSatelliteAllowAccessControl;
835             }
836             return mIsSatelliteAllowAccessControl;
837         }
838     }
839 
handleCmdIsSatelliteAllowedForCurrentLocation( @onNull Pair<Integer, ResultReceiver> requestArguments)840     private void handleCmdIsSatelliteAllowedForCurrentLocation(
841             @NonNull Pair<Integer, ResultReceiver> requestArguments) {
842         synchronized (mLock) {
843             mSatelliteAllowResultReceivers.add(requestArguments.second);
844             if (mSatelliteAllowResultReceivers.size() > 1) {
845                 plogd("requestIsCommunicationAllowedForCurrentLocation is already being "
846                         + "processed");
847                 return;
848             }
849             mTotalCheckingStartTimeMillis = System.currentTimeMillis();
850             mSatelliteController.requestIsSatelliteSupported(
851                     requestArguments.first, mInternalSatelliteSupportedResultReceiver);
852         }
853     }
854 
handleWaitForCurrentLocationTimedOutEvent()855     private void handleWaitForCurrentLocationTimedOutEvent() {
856         plogd("Timed out to wait for current location");
857         synchronized (mLock) {
858             if (mLocationRequestCancellationSignal != null) {
859                 mLocationRequestCancellationSignal.cancel();
860                 mLocationRequestCancellationSignal = null;
861                 onCurrentLocationAvailable(null);
862             } else {
863                 ploge("handleWaitForCurrentLocationTimedOutEvent: "
864                         + "mLocationRequestCancellationSignal is null");
865             }
866         }
867     }
868 
handleIsSatelliteSupportedResult(int resultCode, Bundle resultData)869     private void handleIsSatelliteSupportedResult(int resultCode, Bundle resultData) {
870         plogd("handleIsSatelliteSupportedResult: resultCode=" + resultCode);
871         synchronized (mLock) {
872             if (resultCode == SATELLITE_RESULT_SUCCESS) {
873                 if (resultData.containsKey(KEY_SATELLITE_SUPPORTED)) {
874                     boolean isSatelliteSupported = resultData.getBoolean(KEY_SATELLITE_SUPPORTED);
875                     if (!isSatelliteSupported) {
876                         plogd("Satellite is not supported");
877                         Bundle bundle = new Bundle();
878                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
879                                 false);
880                         sendSatelliteAllowResultToReceivers(resultCode, bundle, false);
881                     } else {
882                         plogd("Satellite is supported");
883                         checkSatelliteAccessRestrictionUsingGPS();
884                     }
885                 } else {
886                     ploge("KEY_SATELLITE_SUPPORTED does not exist.");
887                     sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
888                 }
889             } else {
890                 sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
891             }
892         }
893     }
894 
handleIsSatelliteProvisionedResult(int resultCode, Bundle resultData)895     private void handleIsSatelliteProvisionedResult(int resultCode, Bundle resultData) {
896         plogd("handleIsSatelliteProvisionedResult: resultCode=" + resultCode);
897         synchronized (mLock) {
898             if (resultCode == SATELLITE_RESULT_SUCCESS) {
899                 if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
900                     boolean isSatelliteProvisioned =
901                             resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
902                     if (!isSatelliteProvisioned) {
903                         plogd("Satellite is not provisioned");
904                         Bundle bundle = new Bundle();
905                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
906                                 false);
907                         sendSatelliteAllowResultToReceivers(resultCode, bundle, false);
908                     } else {
909                         plogd("Satellite is provisioned");
910                         checkSatelliteAccessRestrictionUsingGPS();
911                     }
912                 } else {
913                     ploge("KEY_SATELLITE_PROVISIONED does not exist.");
914                     sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
915                 }
916             } else {
917                 sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
918             }
919         }
920     }
921 
sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData, boolean allowed)922     private void sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData,
923             boolean allowed) {
924         if (resultCode == SATELLITE_RESULT_SUCCESS) {
925             updateCurrentSatelliteAllowedState(allowed);
926         }
927         synchronized (mLock) {
928             for (ResultReceiver resultReceiver : mSatelliteAllowResultReceivers) {
929                 resultReceiver.send(resultCode, resultData);
930             }
931             mSatelliteAllowResultReceivers.clear();
932         }
933         reportMetrics(resultCode, allowed);
934     }
935 
936     /**
937      * Telephony-internal logic to verify if satellite access is restricted at the current location.
938      */
checkSatelliteAccessRestrictionForCurrentLocation()939     private void checkSatelliteAccessRestrictionForCurrentLocation() {
940         synchronized (mLock) {
941             List<String> networkCountryIsoList = mCountryDetector.getCurrentNetworkCountryIso();
942             if (!networkCountryIsoList.isEmpty()) {
943                 plogd("Use current network country codes=" + String.join(", ",
944                         networkCountryIsoList));
945 
946                 boolean allowed = isSatelliteAccessAllowedForLocation(networkCountryIsoList);
947                 Bundle bundle = new Bundle();
948                 bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, allowed);
949                 mAccessControllerMetricsStats
950                         .setAccessControlType(
951                                 SatelliteConstants.ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE)
952                         .setCountryCodes(networkCountryIsoList);
953                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, allowed);
954             } else {
955                 if (shouldUseOnDeviceAccessController()) {
956                     // This will be an asynchronous check when it needs to wait for the current
957                     // location from location service
958                     checkSatelliteAccessRestrictionUsingOnDeviceData();
959                 } else {
960                     // This is always a synchronous check
961                     checkSatelliteAccessRestrictionUsingCachedCountryCodes();
962                 }
963             }
964         }
965     }
966 
967     /**
968      * Telephony-internal logic to verify if satellite access is restricted from the location query.
969      */
checkSatelliteAccessRestrictionUsingGPS()970     private void checkSatelliteAccessRestrictionUsingGPS() {
971         logv("checkSatelliteAccessRestrictionUsingGPS:");
972         if (isInEmergency()) {
973             executeLocationQuery();
974         } else {
975             if (mLocationManager.isLocationEnabled()) {
976                 plogd("location query is allowed");
977                 if (isCommunicationAllowedCacheValid()) {
978                     Bundle bundle = new Bundle();
979                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
980                             mLatestSatelliteCommunicationAllowed);
981                     sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
982                             mLatestSatelliteCommunicationAllowed);
983                 } else {
984                     executeLocationQuery();
985                 }
986             } else {
987                 plogv("location query is not allowed");
988                 Bundle bundle = new Bundle();
989                 bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
990                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, false);
991             }
992         }
993     }
994 
995     /**
996      * @return {@code true} if the latest query was executed within the predefined valid duration,
997      * {@code false} otherwise.
998      */
isCommunicationAllowedCacheValid()999     private boolean isCommunicationAllowedCacheValid() {
1000         if (mLatestSatelliteCommunicationAllowedSetTime > 0) {
1001             long currentTime = SystemClock.elapsedRealtimeNanos();
1002             if ((currentTime - mLatestSatelliteCommunicationAllowedSetTime)
1003                     <= ALLOWED_STATE_CACHE_VALID_DURATION_HOURS) {
1004                 logv("isCommunicationAllowedCacheValid: cache is valid");
1005                 return true;
1006             }
1007         }
1008         logv("isCommunicationAllowedCacheValid: cache is expired");
1009         return false;
1010     }
1011 
executeLocationQuery()1012     private void executeLocationQuery() {
1013         plogv("executeLocationQuery");
1014         synchronized (mLock) {
1015             mFreshLastKnownLocation = getFreshLastKnownLocation();
1016             checkSatelliteAccessRestrictionUsingOnDeviceData();
1017         }
1018     }
1019 
1020     /**
1021      * This function synchronously checks if satellite is allowed at current location using cached
1022      * country codes.
1023      */
checkSatelliteAccessRestrictionUsingCachedCountryCodes()1024     private void checkSatelliteAccessRestrictionUsingCachedCountryCodes() {
1025         Pair<String, Long> locationCountryCodeInfo =
1026                 mCountryDetector.getCachedLocationCountryIsoInfo();
1027         Map<String, Long> networkCountryCodeInfoMap =
1028                 mCountryDetector.getCachedNetworkCountryIsoInfo();
1029         List<String> countryCodeList;
1030 
1031         // Check if the cached location country code's timestamp is newer than all cached network
1032         // country codes
1033         if (!TextUtils.isEmpty(locationCountryCodeInfo.first) && isGreaterThanAll(
1034                 locationCountryCodeInfo.second, networkCountryCodeInfoMap.values())) {
1035             // Use cached location country code
1036             countryCodeList = Arrays.asList(locationCountryCodeInfo.first);
1037         } else {
1038             // Use cached network country codes
1039             countryCodeList = networkCountryCodeInfoMap.keySet().stream().toList();
1040         }
1041         plogd("Use cached country codes=" + String.join(", ", countryCodeList));
1042         mAccessControllerMetricsStats.setAccessControlType(
1043                 SatelliteConstants.ACCESS_CONTROL_TYPE_CACHED_COUNTRY_CODE);
1044 
1045         boolean allowed = isSatelliteAccessAllowedForLocation(countryCodeList);
1046         Bundle bundle = new Bundle();
1047         bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, allowed);
1048         sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, allowed);
1049     }
1050 
1051     /**
1052      * This function asynchronously checks if satellite is allowed at the current location using
1053      * on-device data. Asynchronous check happens when it needs to wait for the current location
1054      * from location service.
1055      */
checkSatelliteAccessRestrictionUsingOnDeviceData()1056     private void checkSatelliteAccessRestrictionUsingOnDeviceData() {
1057         mOnDeviceLookupStartTimeMillis = System.currentTimeMillis();
1058         synchronized (mLock) {
1059             plogd("Use on-device data");
1060             if (mFreshLastKnownLocation != null) {
1061                 mAccessControllerMetricsStats.setAccessControlType(
1062                         SatelliteConstants.ACCESS_CONTROL_TYPE_LAST_KNOWN_LOCATION);
1063                 checkSatelliteAccessRestrictionForLocation(mFreshLastKnownLocation);
1064                 mFreshLastKnownLocation = null;
1065             } else {
1066                 Location freshLastKnownLocation = getFreshLastKnownLocation();
1067                 if (freshLastKnownLocation != null) {
1068                     mAccessControllerMetricsStats.setAccessControlType(
1069                             SatelliteConstants.ACCESS_CONTROL_TYPE_LAST_KNOWN_LOCATION);
1070                     checkSatelliteAccessRestrictionForLocation(freshLastKnownLocation);
1071                 } else {
1072                     queryCurrentLocation();
1073                 }
1074             }
1075         }
1076     }
1077 
queryCurrentLocation()1078     private void queryCurrentLocation() {
1079         synchronized (mLock) {
1080             if (mLocationRequestCancellationSignal != null) {
1081                 plogd("Request for current location was already sent to LocationManager");
1082                 return;
1083             }
1084             mLocationRequestCancellationSignal = new CancellationSignal();
1085             mLocationQueryStartTimeMillis = System.currentTimeMillis();
1086             mLocationManager.getCurrentLocation(LocationManager.GPS_PROVIDER,
1087                     new LocationRequest.Builder(0)
1088                             .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
1089                             .setLocationSettingsIgnored(true)
1090                             .build(),
1091                     mLocationRequestCancellationSignal, this::post,
1092                     this::onCurrentLocationAvailable);
1093             startWaitForCurrentLocationTimer();
1094         }
1095     }
1096 
onCurrentLocationAvailable(@ullable Location location)1097     private void onCurrentLocationAvailable(@Nullable Location location) {
1098         plogd("onCurrentLocationAvailable " + (location != null));
1099         synchronized (mLock) {
1100             stopWaitForCurrentLocationTimer();
1101             mLocationRequestCancellationSignal = null;
1102             mAccessControllerMetricsStats.setLocationQueryTime(mLocationQueryStartTimeMillis);
1103             Bundle bundle = new Bundle();
1104             if (location != null) {
1105                 plogd("onCurrentLocationAvailable: lat=" + Rlog.pii(TAG, location.getLatitude())
1106                         + ", long=" + Rlog.pii(TAG, location.getLongitude()));
1107                 if (location.isMock() && !isMockModemAllowed()) {
1108                     logd("location is mock");
1109                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
1110                     sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, false);
1111                     return;
1112                 }
1113                 mAccessControllerMetricsStats.setAccessControlType(
1114                         SatelliteConstants.ACCESS_CONTROL_TYPE_CURRENT_LOCATION);
1115                 checkSatelliteAccessRestrictionForLocation(location);
1116             } else {
1117                 plogd("current location is not available");
1118                 if (isCommunicationAllowedCacheValid()) {
1119                     plogd("onCurrentLocationAvailable: cache is still valid, using it");
1120                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
1121                             mLatestSatelliteCommunicationAllowed);
1122                     sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
1123                             mLatestSatelliteCommunicationAllowed);
1124                 } else {
1125                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
1126                     sendSatelliteAllowResultToReceivers(
1127                             SATELLITE_RESULT_LOCATION_NOT_AVAILABLE, bundle, false);
1128                 }
1129             }
1130         }
1131     }
1132 
checkSatelliteAccessRestrictionForLocation(@onNull Location location)1133     private void checkSatelliteAccessRestrictionForLocation(@NonNull Location location) {
1134         synchronized (mLock) {
1135             try {
1136                 SatelliteOnDeviceAccessController.LocationToken locationToken =
1137                         SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
1138                                 location.getLatitude(),
1139                                 location.getLongitude(), mS2Level);
1140                 boolean satelliteAllowed;
1141                 if (mCachedAccessRestrictionMap.containsKey(locationToken)) {
1142                     satelliteAllowed = mCachedAccessRestrictionMap.get(locationToken);
1143                 } else {
1144                     if (!initSatelliteOnDeviceAccessController()) {
1145                         ploge("Failed to init SatelliteOnDeviceAccessController");
1146                         Bundle bundle = new Bundle();
1147                         bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
1148                         sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
1149                                 false);
1150                         return;
1151                     }
1152                     satelliteAllowed = mSatelliteOnDeviceAccessController
1153                             .isSatCommunicationAllowedAtLocation(locationToken);
1154                     updateCachedAccessRestrictionMap(locationToken, satelliteAllowed);
1155                 }
1156                 mAccessControllerMetricsStats.setOnDeviceLookupTime(mOnDeviceLookupStartTimeMillis);
1157                 Bundle bundle = new Bundle();
1158                 bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, satelliteAllowed);
1159                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
1160                         satelliteAllowed);
1161                 mLatestSatelliteCommunicationAllowed = satelliteAllowed;
1162                 mLatestSatelliteCommunicationAllowedSetTime = SystemClock.elapsedRealtimeNanos();
1163                 persistLatestSatelliteCommunicationAllowedState();
1164             } catch (Exception ex) {
1165                 ploge("checkSatelliteAccessRestrictionForLocation: ex=" + ex);
1166                 reportAnomaly(UUID_ON_DEVICE_LOOKUP_EXCEPTION,
1167                         "On-device satellite lookup exception");
1168                 Bundle bundle = new Bundle();
1169                 if (isCommunicationAllowedCacheValid()) {
1170                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
1171                             mLatestSatelliteCommunicationAllowed);
1172                     plogd("checkSatelliteAccessRestrictionForLocation: cache is still valid, "
1173                             + "using it");
1174                 } else {
1175                     bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
1176                 }
1177                 sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
1178                         mLatestSatelliteCommunicationAllowed);
1179             }
1180         }
1181     }
1182 
updateCachedAccessRestrictionMap( @onNull SatelliteOnDeviceAccessController.LocationToken locationToken, boolean satelliteAllowed)1183     private void updateCachedAccessRestrictionMap(
1184             @NonNull SatelliteOnDeviceAccessController.LocationToken locationToken,
1185             boolean satelliteAllowed) {
1186         synchronized (mLock) {
1187             mCachedAccessRestrictionMap.put(locationToken, satelliteAllowed);
1188         }
1189     }
1190 
isGreaterThanAll( long comparedItem, @NonNull Collection<Long> itemCollection)1191     private boolean isGreaterThanAll(
1192             long comparedItem, @NonNull Collection<Long> itemCollection) {
1193         for (long item : itemCollection) {
1194             if (comparedItem <= item) return false;
1195         }
1196         return true;
1197     }
1198 
isSatelliteAccessAllowedForLocation( @onNull List<String> networkCountryIsoList)1199     private boolean isSatelliteAccessAllowedForLocation(
1200             @NonNull List<String> networkCountryIsoList) {
1201         if (isSatelliteAllowAccessControl()) {
1202             // The current country is unidentified, we're uncertain and thus returning false
1203             if (networkCountryIsoList.isEmpty()) {
1204                 return false;
1205             }
1206 
1207             // In case of allowed list, satellite is allowed if all country codes are be in the
1208             // allowed list
1209             return getSatelliteCountryCodes().containsAll(networkCountryIsoList);
1210         } else {
1211             // No country is barred, thus returning true
1212             if (getSatelliteCountryCodes().isEmpty()) {
1213                 return true;
1214             }
1215 
1216             // The current country is unidentified, we're uncertain and thus returning false
1217             if (networkCountryIsoList.isEmpty()) {
1218                 return false;
1219             }
1220 
1221             // In case of disallowed list, if any country code is in the list, satellite will be
1222             // disallowed
1223             for (String countryCode : networkCountryIsoList) {
1224                 if (getSatelliteCountryCodes().contains(countryCode)) {
1225                     return false;
1226                 }
1227             }
1228             return true;
1229         }
1230     }
1231 
shouldUseOnDeviceAccessController()1232     private boolean shouldUseOnDeviceAccessController() {
1233         if (getSatelliteS2CellFile() == null) {
1234             return false;
1235         }
1236 
1237         if (isInEmergency() || mLocationManager.isLocationEnabled()) {
1238             return true;
1239         }
1240 
1241         Location freshLastKnownLocation = getFreshLastKnownLocation();
1242         if (freshLastKnownLocation != null) {
1243             synchronized (mLock) {
1244                 mFreshLastKnownLocation = freshLastKnownLocation;
1245             }
1246             return true;
1247         } else {
1248             synchronized (mLock) {
1249                 mFreshLastKnownLocation = null;
1250             }
1251         }
1252         return false;
1253     }
1254 
1255     @Nullable
getFreshLastKnownLocation()1256     private Location getFreshLastKnownLocation() {
1257         Location lastKnownLocation = getLastKnownLocation();
1258         if (lastKnownLocation != null) {
1259             long lastKnownLocationAge =
1260                     getElapsedRealtimeNanos() - lastKnownLocation.getElapsedRealtimeNanos();
1261             if (lastKnownLocationAge <= getLocationFreshDurationNanos()) {
1262                 return lastKnownLocation;
1263             }
1264         }
1265         return null;
1266     }
1267 
isInEmergency()1268     private boolean isInEmergency() {
1269         // Check if emergency call is ongoing
1270         if (mTelecomManager.isInEmergencyCall()) {
1271             plogd("In emergency call");
1272             return true;
1273         }
1274 
1275         // Check if the device is in emergency callback mode
1276         for (Phone phone : PhoneFactory.getPhones()) {
1277             if (phone.isInEcm()) {
1278                 plogd("In emergency callback mode");
1279                 return true;
1280             }
1281         }
1282 
1283         // Check if satellite is in emergency mode
1284         if (mSatelliteController.isInEmergencyMode()) {
1285             plogd("In satellite emergency mode");
1286             return true;
1287         }
1288         return false;
1289     }
1290 
1291     @Nullable
getLastKnownLocation()1292     private Location getLastKnownLocation() {
1293         Location result = null;
1294         for (String provider : mLocationManager.getProviders(true)) {
1295             Location location = mLocationManager.getLastKnownLocation(provider);
1296             if (location != null && (result == null
1297                     || result.getElapsedRealtimeNanos() < location.getElapsedRealtimeNanos())) {
1298                 result = location;
1299             }
1300         }
1301 
1302         if (result == null || isMockModemAllowed()) {
1303             return result;
1304         }
1305 
1306         return result.isMock() ? null : result;
1307     }
1308 
initSharedPreferences(@onNull Context context)1309     private void initSharedPreferences(@NonNull Context context) {
1310         try {
1311             mSharedPreferences =
1312                     context.getSharedPreferences(SATELLITE_SHARED_PREF, Context.MODE_PRIVATE);
1313         } catch (Exception e) {
1314             ploge("Cannot get default shared preferences: " + e);
1315         }
1316     }
1317 
1318     /**
1319      * @return {@code true} if successfully initialize the {@link SatelliteOnDeviceAccessController}
1320      * instance, {@code false} otherwise.
1321      * @throws IllegalStateException in case of getting any exception in creating the
1322      *                               {@link SatelliteOnDeviceAccessController} instance and the
1323      *                               device is using a user build.
1324      */
initSatelliteOnDeviceAccessController()1325     private boolean initSatelliteOnDeviceAccessController() throws IllegalStateException {
1326         synchronized (mLock) {
1327             if (getSatelliteS2CellFile() == null) return false;
1328 
1329             // mSatelliteOnDeviceAccessController was already initialized successfully
1330             if (mSatelliteOnDeviceAccessController != null) {
1331                 restartKeepOnDeviceAccessControllerResourcesTimer();
1332                 return true;
1333             }
1334 
1335             try {
1336                 mSatelliteOnDeviceAccessController =
1337                         SatelliteOnDeviceAccessController.create(getSatelliteS2CellFile());
1338                 restartKeepOnDeviceAccessControllerResourcesTimer();
1339                 mS2Level = mSatelliteOnDeviceAccessController.getS2Level();
1340                 plogd("mS2Level=" + mS2Level);
1341             } catch (Exception ex) {
1342                 ploge("Got exception in creating an instance of SatelliteOnDeviceAccessController,"
1343                         + " ex=" + ex + ", sat s2 file="
1344                         + getSatelliteS2CellFile().getAbsolutePath());
1345                 reportAnomaly(UUID_CREATE_ON_DEVICE_ACCESS_CONTROLLER_EXCEPTION,
1346                         "Exception in creating on-device satellite access controller");
1347                 mSatelliteOnDeviceAccessController = null;
1348                 if (!mIsOverlayConfigOverridden) {
1349                     mSatelliteS2CellFile = null;
1350                 }
1351                 return false;
1352             }
1353             return true;
1354         }
1355     }
1356 
cleanupOnDeviceAccessControllerResources()1357     private void cleanupOnDeviceAccessControllerResources() {
1358         synchronized (mLock) {
1359             plogd("cleanupOnDeviceAccessControllerResources="
1360                     + (mSatelliteOnDeviceAccessController != null));
1361             if (mSatelliteOnDeviceAccessController != null) {
1362                 try {
1363                     mSatelliteOnDeviceAccessController.close();
1364                 } catch (Exception ex) {
1365                     ploge("cleanupOnDeviceAccessControllerResources: ex=" + ex);
1366                 }
1367                 mSatelliteOnDeviceAccessController = null;
1368                 stopKeepOnDeviceAccessControllerResourcesTimer();
1369             }
1370         }
1371     }
1372 
getSatelliteAccessAllowFromOverlayConfig(@onNull Context context)1373     private static boolean getSatelliteAccessAllowFromOverlayConfig(@NonNull Context context) {
1374         Boolean accessAllowed = null;
1375         try {
1376             accessAllowed = context.getResources().getBoolean(
1377                     com.android.internal.R.bool.config_oem_enabled_satellite_access_allow);
1378         } catch (Resources.NotFoundException ex) {
1379             loge("getSatelliteAccessAllowFromOverlayConfig: got ex=" + ex);
1380         }
1381         if (accessAllowed == null && isMockModemAllowed()) {
1382             logd("getSatelliteAccessAllowFromOverlayConfig: Read "
1383                     + "config_oem_enabled_satellite_access_allow from device config");
1384             accessAllowed = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
1385                     "config_oem_enabled_satellite_access_allow", DEFAULT_SATELLITE_ACCESS_ALLOW);
1386         }
1387         if (accessAllowed == null) {
1388             logd("Use default satellite access allow=true control");
1389             accessAllowed = true;
1390         }
1391         return accessAllowed;
1392     }
1393 
1394     @Nullable
getSatelliteS2CellFileFromOverlayConfig(@onNull Context context)1395     private static String getSatelliteS2CellFileFromOverlayConfig(@NonNull Context context) {
1396         String s2CellFile = null;
1397         try {
1398             s2CellFile = context.getResources().getString(
1399                     com.android.internal.R.string.config_oem_enabled_satellite_s2cell_file);
1400         } catch (Resources.NotFoundException ex) {
1401             loge("getSatelliteS2CellFileFromOverlayConfig: got ex=" + ex);
1402         }
1403         if (TextUtils.isEmpty(s2CellFile) && isMockModemAllowed()) {
1404             logd("getSatelliteS2CellFileFromOverlayConfig: Read "
1405                     + "config_oem_enabled_satellite_s2cell_file from device config");
1406             s2CellFile = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
1407                     "config_oem_enabled_satellite_s2cell_file", null);
1408         }
1409         logd("s2CellFile=" + s2CellFile);
1410         return s2CellFile;
1411     }
1412 
1413     @NonNull
getSatelliteCountryCodesFromOverlayConfig( @onNull Context context)1414     private static List<String> getSatelliteCountryCodesFromOverlayConfig(
1415             @NonNull Context context) {
1416         String[] countryCodes = readStringArrayFromOverlayConfig(context,
1417                 com.android.internal.R.array.config_oem_enabled_satellite_country_codes);
1418         if (countryCodes.length == 0 && isMockModemAllowed()) {
1419             logd("getSatelliteCountryCodesFromOverlayConfig: Read "
1420                     + "config_oem_enabled_satellite_country_codes from device config");
1421             String countryCodesStr = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
1422                     "config_oem_enabled_satellite_country_codes", "");
1423             countryCodes = countryCodesStr.split(",");
1424         }
1425         return Arrays.stream(countryCodes)
1426                 .map(x -> x.toUpperCase(Locale.US))
1427                 .collect(Collectors.toList());
1428     }
1429 
1430     @NonNull
readStringArrayFromOverlayConfig( @onNull Context context, @ArrayRes int id)1431     private static String[] readStringArrayFromOverlayConfig(
1432             @NonNull Context context, @ArrayRes int id) {
1433         String[] strArray = null;
1434         try {
1435             strArray = context.getResources().getStringArray(id);
1436         } catch (Resources.NotFoundException ex) {
1437             loge("readStringArrayFromOverlayConfig: id= " + id + ", ex=" + ex);
1438         }
1439         if (strArray == null) {
1440             strArray = new String[0];
1441         }
1442         return strArray;
1443     }
1444 
getSatelliteLocationFreshDurationFromOverlayConfig( @onNull Context context)1445     private static long getSatelliteLocationFreshDurationFromOverlayConfig(
1446             @NonNull Context context) {
1447         Integer freshDuration = null;
1448         try {
1449             freshDuration = context.getResources().getInteger(com.android.internal.R.integer
1450                     .config_oem_enabled_satellite_location_fresh_duration);
1451         } catch (Resources.NotFoundException ex) {
1452             loge("getSatelliteLocationFreshDurationFromOverlayConfig: got ex=" + ex);
1453         }
1454         if (freshDuration == null && isMockModemAllowed()) {
1455             logd("getSatelliteLocationFreshDurationFromOverlayConfig: Read "
1456                     + "config_oem_enabled_satellite_location_fresh_duration from device config");
1457             freshDuration = DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
1458                     "config_oem_enabled_satellite_location_fresh_duration",
1459                     DEFAULT_LOCATION_FRESH_DURATION_SECONDS);
1460         }
1461         if (freshDuration == null) {
1462             logd("Use default satellite location fresh duration="
1463                     + DEFAULT_LOCATION_FRESH_DURATION_SECONDS);
1464             freshDuration = DEFAULT_LOCATION_FRESH_DURATION_SECONDS;
1465         }
1466         return TimeUnit.SECONDS.toNanos(freshDuration);
1467     }
1468 
startWaitForCurrentLocationTimer()1469     private void startWaitForCurrentLocationTimer() {
1470         synchronized (mLock) {
1471             if (hasMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT)) {
1472                 plogw("WaitForCurrentLocationTimer is already started");
1473                 removeMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
1474             }
1475             sendEmptyMessageDelayed(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT,
1476                     WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS);
1477         }
1478     }
1479 
stopWaitForCurrentLocationTimer()1480     private void stopWaitForCurrentLocationTimer() {
1481         synchronized (mLock) {
1482             removeMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
1483         }
1484     }
1485 
restartKeepOnDeviceAccessControllerResourcesTimer()1486     private void restartKeepOnDeviceAccessControllerResourcesTimer() {
1487         synchronized (mLock) {
1488             if (hasMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT)) {
1489                 plogd("KeepOnDeviceAccessControllerResourcesTimer is already started. "
1490                         + "Restarting it...");
1491                 removeMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
1492             }
1493             sendEmptyMessageDelayed(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT,
1494                     KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS);
1495         }
1496     }
1497 
stopKeepOnDeviceAccessControllerResourcesTimer()1498     private void stopKeepOnDeviceAccessControllerResourcesTimer() {
1499         synchronized (mLock) {
1500             removeMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
1501         }
1502     }
1503 
reportAnomaly(@onNull String uuid, @NonNull String log)1504     private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
1505         ploge(log);
1506         AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
1507     }
1508 
isMockModemAllowed()1509     private static boolean isMockModemAllowed() {
1510         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)
1511                 || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
1512     }
1513 
1514     /**
1515      * Posts the specified command to be executed on the main thread and returns immediately.
1516      *
1517      * @param command  command to be executed on the main thread
1518      * @param argument additional parameters required to perform of the operation
1519      */
sendRequestAsync(int command, @NonNull Object argument)1520     private void sendRequestAsync(int command, @NonNull Object argument) {
1521         Message msg = this.obtainMessage(command, argument);
1522         msg.sendToTarget();
1523     }
1524 
1525     /**
1526      * Registers for the satellite communication allowed state changed.
1527      *
1528      * @param subId    The subId of the subscription to register for the satellite communication
1529      *                 allowed state changed.
1530      * @param callback The callback to handle the satellite communication allowed state changed
1531      *                 event.
1532      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
1533      */
1534     @SatelliteManager.SatelliteResult
registerForCommunicationAllowedStateChanged(int subId, @NonNull ISatelliteCommunicationAllowedStateCallback callback)1535     public int registerForCommunicationAllowedStateChanged(int subId,
1536             @NonNull ISatelliteCommunicationAllowedStateCallback callback) {
1537         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
1538             plogd("registerForCommunicationAllowedStateChanged: oemEnabledSatelliteFlag is "
1539                     + "disabled");
1540             return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
1541         }
1542 
1543         mSatelliteCommunicationAllowedStateChangedListeners.put(callback.asBinder(), callback);
1544         return SATELLITE_RESULT_SUCCESS;
1545     }
1546 
1547     /**
1548      * Unregisters for the satellite communication allowed state changed.
1549      * If callback was not registered before, the request will be ignored.
1550      *
1551      * @param subId    The subId of the subscription to unregister for the satellite communication
1552      *                 allowed state changed.
1553      * @param callback The callback that was passed to
1554      *                 {@link #registerForCommunicationAllowedStateChanged(int,
1555      *                 ISatelliteCommunicationAllowedStateCallback)}.
1556      */
unregisterForCommunicationAllowedStateChanged( int subId, @NonNull ISatelliteCommunicationAllowedStateCallback callback)1557     public void unregisterForCommunicationAllowedStateChanged(
1558             int subId, @NonNull ISatelliteCommunicationAllowedStateCallback callback) {
1559         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
1560             plogd("unregisterForCommunicationAllowedStateChanged: "
1561                     + "oemEnabledSatelliteFlag is disabled");
1562             return;
1563         }
1564 
1565         mSatelliteCommunicationAllowedStateChangedListeners.remove(callback.asBinder());
1566     }
1567 
1568     /**
1569      * This API can be used by only CTS to set the cache whether satellite communication is allowed.
1570      *
1571      * @param state a state indicates whether satellite access allowed state should be cached and
1572      * the allowed state.
1573      * @return {@code true} if the setting is successful, {@code false} otherwise.
1574      */
setIsSatelliteCommunicationAllowedForCurrentLocationCache(String state)1575     public boolean setIsSatelliteCommunicationAllowedForCurrentLocationCache(String state) {
1576         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
1577             logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
1578                     + "oemEnabledSatelliteFlag is disabled");
1579             return false;
1580         }
1581 
1582         if (!isMockModemAllowed()) {
1583             logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
1584                     + "mock modem not allowed.");
1585             return false;
1586         }
1587 
1588         logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: state=" + state);
1589 
1590         synchronized (mSatelliteCommunicationAllowStateLock) {
1591             if ("cache_allowed".equalsIgnoreCase(state)) {
1592                 mLatestSatelliteCommunicationAllowedSetTime = SystemClock.elapsedRealtimeNanos();
1593                 mLatestSatelliteCommunicationAllowed = true;
1594                 mCurrentSatelliteAllowedState = true;
1595             } else if ("cache_clear_and_not_allowed".equalsIgnoreCase(state)) {
1596                 mLatestSatelliteCommunicationAllowedSetTime = 0;
1597                 mLatestSatelliteCommunicationAllowed = false;
1598                 mCurrentSatelliteAllowedState = false;
1599                 persistLatestSatelliteCommunicationAllowedState();
1600             } else if ("clear_cache_only".equalsIgnoreCase(state)) {
1601                 mLatestSatelliteCommunicationAllowedSetTime = 0;
1602                 mLatestSatelliteCommunicationAllowed = false;
1603                 persistLatestSatelliteCommunicationAllowedState();
1604             } else {
1605                 loge("setIsSatelliteCommunicationAllowedForCurrentLocationCache: invalid state="
1606                         + state);
1607                 return false;
1608             }
1609         }
1610         return true;
1611     }
1612 
notifySatelliteCommunicationAllowedStateChanged(boolean allowState)1613     private void notifySatelliteCommunicationAllowedStateChanged(boolean allowState) {
1614         plogd("notifySatelliteCommunicationAllowedStateChanged: allowState=" + allowState);
1615 
1616         List<ISatelliteCommunicationAllowedStateCallback> deadCallersList = new ArrayList<>();
1617         mSatelliteCommunicationAllowedStateChangedListeners.values().forEach(listener -> {
1618             try {
1619                 listener.onSatelliteCommunicationAllowedStateChanged(allowState);
1620             } catch (RemoteException e) {
1621                 plogd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
1622                 deadCallersList.add(listener);
1623             }
1624         });
1625         deadCallersList.forEach(listener -> {
1626             mSatelliteCommunicationAllowedStateChangedListeners.remove(listener.asBinder());
1627         });
1628     }
1629 
reportMetrics(int resultCode, boolean allowed)1630     private void reportMetrics(int resultCode, boolean allowed) {
1631         if (resultCode == SATELLITE_RESULT_SUCCESS) {
1632             mControllerMetricsStats.reportAllowedSatelliteAccessCount(allowed);
1633         } else {
1634             mControllerMetricsStats.reportFailedSatelliteAccessCheckCount();
1635         }
1636 
1637         mAccessControllerMetricsStats
1638                 .setLocationQueryTime(mLocationQueryStartTimeMillis)
1639                 .setTotalCheckingTime(mTotalCheckingStartTimeMillis)
1640                 .setIsAllowed(allowed)
1641                 .setIsEmergency(isInEmergency())
1642                 .setResult(resultCode)
1643                 .reportAccessControllerMetrics();
1644         mLocationQueryStartTimeMillis = 0;
1645         mOnDeviceLookupStartTimeMillis = 0;
1646         mTotalCheckingStartTimeMillis = 0;
1647     }
1648 
logd(@onNull String log)1649     private static void logd(@NonNull String log) {
1650         Rlog.d(TAG, log);
1651     }
1652 
logw(@onNull String log)1653     private static void logw(@NonNull String log) {
1654         Rlog.w(TAG, log);
1655     }
1656 
loge(@onNull String log)1657     private static void loge(@NonNull String log) {
1658         Rlog.e(TAG, log);
1659     }
1660 
logv(@onNull String log)1661     private static void logv(@NonNull String log) {
1662         Rlog.v(TAG, log);
1663     }
1664 
isSatellitePersistentLoggingEnabled( @onNull Context context, @NonNull FeatureFlags featureFlags)1665     private boolean isSatellitePersistentLoggingEnabled(
1666             @NonNull Context context, @NonNull FeatureFlags featureFlags) {
1667         if (featureFlags.satellitePersistentLogging()) {
1668             return true;
1669         }
1670         try {
1671             return context.getResources().getBoolean(
1672                     R.bool.config_dropboxmanager_persistent_logging_enabled);
1673         } catch (RuntimeException e) {
1674             return false;
1675         }
1676     }
1677 
plogv(@onNull String log)1678     private void plogv(@NonNull String log) {
1679         Rlog.v(TAG, log);
1680         if (mPersistentLogger != null) {
1681             mPersistentLogger.debug(TAG, log);
1682         }
1683     }
1684 
plogd(@onNull String log)1685     private void plogd(@NonNull String log) {
1686         Rlog.d(TAG, log);
1687         if (mPersistentLogger != null) {
1688             mPersistentLogger.debug(TAG, log);
1689         }
1690     }
1691 
plogw(@onNull String log)1692     private void plogw(@NonNull String log) {
1693         Rlog.w(TAG, log);
1694         if (mPersistentLogger != null) {
1695             mPersistentLogger.warn(TAG, log);
1696         }
1697     }
1698 
ploge(@onNull String log)1699     private void ploge(@NonNull String log) {
1700         Rlog.e(TAG, log);
1701         if (mPersistentLogger != null) {
1702             mPersistentLogger.error(TAG, log);
1703         }
1704     }
1705 }
1706