1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony.mockmodem;
18 
19 import android.hardware.radio.RadioError;
20 import android.hardware.radio.RadioIndicationType;
21 import android.hardware.radio.RadioResponseInfo;
22 import android.hardware.radio.config.IRadioConfig;
23 import android.hardware.radio.config.IRadioConfigIndication;
24 import android.hardware.radio.config.IRadioConfigResponse;
25 import android.hardware.radio.config.PhoneCapability;
26 import android.hardware.radio.config.SimSlotStatus;
27 import android.hardware.radio.config.SlotPortMapping;
28 import android.os.AsyncResult;
29 import android.os.Build;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 public class IRadioConfigImpl extends IRadioConfig.Stub {
36     private static final String TAG = "MRCFG";
37 
38     private final MockModemService mService;
39     private IRadioConfigResponse mRadioConfigResponse;
40     private IRadioConfigIndication mRadioConfigIndication;
41     private MockModemConfigInterface mMockModemConfigInterface;
42     private final Object mCacheUpdateMutex;
43     private final Handler mHandler;
44     private int mSubId;
45     private String mTag;
46 
47     // ***** Events
48     static final int EVENT_NUM_OF_LIVE_MODEM_CHANGED = 1;
49     static final int EVENT_PHONE_CAPABILITY_CHANGED = 2;
50     static final int EVENT_SIM_SLOT_STATUS_CHANGED = 3;
51     static final int EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED = 4;
52 
53     // ***** Cache of modem attributes/status
54     private int mSlotNum = 1;
55     private byte mNumOfLiveModems = 1;
56     private PhoneCapability mPhoneCapability = new PhoneCapability();
57     private SimSlotStatus[] mSimSlotStatus;
58     private int[] mEnabledLogicalSlots = {};
59 
60     MockCentralizedNetworkAgent mMockCentralizedNetworkAgent;
61 
IRadioConfigImpl( MockModemService service, MockModemConfigInterface configInterface, MockCentralizedNetworkAgent centralizedNetworkAgent, int instanceId)62     public IRadioConfigImpl(
63             MockModemService service, MockModemConfigInterface configInterface,
64             MockCentralizedNetworkAgent centralizedNetworkAgent, int instanceId) {
65         mTag = TAG + "-" + instanceId;
66         Log.d(mTag, "Instantiated");
67 
68         this.mService = service;
69         mMockModemConfigInterface = configInterface;
70         mSlotNum = mService.getNumPhysicalSlots();
71         mSimSlotStatus = new SimSlotStatus[mSlotNum];
72         mCacheUpdateMutex = new Object();
73         mHandler = new IRadioConfigHandler();
74         mSubId = instanceId;
75         mMockCentralizedNetworkAgent = centralizedNetworkAgent;
76 
77         // Register events
78         mMockModemConfigInterface.registerForNumOfLiveModemChanged(
79                 mSubId, mHandler, EVENT_NUM_OF_LIVE_MODEM_CHANGED, null);
80         mMockModemConfigInterface.registerForPhoneCapabilityChanged(
81                 mSubId, mHandler, EVENT_PHONE_CAPABILITY_CHANGED, null);
82         mMockModemConfigInterface.registerForSimSlotStatusChanged(
83                 mSubId, mHandler, EVENT_SIM_SLOT_STATUS_CHANGED, null);
84         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
85             mMockModemConfigInterface.registerForSimultaneousCallingSupportStatusChanged(
86                     mSubId, mHandler, EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED, null);
87         }
88     }
89 
90     /** Handler class to handle callbacks */
91     private final class IRadioConfigHandler extends Handler {
92         @Override
handleMessage(Message msg)93         public void handleMessage(Message msg) {
94             AsyncResult ar;
95             synchronized (mCacheUpdateMutex) {
96                 switch (msg.what) {
97                     case EVENT_NUM_OF_LIVE_MODEM_CHANGED:
98                         Log.d(mTag, "Received EVENT_NUM_OF_LIVE_MODEM_CHANGED");
99                         ar = (AsyncResult) msg.obj;
100                         if (ar != null && ar.exception == null) {
101                             mNumOfLiveModems = (byte) ar.result;
102                             Log.i(mTag, "Number of live modem: " + mNumOfLiveModems);
103                         } else {
104                             Log.e(mTag, msg.what + " failure. Exception: " + ar.exception);
105                         }
106                         break;
107                     case EVENT_PHONE_CAPABILITY_CHANGED:
108                         Log.d(mTag, "Received EVENT_PHONE_CAPABILITY_CHANGED");
109                         ar = (AsyncResult) msg.obj;
110                         if (ar != null && ar.exception == null) {
111                             mPhoneCapability = (PhoneCapability) ar.result;
112                             Log.i(mTag, "Phone capability: " + mPhoneCapability);
113                         } else {
114                             Log.e(mTag, msg.what + " failure. Exception: " + ar.exception);
115                         }
116                         break;
117                     case EVENT_SIM_SLOT_STATUS_CHANGED:
118                         Log.d(mTag, "Received EVENT_SIM_SLOT_STATUS_CHANGED");
119                         ar = (AsyncResult) msg.obj;
120                         if (ar != null && ar.exception == null) {
121                             mSimSlotStatus = (SimSlotStatus[]) ar.result;
122                             for (int i = 0; i < mSlotNum; i++) {
123                                 Log.i(mTag, "Sim slot status: " + mSimSlotStatus[i]);
124                             }
125                             unsolSimSlotsStatusChanged();
126                         } else {
127                             Log.e(mTag, msg.what + " failure. Exception: " + ar.exception);
128                         }
129                         break;
130                     case EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED:
131                         Log.i(mTag, "Received EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED");
132                         ar = (AsyncResult) msg.obj;
133                         if (ar != null && ar.exception == null) {
134                             mEnabledLogicalSlots = (int[]) ar.result;
135                             if (mEnabledLogicalSlots != null) {
136                                 for (int enabledLogicalSlot : mEnabledLogicalSlots) {
137                                     Log.i(mTag, "Enabled logical slot = "
138                                             + enabledLogicalSlot);
139                                 }
140                             } else {
141                                 Log.i(mTag, "mEnabledLogicalSlots is null");
142                             }
143                             unsolSimultaneousCallingSupportChanged();
144                         } else {
145                             Log.e(mTag, msg.what + " failure. Exception: " + ar.exception);
146                         }
147                         break;
148                 }
149             }
150         }
151     }
152 
153     // Implementation of IRadioConfig functions
154     @Override
getHalDeviceCapabilities(int serial)155     public void getHalDeviceCapabilities(int serial) {
156         Log.d(mTag, "getHalDeviceCapabilities");
157 
158         boolean modemReducedFeatureSet1 = false;
159 
160         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
161         try {
162             mRadioConfigResponse.getHalDeviceCapabilitiesResponse(rsp, modemReducedFeatureSet1);
163         } catch (RemoteException ex) {
164             Log.e(
165                     mTag,
166                     "Failed to invoke getHalDeviceCapabilitiesResponse from AIDL. Exception" + ex);
167         }
168     }
169 
170     @Override
getNumOfLiveModems(int serial)171     public void getNumOfLiveModems(int serial) {
172         Log.d(mTag, "getNumOfLiveModems");
173         byte numoflivemodem;
174 
175         synchronized (mCacheUpdateMutex) {
176             numoflivemodem = mNumOfLiveModems;
177         }
178 
179         RadioResponseInfo rsp = mService.makeSolRsp(serial);
180         try {
181             mRadioConfigResponse.getNumOfLiveModemsResponse(rsp, numoflivemodem);
182         } catch (RemoteException ex) {
183             Log.e(mTag, "Failed to invoke getNumOfLiveModemsResponse from AIDL. Exception" + ex);
184         }
185     }
186 
187     @Override
getPhoneCapability(int serial)188     public void getPhoneCapability(int serial) {
189         Log.d(mTag, "getPhoneCapability");
190         PhoneCapability phoneCapability;
191 
192         synchronized (mCacheUpdateMutex) {
193             phoneCapability = mPhoneCapability;
194         }
195 
196         RadioResponseInfo rsp = mService.makeSolRsp(serial);
197         try {
198             mRadioConfigResponse.getPhoneCapabilityResponse(rsp, phoneCapability);
199         } catch (RemoteException ex) {
200             Log.e(mTag, "Failed to invoke getPhoneCapabilityResponse from AIDL. Exception" + ex);
201         }
202     }
203 
204     @Override
getSimultaneousCallingSupport(int serial)205     public void getSimultaneousCallingSupport(int serial) {
206         Log.d(mTag, "getSimultaneousCallingSupport");
207         int[] enabledLogicalSlots;
208 
209         synchronized (mCacheUpdateMutex) {
210             enabledLogicalSlots = mEnabledLogicalSlots;
211         }
212 
213         RadioResponseInfo rsp = mService.makeSolRsp(serial);
214         try {
215             mRadioConfigResponse.getSimultaneousCallingSupportResponse(rsp, enabledLogicalSlots);
216         } catch (RemoteException ex) {
217             Log.e(mTag, "Failed to invoke getSimultaneousCallingSupportResponse from AIDL. "
218                     + "Exception" + ex);
219         }
220     }
221 
222     @Override
getSimSlotsStatus(int serial)223     public void getSimSlotsStatus(int serial) {
224         Log.d(mTag, "getSimSlotsStatus");
225         SimSlotStatus[] slotStatus;
226 
227         synchronized (mCacheUpdateMutex) {
228             if (mSlotNum < 1) {
229                 Log.d(mTag, "No slot information is retured.");
230                 slotStatus = null;
231             } else {
232                 slotStatus = mSimSlotStatus;
233             }
234         }
235 
236         RadioResponseInfo rsp = mService.makeSolRsp(serial);
237         try {
238             mRadioConfigResponse.getSimSlotsStatusResponse(rsp, slotStatus);
239         } catch (RemoteException ex) {
240             Log.e(mTag, "Failed to invoke getSimSlotsStatusResponse from AIDL. Exception" + ex);
241         }
242     }
243 
244     @Override
setNumOfLiveModems(int serial, byte numOfLiveModems)245     public void setNumOfLiveModems(int serial, byte numOfLiveModems) {
246         Log.d(mTag, "setNumOfLiveModems");
247         // TODO: cache value
248 
249         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
250         try {
251             mRadioConfigResponse.setNumOfLiveModemsResponse(rsp);
252         } catch (RemoteException ex) {
253             Log.e(mTag, "Failed to invoke setNumOfLiveModemsResponse from AIDL. Exception" + ex);
254         }
255     }
256 
257     @Override
setPreferredDataModem(int serial, byte modemId)258     public void setPreferredDataModem(int serial, byte modemId) {
259         Log.d(mTag, "setPreferredDataModem");
260         // TODO: cache value
261 
262         RadioResponseInfo rsp = mService.makeSolRsp(serial);
263         try {
264             mMockCentralizedNetworkAgent.setPreferredDataPhone((int) modemId);
265             mRadioConfigResponse.setPreferredDataModemResponse(rsp);
266         } catch (RemoteException ex) {
267             Log.e(mTag, "Failed to invoke setPreferredDataModemResponse from AIDL. Exception" + ex);
268         }
269     }
270 
271     @Override
setResponseFunctions( IRadioConfigResponse radioConfigResponse, IRadioConfigIndication radioConfigIndication)272     public void setResponseFunctions(
273             IRadioConfigResponse radioConfigResponse,
274             IRadioConfigIndication radioConfigIndication) {
275         Log.d(mTag, "setResponseFunctions");
276         mRadioConfigResponse = radioConfigResponse;
277         mRadioConfigIndication = radioConfigIndication;
278         mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY);
279     }
280 
281     @Override
setSimSlotsMapping(int serial, SlotPortMapping[] slotMap)282     public void setSimSlotsMapping(int serial, SlotPortMapping[] slotMap) {
283         Log.d(mTag, "setSimSlotsMapping");
284         // TODO: cache value
285 
286         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
287         try {
288             mRadioConfigResponse.setSimSlotsMappingResponse(rsp);
289         } catch (RemoteException ex) {
290             Log.e(mTag, "Failed to invoke setSimSlotsMappingResponse from AIDL. Exception" + ex);
291         }
292     }
293 
unsolSimSlotsStatusChanged()294     public void unsolSimSlotsStatusChanged() {
295         Log.d(mTag, "unsolSimSlotsStatusChanged");
296         SimSlotStatus[] slotStatus;
297 
298         if (mRadioConfigIndication != null) {
299             synchronized (mCacheUpdateMutex) {
300                 if (mSlotNum < 1) {
301                     Log.d(mTag, "No slot information is retured.");
302                     slotStatus = null;
303                 } else {
304                     slotStatus = mSimSlotStatus;
305                 }
306             }
307 
308             try {
309                 mRadioConfigIndication.simSlotsStatusChanged(
310                         RadioIndicationType.UNSOLICITED, slotStatus);
311             } catch (RemoteException ex) {
312                 Log.e(mTag, "Failed to invoke simSlotsStatusChanged from AIDL. Exception" + ex);
313             }
314         } else {
315             Log.e(mTag, "null mRadioConfigIndication");
316         }
317     }
318 
unsolSimultaneousCallingSupportChanged()319     public void unsolSimultaneousCallingSupportChanged() {
320         Log.d(mTag, "unsolSimultaneousCallingSupportChanged");
321         int[] enabledLogicalSlots;
322 
323         if (mRadioConfigIndication != null) {
324             synchronized (mCacheUpdateMutex) {
325                 if (mSlotNum < 1) {
326                     Log.d(mTag, "No slot information is retured.");
327                     enabledLogicalSlots = null;
328                 } else {
329                     enabledLogicalSlots = mEnabledLogicalSlots;
330                 }
331             }
332 
333             try {
334                 mRadioConfigIndication.onSimultaneousCallingSupportChanged(enabledLogicalSlots);
335             } catch (RemoteException ex) {
336                 Log.e(mTag, "Failed to invoke onSimultaneousCallingSupportChanged from AIDL."
337                         + " Exception" + ex);
338             }
339         } else {
340             Log.e(mTag, "null mRadioConfigIndication");
341         }
342     }
343 
344     @Override
getInterfaceHash()345     public String getInterfaceHash() {
346         return IRadioConfig.HASH;
347     }
348 
349     @Override
getInterfaceVersion()350     public int getInterfaceVersion() {
351         return IRadioConfig.VERSION;
352     }
353 }
354