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