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.google.android.telephony.satellite; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Intent; 22 import android.os.Binder; 23 import android.os.IBinder; 24 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer; 25 import android.telephony.satellite.stub.ISatelliteListener; 26 import android.telephony.satellite.stub.NTRadioTechnology; 27 import android.telephony.satellite.stub.PointingInfo; 28 import android.telephony.satellite.stub.SatelliteCapabilities; 29 import android.telephony.satellite.stub.SatelliteDatagram; 30 import android.telephony.satellite.stub.SatelliteError; 31 import android.telephony.satellite.stub.SatelliteImplBase; 32 import android.telephony.satellite.stub.SatelliteModemState; 33 import android.telephony.satellite.stub.SatelliteService; 34 35 import com.android.internal.telephony.IBooleanConsumer; 36 import com.android.internal.telephony.IIntegerConsumer; 37 import com.android.internal.util.FunctionalUtils; 38 import com.android.telephony.Rlog; 39 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Set; 46 import java.util.concurrent.Executor; 47 48 public class CFSatelliteService extends SatelliteImplBase { 49 private static final String TAG = "CFSatelliteService"; 50 51 // Hardcoded values below 52 private static final int SATELLITE_ALWAYS_VISIBLE = 0; 53 /** SatelliteCapabilities constant indicating that the radio technology is proprietary. */ 54 private static final int[] SUPPORTED_RADIO_TECHNOLOGIES = 55 new int[] {NTRadioTechnology.PROPRIETARY}; 56 /** SatelliteCapabilities constant indicating that pointing to satellite is required. */ 57 private static final boolean POINTING_TO_SATELLITE_REQUIRED = true; 58 /** SatelliteCapabilities constant indicating the maximum number of characters per datagram. */ 59 private static final int MAX_BYTES_PER_DATAGRAM = 339; 60 61 @NonNull private final Map<IBinder, ISatelliteListener> mListeners = new HashMap<>(); 62 63 private boolean mIsCommunicationAllowedInLocation; 64 private boolean mIsEnabled; 65 private boolean mIsProvisioned; 66 private boolean mIsSupported; 67 private int mModemState; 68 69 /** 70 * Create CFSatelliteService using the Executor specified for methods being called from 71 * the framework. 72 * 73 * @param executor The executor for the framework to use when executing satellite methods. 74 */ CFSatelliteService(@onNull Executor executor)75 public CFSatelliteService(@NonNull Executor executor) { 76 super(executor); 77 mIsCommunicationAllowedInLocation = true; 78 mIsEnabled = false; 79 mIsProvisioned = false; 80 mIsSupported = true; 81 mModemState = SatelliteModemState.SATELLITE_MODEM_STATE_OFF; 82 } 83 84 /** 85 * Zero-argument constructor to prevent service binding exception. 86 */ CFSatelliteService()87 public CFSatelliteService() { 88 this(Runnable::run); 89 } 90 91 @Override onBind(Intent intent)92 public IBinder onBind(Intent intent) { 93 if (SatelliteService.SERVICE_INTERFACE.equals(intent.getAction())) { 94 logd("CFSatelliteService bound"); 95 return new CFSatelliteService().getBinder(); 96 } 97 return null; 98 } 99 100 @Override onCreate()101 public void onCreate() { 102 super.onCreate(); 103 logd("onCreate"); 104 } 105 106 @Override onDestroy()107 public void onDestroy() { 108 super.onDestroy(); 109 logd("onDestroy"); 110 } 111 112 @Override setSatelliteListener(@onNull ISatelliteListener listener)113 public void setSatelliteListener(@NonNull ISatelliteListener listener) { 114 logd("setSatelliteListener"); 115 mListeners.put(listener.asBinder(), listener); 116 } 117 118 @Override requestSatelliteListeningEnabled(boolean enable, int timeout, @NonNull IIntegerConsumer errorCallback)119 public void requestSatelliteListeningEnabled(boolean enable, int timeout, 120 @NonNull IIntegerConsumer errorCallback) { 121 logd("requestSatelliteListeningEnabled"); 122 if (!verifySatelliteModemState(errorCallback)) { 123 return; 124 } 125 if (enable) { 126 updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_LISTENING); 127 } else { 128 updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_IDLE); 129 } 130 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 131 } 132 133 @Override requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, @NonNull IIntegerConsumer errorCallback)134 public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, 135 @NonNull IIntegerConsumer errorCallback) { 136 logd("requestSatelliteEnabled"); 137 if (enableSatellite) { 138 enableSatellite(errorCallback); 139 } else { 140 disableSatellite(errorCallback); 141 } 142 } 143 enableSatellite(@onNull IIntegerConsumer errorCallback)144 private void enableSatellite(@NonNull IIntegerConsumer errorCallback) { 145 mIsEnabled = true; 146 updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_IDLE); 147 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 148 } 149 disableSatellite(@onNull IIntegerConsumer errorCallback)150 private void disableSatellite(@NonNull IIntegerConsumer errorCallback) { 151 mIsEnabled = false; 152 updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_OFF); 153 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 154 } 155 156 @Override requestIsSatelliteEnabled(@onNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback)157 public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer errorCallback, 158 @NonNull IBooleanConsumer callback) { 159 logd("requestIsSatelliteEnabled"); 160 runWithExecutor(() -> callback.accept(mIsEnabled)); 161 } 162 163 @Override requestIsSatelliteSupported(@onNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback)164 public void requestIsSatelliteSupported(@NonNull IIntegerConsumer errorCallback, 165 @NonNull IBooleanConsumer callback) { 166 logd("requestIsSatelliteSupported"); 167 runWithExecutor(() -> callback.accept(mIsSupported)); 168 } 169 170 @Override requestSatelliteCapabilities(@onNull IIntegerConsumer errorCallback, @NonNull ISatelliteCapabilitiesConsumer callback)171 public void requestSatelliteCapabilities(@NonNull IIntegerConsumer errorCallback, 172 @NonNull ISatelliteCapabilitiesConsumer callback) { 173 logd("requestSatelliteCapabilities"); 174 SatelliteCapabilities capabilities = new SatelliteCapabilities(); 175 capabilities.supportedRadioTechnologies = SUPPORTED_RADIO_TECHNOLOGIES; 176 capabilities.isPointingRequired = POINTING_TO_SATELLITE_REQUIRED; 177 capabilities.maxBytesPerOutgoingDatagram = MAX_BYTES_PER_DATAGRAM; 178 runWithExecutor(() -> callback.accept(capabilities)); 179 } 180 181 @Override startSendingSatellitePointingInfo(@onNull IIntegerConsumer errorCallback)182 public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) { 183 logd("startSendingSatellitePointingInfo"); 184 if (!verifySatelliteModemState(errorCallback)) { 185 return; 186 } 187 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 188 } 189 190 @Override stopSendingSatellitePointingInfo(@onNull IIntegerConsumer errorCallback)191 public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) { 192 logd("stopSendingSatellitePointingInfo"); 193 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 194 } 195 196 @Override provisionSatelliteService(@onNull String token, @NonNull byte[] provisionData, @NonNull IIntegerConsumer errorCallback)197 public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData, 198 @NonNull IIntegerConsumer errorCallback) { 199 logd("provisionSatelliteService"); 200 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 201 updateSatelliteProvisionState(true); 202 } 203 204 @Override deprovisionSatelliteService(@onNull String token, @NonNull IIntegerConsumer errorCallback)205 public void deprovisionSatelliteService(@NonNull String token, 206 @NonNull IIntegerConsumer errorCallback) { 207 logd("deprovisionSatelliteService"); 208 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 209 updateSatelliteProvisionState(false); 210 } 211 212 @Override requestIsSatelliteProvisioned(@onNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback)213 public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer errorCallback, 214 @NonNull IBooleanConsumer callback) { 215 logd("requestIsSatelliteProvisioned"); 216 runWithExecutor(() -> callback.accept(mIsProvisioned)); 217 } 218 219 @Override pollPendingSatelliteDatagrams(@onNull IIntegerConsumer errorCallback)220 public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer errorCallback) { 221 logd("pollPendingSatelliteDatagrams"); 222 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 223 } 224 225 @Override sendSatelliteDatagram(@onNull SatelliteDatagram datagram, boolean isEmergency, @NonNull IIntegerConsumer errorCallback)226 public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency, 227 @NonNull IIntegerConsumer errorCallback) { 228 logd("sendSatelliteDatagram"); 229 runWithExecutor(() -> errorCallback.accept(SatelliteError.ERROR_NONE)); 230 } 231 232 @Override requestSatelliteModemState(@onNull IIntegerConsumer errorCallback, @NonNull IIntegerConsumer callback)233 public void requestSatelliteModemState(@NonNull IIntegerConsumer errorCallback, 234 @NonNull IIntegerConsumer callback) { 235 logd("requestSatelliteModemState"); 236 runWithExecutor(() -> callback.accept(mModemState)); 237 } 238 239 @Override requestIsSatelliteCommunicationAllowedForCurrentLocation( @onNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback)240 public void requestIsSatelliteCommunicationAllowedForCurrentLocation( 241 @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) { 242 logd("requestIsSatelliteCommunicationAllowedForCurrentLocation"); 243 if (mIsCommunicationAllowedInLocation) { 244 runWithExecutor(() -> callback.accept(true)); 245 } else { 246 runWithExecutor(() -> callback.accept(false)); 247 } 248 } 249 250 @Override requestTimeForNextSatelliteVisibility(@onNull IIntegerConsumer errorCallback, @NonNull IIntegerConsumer callback)251 public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer errorCallback, 252 @NonNull IIntegerConsumer callback) { 253 logd("requestTimeForNextSatelliteVisibility"); 254 runWithExecutor(() -> callback.accept(SATELLITE_ALWAYS_VISIBLE)); 255 } 256 257 /** 258 * Helper method to verify that the satellite modem is properly configured to receive requests. 259 * 260 * @param errorCallback The callback to notify of any errors preventing satellite requests. 261 * @return {@code true} if the satellite modem is configured to receive requests and 262 * {@code false} if it is not. 263 */ verifySatelliteModemState(@onNull IIntegerConsumer errorCallback)264 private boolean verifySatelliteModemState(@NonNull IIntegerConsumer errorCallback) { 265 if (!mIsSupported) { 266 runWithExecutor(() -> errorCallback.accept(SatelliteError.REQUEST_NOT_SUPPORTED)); 267 return false; 268 } 269 if (!mIsProvisioned) { 270 runWithExecutor(() -> errorCallback.accept(SatelliteError.SERVICE_NOT_PROVISIONED)); 271 return false; 272 } 273 if (!mIsEnabled) { 274 runWithExecutor(() -> errorCallback.accept(SatelliteError.INVALID_MODEM_STATE)); 275 return false; 276 } 277 return true; 278 } 279 280 /** 281 * Update the satellite modem state and notify listeners if it changed. 282 * 283 * @param modemState The {@link SatelliteModemState} to update. 284 */ updateSatelliteModemState(int modemState)285 private void updateSatelliteModemState(int modemState) { 286 if (modemState == mModemState) { 287 return; 288 } 289 logd("updateSatelliteModemState: mListeners.size=" + mListeners.size()); 290 mListeners.values().forEach(listener -> runWithExecutor(() -> 291 listener.onSatelliteModemStateChanged(modemState))); 292 mModemState = modemState; 293 } 294 295 /** 296 * Update the satellite provision state and notify listeners if it changed. 297 * 298 * @param isProvisioned {@code true} if the satellite is currently provisioned and 299 * {@code false} if it is not. 300 */ updateSatelliteProvisionState(boolean isProvisioned)301 private void updateSatelliteProvisionState(boolean isProvisioned) { 302 if (isProvisioned == mIsProvisioned) { 303 return; 304 } 305 logd("updateSatelliteProvisionState: mListeners.size=" + mListeners.size()); 306 mIsProvisioned = isProvisioned; 307 mListeners.values().forEach(listener -> runWithExecutor(() -> 308 listener.onSatelliteProvisionStateChanged(mIsProvisioned))); 309 } 310 311 /** 312 * Execute the given runnable using the executor that this service was created with. 313 * 314 * @param r A runnable that can throw an exception. 315 */ runWithExecutor(@onNull FunctionalUtils.ThrowingRunnable r)316 private void runWithExecutor(@NonNull FunctionalUtils.ThrowingRunnable r) { 317 mExecutor.execute(() -> Binder.withCleanCallingIdentity(r)); 318 } 319 320 /** 321 * Log the message to the radio buffer with {@code DEBUG} priority. 322 * 323 * @param log The message to log. 324 */ logd(@onNull String log)325 private static void logd(@NonNull String log) { 326 Rlog.d(TAG, log); 327 } 328 } 329