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 static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_DEFAULT;
20 
21 import android.app.Service;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.hardware.radio.RadioError;
25 import android.hardware.radio.RadioResponseInfo;
26 import android.hardware.radio.RadioResponseType;
27 import android.os.Binder;
28 import android.os.IBinder;
29 import android.telephony.TelephonyManager;
30 import android.util.Log;
31 import android.util.Pair;
32 
33 import androidx.test.platform.app.InstrumentationRegistry;
34 
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.TimeUnit;
37 
38 public class MockModemService extends Service {
39     private static final String TAG = "MockModemService";
40     private static final String RESOURCE_PACKAGE_NAME = "android";
41 
42     public static final int TEST_TIMEOUT_MS = 30000;
43     public static final String IRADIOCONFIG_INTERFACE = "android.telephony.mockmodem.iradioconfig";
44     public static final String IRADIOMODEM_INTERFACE = "android.telephony.mockmodem.iradiomodem";
45     public static final String IRADIOSIM_INTERFACE = "android.telephony.mockmodem.iradiosim";
46     public static final String IRADIONETWORK_INTERFACE =
47             "android.telephony.mockmodem.iradionetwork";
48     public static final String IRADIODATA_INTERFACE = "android.telephony.mockmodem.iradiodata";
49     public static final String IRADIOMESSAGING_INTERFACE =
50             "android.telephony.mockmodem.iradiomessaging";
51     public static final String IRADIOVOICE_INTERFACE = "android.telephony.mockmodem.iradiovoice";
52     public static final String IRADIOIMS_INTERFACE = "android.telephony.mockmodem.iradioims";
53     public static final String PHONE_ID = "phone_id";
54 
55     private static MockModemConfigInterface sMockModemConfigInterface;
56     private static MockCentralizedNetworkAgent sMockCentralizedNetworkAgent;
57     private static IRadioConfigImpl sIRadioConfigImpl;
58     private static IRadioModemImpl[] sIRadioModemImpl;
59     private static IRadioSimImpl[] sIRadioSimImpl;
60     private static IRadioNetworkImpl[] sIRadioNetworkImpl;
61     private static IRadioDataImpl[] sIRadioDataImpl;
62     private static IRadioMessagingImpl[] sIRadioMessagingImpl;
63     private static IRadioVoiceImpl[] sIRadioVoiceImpl;
64     private static IRadioImsImpl[] sIRadioImsImpl;
65 
66     public static final byte PHONE_ID_0 = 0x00;
67     public static final byte PHONE_ID_1 = 0x01;
68     public static final byte PHONE_ID_2 = 0x02;
69     public static final byte MAX_PHONE_NUM = 3;
70 
71     public static final int LATCH_MOCK_MODEM_SERVICE_READY = 0;
72     public static final int LATCH_RADIO_INTERFACES_READY = 1;
73     public static final int LATCH_MAX = 2;
74 
75     private static final int IRADIO_CONFIG_INTERFACE_NUMBER = 1;
76     private static final int IRADIO_INTERFACE_NUMBER = 6;
77 
78     private Context mContext;
79     private TelephonyManager mTelephonyManager;
80     private int mNumOfSim;
81     private int mNumOfPhone;
82     private static final int DEFAULT_SUB_ID = 0;
83 
84     private Object mLock;
85     protected static CountDownLatch[] sLatches;
86     private LocalBinder mBinder;
87 
88     // For local access of this Service.
89     class LocalBinder extends Binder {
getService()90         MockModemService getService() {
91             return MockModemService.this;
92         }
93     }
94 
95     @Override
onCreate()96     public void onCreate() {
97         Log.d(TAG, "Mock Modem Service Created");
98 
99         mContext = InstrumentationRegistry.getInstrumentation().getContext();
100         mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
101         mNumOfSim = getNumPhysicalSlots();
102         mNumOfPhone = mTelephonyManager.getActiveModemCount();
103         Log.d(TAG, "Support number of phone = " + mNumOfPhone + ", number of SIM = " + mNumOfSim);
104 
105         if (mNumOfPhone > MAX_PHONE_NUM) {
106             mNumOfPhone = MAX_PHONE_NUM;
107         }
108 
109         // Number of physical Sim slot should be equals to or greater than number of phone.
110         if (mNumOfSim < mNumOfPhone) {
111             mNumOfSim = mNumOfPhone;
112         }
113 
114         mLock = new Object();
115         sLatches = new CountDownLatch[LATCH_MAX];
116 
117         for (int i = 0; i < LATCH_MAX; i++) {
118             sLatches[i] = new CountDownLatch(1);
119             if (i == LATCH_RADIO_INTERFACES_READY) {
120                 int radioServiceSupportedNumber = 0;
121                 int radioInterfaceNumber = 0;
122 
123                 try {
124                     for (int j = TelephonyManager.HAL_SERVICE_DATA;
125                             j <= TelephonyManager.HAL_SERVICE_IMS;
126                             j++) {
127                         Pair<Integer, Integer> halVersion = mTelephonyManager.getHalVersion(j);
128                         if (halVersion.first == -2 && halVersion.second == -2) {
129                             Log.d(TAG, "Service: " + j + " unsupported");
130                         } else {
131                             radioServiceSupportedNumber++;
132                         }
133                     }
134                 } catch (NoSuchMethodError | IllegalStateException e) {
135                     Log.e(TAG, "Use the default number of interfaces - " + IRADIO_INTERFACE_NUMBER);
136                     radioServiceSupportedNumber = IRADIO_INTERFACE_NUMBER;
137                 }
138 
139                 radioInterfaceNumber =
140                         IRADIO_CONFIG_INTERFACE_NUMBER + mNumOfPhone * radioServiceSupportedNumber;
141                 sLatches[i] = new CountDownLatch(radioInterfaceNumber);
142             } else {
143                 sLatches[i] = new CountDownLatch(1);
144             }
145         }
146 
147         sMockModemConfigInterface = new MockModemConfigBase(mContext, mNumOfSim, mNumOfPhone);
148         sIRadioConfigImpl = new IRadioConfigImpl(this,
149             sMockModemConfigInterface,
150             sMockCentralizedNetworkAgent,
151             PHONE_ID_0);
152         sIRadioModemImpl = new IRadioModemImpl[mNumOfPhone];
153         sIRadioSimImpl = new IRadioSimImpl[mNumOfPhone];
154         sIRadioNetworkImpl = new IRadioNetworkImpl[mNumOfPhone];
155         sIRadioDataImpl = new IRadioDataImpl[mNumOfPhone];
156         sIRadioMessagingImpl = new IRadioMessagingImpl[mNumOfPhone];
157         sIRadioVoiceImpl = new IRadioVoiceImpl[mNumOfPhone];
158         sIRadioImsImpl = new IRadioImsImpl[mNumOfPhone];
159         for (int i = 0; i < mNumOfPhone; i++) {
160             sIRadioModemImpl[i] = new IRadioModemImpl(this, sMockModemConfigInterface, i);
161             sIRadioSimImpl[i] = new IRadioSimImpl(this, sMockModemConfigInterface, i);
162             sIRadioNetworkImpl[i] =
163                     new IRadioNetworkImpl(this, mContext, sMockModemConfigInterface, i);
164             sIRadioDataImpl[i] = new IRadioDataImpl(this, mContext, sMockModemConfigInterface, i);
165             sIRadioMessagingImpl[i] = new IRadioMessagingImpl(this, sMockModemConfigInterface, i);
166             sIRadioVoiceImpl[i] = new IRadioVoiceImpl(this, sMockModemConfigInterface, i);
167             sIRadioImsImpl[i] = new IRadioImsImpl(this, sMockModemConfigInterface, i);
168         }
169         try {
170             sMockCentralizedNetworkAgent.setNetworkAgentInfo(sIRadioDataImpl, mNumOfPhone);
171         } catch (Exception e) {
172             Log.e(TAG, "Exception error: " + e);
173         }
174         mBinder = new LocalBinder();
175     }
176 
177     @Override
onDestroy()178     public void onDestroy() {
179         Log.d(TAG, "Mock Modem Service on distory");
180         if (sMockModemConfigInterface != null) {
181             sMockModemConfigInterface.destroy();
182         }
183         super.onDestroy();
184     }
185 
186     @Override
onBind(Intent intent)187     public IBinder onBind(Intent intent) {
188 
189         String action = intent.getAction();
190         if (action == null) {
191             countDownLatch(LATCH_MOCK_MODEM_SERVICE_READY);
192             Log.i(TAG, "onBind-Local");
193             return mBinder;
194         }
195 
196         byte phoneId = intent.getByteExtra(PHONE_ID, PHONE_ID_0);
197 
198         if (phoneId >= MAX_PHONE_NUM) {
199             Log.e(TAG, "Not suuport for phone " + phoneId);
200             return null;
201         }
202 
203         if (action.startsWith(IRADIOCONFIG_INTERFACE)) {
204             Log.i(TAG, "onBind-IRadioConfig " + phoneId);
205             return sIRadioConfigImpl;
206         } else if (action.startsWith(IRADIOMODEM_INTERFACE)) {
207             Log.i(TAG, "onBind-IRadioModem " + phoneId);
208             return sIRadioModemImpl[phoneId];
209         } else if (action.startsWith(IRADIOSIM_INTERFACE)) {
210             Log.i(TAG, "onBind-IRadioSim " + phoneId);
211             return sIRadioSimImpl[phoneId];
212         } else if (action.startsWith(IRADIONETWORK_INTERFACE)) {
213             Log.i(TAG, "onBind-IRadioNetwork " + phoneId);
214             return sIRadioNetworkImpl[phoneId];
215         } else if (action.startsWith(IRADIODATA_INTERFACE)) {
216             Log.i(TAG, "onBind-IRadioData " + phoneId);
217             return sIRadioDataImpl[phoneId];
218         } else if (action.startsWith(IRADIOMESSAGING_INTERFACE)) {
219             Log.i(TAG, "onBind-IRadioMessaging " + phoneId);
220             return sIRadioMessagingImpl[phoneId];
221         } else if (action.startsWith(IRADIOVOICE_INTERFACE)) {
222             Log.i(TAG, "onBind-IRadioVoice " + phoneId);
223             return sIRadioVoiceImpl[phoneId];
224         } else if (action.startsWith(IRADIOIMS_INTERFACE)) {
225             Log.i(TAG, "onBind-IRadioIms " + phoneId);
226             return sIRadioImsImpl[phoneId];
227         }
228 
229         return null;
230     }
231 
waitForLatchCountdown(int latchIndex)232     public boolean waitForLatchCountdown(int latchIndex) {
233         boolean complete = false;
234         try {
235             CountDownLatch latch;
236             synchronized (mLock) {
237                 latch = sLatches[latchIndex];
238             }
239             complete = latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
240         } catch (InterruptedException e) {
241         }
242         synchronized (mLock) {
243             sLatches[latchIndex] = new CountDownLatch(1);
244         }
245         return complete;
246     }
247 
waitForLatchCountdown(int latchIndex, long waitMs)248     public boolean waitForLatchCountdown(int latchIndex, long waitMs) {
249         boolean complete = false;
250         try {
251             CountDownLatch latch;
252             synchronized (mLock) {
253                 latch = sLatches[latchIndex];
254             }
255             complete = latch.await(waitMs, TimeUnit.MILLISECONDS);
256         } catch (InterruptedException e) {
257         }
258         synchronized (mLock) {
259             sLatches[latchIndex] = new CountDownLatch(1);
260         }
261         return complete;
262     }
263 
countDownLatch(int latchIndex)264     public void countDownLatch(int latchIndex) {
265         synchronized (mLock) {
266             sLatches[latchIndex].countDown();
267         }
268     }
269 
getNumPhysicalSlots()270     public int getNumPhysicalSlots() {
271         int numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN;
272         int resourceId =
273                 mContext.getResources()
274                         .getIdentifier(
275                                 "config_num_physical_slots", "integer", RESOURCE_PACKAGE_NAME);
276 
277         if (resourceId > 0) {
278             numPhysicalSlots = mContext.getResources().getInteger(resourceId);
279         } else {
280             Log.d(TAG, "Fail to get the resource Id, using default: " + numPhysicalSlots);
281         }
282 
283         if (numPhysicalSlots > MockSimService.MOCK_SIM_SLOT_MAX) {
284             Log.d(
285                     TAG,
286                     "Number of physical Slot ("
287                             + numPhysicalSlots
288                             + ") > mock sim slot support. Reset to max number supported ("
289                             + MockSimService.MOCK_SIM_SLOT_MAX
290                             + ").");
291             numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MAX;
292         } else if (numPhysicalSlots < MockSimService.MOCK_SIM_SLOT_MIN) {
293             Log.d(
294                     TAG,
295                     "Number of physical Slot ("
296                             + numPhysicalSlots
297                             + ") < mock sim slot support. Reset to min number supported ("
298                             + MockSimService.MOCK_SIM_SLOT_MIN
299                             + ").");
300             numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN;
301         }
302 
303         return numPhysicalSlots;
304     }
305 
makeSolRsp(int serial)306     public RadioResponseInfo makeSolRsp(int serial) {
307         RadioResponseInfo rspInfo = new RadioResponseInfo();
308         rspInfo.type = RadioResponseType.SOLICITED;
309         rspInfo.serial = serial;
310         rspInfo.error = RadioError.NONE;
311 
312         return rspInfo;
313     }
314 
makeSolRsp(int serial, int error)315     public RadioResponseInfo makeSolRsp(int serial, int error) {
316         RadioResponseInfo rspInfo = new RadioResponseInfo();
317         rspInfo.type = RadioResponseType.SOLICITED;
318         rspInfo.serial = serial;
319         rspInfo.error = error;
320 
321         return rspInfo;
322     }
323 
initialize(int[] simprofiles)324     public boolean initialize(int[] simprofiles) {
325         Log.d(TAG, "initialize for num of Sim = " + simprofiles.length);
326         boolean result = true;
327 
328         // Sync mock modem status between modules
329         for (int i = 0; i < mNumOfPhone; i++) {
330             // Set initial SIM profiles
331             if (simprofiles != null && i < simprofiles.length) {
332                 result = sMockModemConfigInterface.changeSimProfile(i, simprofiles[i], TAG);
333             } else {
334                 result =
335                         sMockModemConfigInterface.changeSimProfile(
336                                 i, MOCK_SIM_PROFILE_ID_DEFAULT, TAG);
337             }
338 
339             // Sync modem configurations to radio modules
340             sMockModemConfigInterface.notifyAllRegistrantNotifications();
341 
342             // Connect to telephony framework
343             sIRadioModemImpl[i].rilConnected();
344         }
345 
346         return result;
347     }
348 
getMockModemConfigInterface()349     public MockModemConfigInterface getMockModemConfigInterface() {
350         return sMockModemConfigInterface;
351     }
352 
getIRadioConfig()353     public IRadioConfigImpl getIRadioConfig() {
354         return sIRadioConfigImpl;
355     }
356 
getIRadioModem()357     public IRadioModemImpl getIRadioModem() {
358         return getIRadioModem(PHONE_ID_0);
359     }
360 
getIRadioModem(byte phoneId)361     public IRadioModemImpl getIRadioModem(byte phoneId) {
362         return sIRadioModemImpl[phoneId];
363     }
364 
getIRadioSim()365     public IRadioSimImpl getIRadioSim() {
366         return getIRadioSim(PHONE_ID_0);
367     }
368 
getIRadioSim(byte phoneId)369     public IRadioSimImpl getIRadioSim(byte phoneId) {
370         return sIRadioSimImpl[phoneId];
371     }
372 
getIRadioNetwork()373     public IRadioNetworkImpl getIRadioNetwork() {
374         return getIRadioNetwork(PHONE_ID_0);
375     }
376 
getIRadioNetwork(byte phoneId)377     public IRadioNetworkImpl getIRadioNetwork(byte phoneId) {
378         return sIRadioNetworkImpl[phoneId];
379     }
380 
getIRadioVoice()381     public IRadioVoiceImpl getIRadioVoice() {
382         return getIRadioVoice(PHONE_ID_0);
383     }
384 
getIRadioVoice(byte phoneId)385     public IRadioVoiceImpl getIRadioVoice(byte phoneId) {
386         return sIRadioVoiceImpl[phoneId];
387     }
388 
getIRadioMessaging()389     public IRadioMessagingImpl getIRadioMessaging() {
390         return getIRadioMessaging(PHONE_ID_0);
391     }
392 
getIRadioMessaging(byte phoneId)393     public IRadioMessagingImpl getIRadioMessaging(byte phoneId) {
394         return sIRadioMessagingImpl[phoneId];
395     }
396 
getIRadioData()397     public IRadioDataImpl getIRadioData() {
398         return getIRadioData(PHONE_ID_0);
399     }
400 
getIRadioData(byte phoneId)401     public IRadioDataImpl getIRadioData(byte phoneId) {
402         return sIRadioDataImpl[phoneId];
403     }
404 
getIRadioIms()405     public IRadioImsImpl getIRadioIms() {
406         return getIRadioIms(PHONE_ID_0);
407     }
408 
getIRadioIms(byte phoneId)409     public IRadioImsImpl getIRadioIms(byte phoneId) {
410         return sIRadioImsImpl[phoneId];
411     }
412 
getActiveMockModemCount()413     public int getActiveMockModemCount() {
414         return mNumOfPhone;
415     }
416 }
417