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