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 com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
20 
21 import android.hardware.radio.RadioError;
22 import android.hardware.radio.RadioIndicationType;
23 import android.hardware.radio.RadioResponseInfo;
24 import android.hardware.radio.modem.IRadioModem;
25 import android.hardware.radio.modem.IRadioModemIndication;
26 import android.hardware.radio.modem.IRadioModemResponse;
27 import android.hardware.radio.modem.ImeiInfo;
28 import android.hardware.radio.modem.RadioState;
29 import android.os.AsyncResult;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.TimeUnit;
37 
38 public class IRadioModemImpl extends IRadioModem.Stub {
39     private static final String TAG = "MRMDM";
40 
41     private final MockModemService mService;
42     private IRadioModemResponse mRadioModemResponse;
43     private IRadioModemIndication mRadioModemIndication;
44 
45     private int mForceRadioPowerError = -1;
46 
47     private MockModemConfigInterface mMockModemConfigInterface;
48     private final Object mCacheUpdateMutex;
49     private final Handler mHandler;
50     private int mSubId;
51     private String mTag;
52 
53     // ***** Events
54     static final int EVENT_BASEBAND_VERSION_CHANGED = 1;
55     static final int EVENT_DEVICE_IDENTITY_CHANGED = 2;
56     static final int EVENT_RADIO_STATE_CHANGED = 3;
57     static final int EVENT_DEVICE_IMEI_INFO_CHANGED = 4;
58     private static final int LATCH_MAX = 2;
59     private final CountDownLatch[] mLatches = new CountDownLatch[LATCH_MAX];
60     // ***** Cache of modem attributes/status
61     private String mBasebandVer;
62     private String mImei;
63     private String mImeiSv;
64     private String mEsn;
65     private String mMeid;
66     private int mImeiType;
67     private int mRadioState;
68 
IRadioModemImpl( MockModemService service, MockModemConfigInterface configInterface, int instanceId)69     public IRadioModemImpl(
70             MockModemService service, MockModemConfigInterface configInterface, int instanceId) {
71         mTag = TAG + "-" + instanceId;
72         Log.d(mTag, "Instantiated");
73 
74         this.mService = service;
75         mMockModemConfigInterface = configInterface;
76         mCacheUpdateMutex = new Object();
77         mHandler = new IRadioModemHandler();
78         mSubId = instanceId;
79 
80         // Register events
81         mMockModemConfigInterface.registerForBasebandVersionChanged(
82                 mSubId, mHandler, EVENT_BASEBAND_VERSION_CHANGED, null);
83         mMockModemConfigInterface.registerForDeviceIdentityChanged(
84                 mSubId, mHandler, EVENT_DEVICE_IDENTITY_CHANGED, null);
85         mMockModemConfigInterface.registerForRadioStateChanged(
86                 mSubId, mHandler, EVENT_RADIO_STATE_CHANGED, null);
87         mMockModemConfigInterface.registerForDeviceImeiInfoChanged(
88                 mSubId, mHandler, EVENT_DEVICE_IMEI_INFO_CHANGED, null);
89     }
90 
91     /** Handler class to handle callbacks */
92     private final class IRadioModemHandler extends Handler {
93         @Override
handleMessage(Message msg)94         public void handleMessage(Message msg) {
95             AsyncResult ar;
96             synchronized (mCacheUpdateMutex) {
97                 switch (msg.what) {
98                     case EVENT_BASEBAND_VERSION_CHANGED:
99                         Log.d(mTag, "Received EVENT_BASEBAND_VERSION_CHANGED");
100                         ar = (AsyncResult) msg.obj;
101                         if (ar != null && ar.exception == null) {
102                             mBasebandVer = (String) ar.result;
103                             Log.i(mTag, "Basedband version = " + mBasebandVer);
104                         } else {
105                             Log.e(
106                                     mTag,
107                                     msg.what
108                                             + " failure. Not update baseband version."
109                                             + ar.exception);
110                         }
111                         break;
112                     case EVENT_DEVICE_IDENTITY_CHANGED:
113                         Log.d(mTag, "Received EVENT_DEVICE_IDENTITY_CHANGED");
114                         ar = (AsyncResult) msg.obj;
115                         if (ar != null && ar.exception == null) {
116                             String[] deviceIdentity = (String[]) ar.result;
117                             mImei = deviceIdentity[0];
118                             mImeiSv = deviceIdentity[1];
119                             mEsn = deviceIdentity[2];
120                             mMeid = deviceIdentity[3];
121                             Log.i(
122                                     mTag,
123                                     "Device identity: IMEI = "
124                                             + mImei
125                                             + " IMEISV = "
126                                             + mImeiSv
127                                             + " ESN = "
128                                             + mEsn
129                                             + " MEID ="
130                                             + mMeid);
131                         } else {
132                             Log.e(
133                                     mTag,
134                                     msg.what
135                                             + " failure. Not update device identity."
136                                             + ar.exception);
137                         }
138                         break;
139                     case EVENT_RADIO_STATE_CHANGED:
140                         Log.d(mTag, "Received EVENT_RADIO_STATE_CHANGED");
141                         ar = (AsyncResult) msg.obj;
142                         if (ar != null && ar.exception == null) {
143                             mRadioState = (int) ar.result;
144                             Log.i(mTag, "Radio state: " + mRadioState);
145                         } else {
146                             Log.e(mTag, msg.what + " failure. Exception: " + ar.exception);
147                         }
148                         break;
149                     case EVENT_DEVICE_IMEI_INFO_CHANGED:
150                         Log.d(mTag, "Received EVENT_DEVICE_IMEIINFO_CHANGED");
151                         ar = (AsyncResult) msg.obj;
152                         if (ar != null && ar.exception == null) {
153                             ImeiInfo imeiInfo = (ImeiInfo) ar.result;
154                             mImei = imeiInfo.imei;
155                             mImeiSv = imeiInfo.svn;
156                             mImeiType = imeiInfo.type;
157                             Log.i(mTag, "IMEIInfo : ImeiType = " + mImeiType);
158                         } else {
159                             Log.e(mTag, msg.what + " failure. Not update device ImeiInfo."
160                                     + ar.exception);
161                         }
162                         break;
163                 }
164             }
165         }
166     }
167 
168     // Implementation of IRadioModem functions
169     @Override
setResponseFunctions( IRadioModemResponse radioModemResponse, IRadioModemIndication radioModemIndication)170     public void setResponseFunctions(
171             IRadioModemResponse radioModemResponse, IRadioModemIndication radioModemIndication) {
172         Log.d(mTag, "setResponseFunctions");
173         mRadioModemResponse = radioModemResponse;
174         mRadioModemIndication = radioModemIndication;
175         mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY);
176     }
177 
178     @Override
enableModem(int serial, boolean on)179     public void enableModem(int serial, boolean on) {
180         Log.d(mTag, "getNumOfLiveModems " + on);
181 
182         // TODO: cache value
183         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
184         try {
185             mRadioModemResponse.enableModemResponse(rsp);
186         } catch (RemoteException ex) {
187             Log.e(mTag, "Failed to enableModem from AIDL. Exception" + ex);
188         }
189     }
190 
191     @Override
getBasebandVersion(int serial)192     public void getBasebandVersion(int serial) {
193         Log.d(mTag, "getBasebandVersion");
194 
195         String baseband;
196 
197         synchronized (mCacheUpdateMutex) {
198             baseband = mBasebandVer;
199         }
200 
201         RadioResponseInfo rsp = mService.makeSolRsp(serial);
202         try {
203             mRadioModemResponse.getBasebandVersionResponse(rsp, baseband);
204         } catch (RemoteException ex) {
205             Log.e(mTag, "Failed to getBasebandVersion from AIDL. Exception" + ex);
206         }
207     }
208 
209     @Override
getDeviceIdentity(int serial)210     public void getDeviceIdentity(int serial) {
211         Log.d(mTag, "getDeviceIdentity");
212 
213         String imei, imeisv, esn, meid;
214 
215         synchronized (mCacheUpdateMutex) {
216             imei = mImei;
217             imeisv = mImeiSv;
218             esn = mEsn;
219             meid = mMeid;
220         }
221 
222         RadioResponseInfo rsp = mService.makeSolRsp(serial);
223         try {
224             mRadioModemResponse.getDeviceIdentityResponse(rsp, imei, imeisv, esn, meid);
225         } catch (RemoteException ex) {
226             Log.e(mTag, "Failed to getDeviceIdentity from AIDL. Exception" + ex);
227         }
228     }
229 
230     @Override
getImei(int serial)231     public void getImei(int serial) {
232         Log.d(mTag, "getImei");
233         android.hardware.radio.modem.ImeiInfo imeiInfo =
234                 new android.hardware.radio.modem.ImeiInfo();
235         synchronized (mCacheUpdateMutex) {
236             imeiInfo.type = mImeiType;
237             imeiInfo.imei = mImei;
238             imeiInfo.svn = mImeiSv;
239         }
240         RadioResponseInfo rsp = mService.makeSolRsp(serial);
241         try {
242             mRadioModemResponse.getImeiResponse(rsp, imeiInfo);
243         } catch (RemoteException ex) {
244             Log.e(mTag, "Failed to getImeiResponse from AIDL. Exception" + ex);
245         }
246     }
247 
248     @Override
getHardwareConfig(int serial)249     public void getHardwareConfig(int serial) {
250         Log.d(mTag, "getHardwareConfig");
251 
252         android.hardware.radio.modem.HardwareConfig[] config =
253                 new android.hardware.radio.modem.HardwareConfig[0];
254         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
255         try {
256             mRadioModemResponse.getHardwareConfigResponse(rsp, config);
257         } catch (RemoteException ex) {
258             Log.e(mTag, "Failed to getHardwareConfig from AIDL. Exception" + ex);
259         }
260     }
261 
262     @Override
getModemActivityInfo(int serial)263     public void getModemActivityInfo(int serial) {
264         Log.d(mTag, "getModemActivityInfo");
265 
266         android.hardware.radio.modem.ActivityStatsInfo activityInfo =
267                 new android.hardware.radio.modem.ActivityStatsInfo();
268 
269         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
270         try {
271             mRadioModemResponse.getModemActivityInfoResponse(rsp, activityInfo);
272         } catch (RemoteException ex) {
273             Log.e(mTag, "Failed to getModemActivityInfo from AIDL. Exception" + ex);
274         }
275     }
276 
277     @Override
getModemStackStatus(int serial)278     public void getModemStackStatus(int serial) {
279         Log.d(mTag, "getModemStackStatus");
280 
281         boolean isEnabled = false;
282 
283         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
284         try {
285             mRadioModemResponse.getModemStackStatusResponse(rsp, isEnabled);
286         } catch (RemoteException ex) {
287             Log.e(mTag, "Failed to getModemStackStatus from AIDL. Exception" + ex);
288         }
289     }
290 
291     @Override
getRadioCapability(int serial)292     public void getRadioCapability(int serial) {
293         Log.d(mTag, "getRadioCapability");
294 
295         android.hardware.radio.modem.RadioCapability rc =
296                 new android.hardware.radio.modem.RadioCapability();
297 
298         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
299         try {
300             mRadioModemResponse.getRadioCapabilityResponse(rsp, rc);
301         } catch (RemoteException ex) {
302             Log.e(mTag, "Failed to getRadioCapability from AIDL. Exception" + ex);
303         }
304     }
305 
306     @Override
nvReadItem(int serial, int itemId)307     public void nvReadItem(int serial, int itemId) {
308         Log.d(mTag, "nvReadItem");
309 
310         // TODO: cache value
311         String result = "";
312 
313         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
314         try {
315             mRadioModemResponse.nvReadItemResponse(rsp, result);
316         } catch (RemoteException ex) {
317             Log.e(mTag, "Failed to nvReadItem from AIDL. Exception" + ex);
318         }
319     }
320 
321     @Override
nvResetConfig(int serial, int resetType)322     public void nvResetConfig(int serial, int resetType) {
323         Log.d(mTag, "nvResetConfig");
324 
325         // TODO: cache value
326 
327         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
328         try {
329             mRadioModemResponse.nvResetConfigResponse(rsp);
330         } catch (RemoteException ex) {
331             Log.e(mTag, "Failed to nvResetConfig from AIDL. Exception" + ex);
332         }
333     }
334 
335     @Override
nvWriteCdmaPrl(int serial, byte[] prl)336     public void nvWriteCdmaPrl(int serial, byte[] prl) {
337         Log.d(mTag, "nvWriteCdmaPrl");
338 
339         // TODO: cache value
340 
341         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
342         try {
343             mRadioModemResponse.nvWriteCdmaPrlResponse(rsp);
344         } catch (RemoteException ex) {
345             Log.e(mTag, "Failed to nvWriteCdmaPrl from AIDL. Exception" + ex);
346         }
347     }
348 
349     @Override
nvWriteItem(int serial, android.hardware.radio.modem.NvWriteItem item)350     public void nvWriteItem(int serial, android.hardware.radio.modem.NvWriteItem item) {
351         Log.d(mTag, "nvWriteItem");
352 
353         // TODO: cache value
354 
355         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
356         try {
357             mRadioModemResponse.nvWriteItemResponse(rsp);
358         } catch (RemoteException ex) {
359             Log.e(mTag, "Failed to nvWriteItem from AIDL. Exception" + ex);
360         }
361     }
362 
363     @Override
requestShutdown(int serial)364     public void requestShutdown(int serial) {
365         Log.d(mTag, "requestShutdown");
366 
367         RadioResponseInfo rsp = mService.makeSolRsp(serial);
368         try {
369             mRadioModemResponse.requestShutdownResponse(rsp);
370         } catch (RemoteException ex) {
371             Log.e(mTag, "Failed to requestShutdown from AIDL. Exception" + ex);
372         }
373     }
374 
375     @Override
sendDeviceState(int serial, int deviceStateType, boolean state)376     public void sendDeviceState(int serial, int deviceStateType, boolean state) {
377         Log.d(mTag, "sendDeviceState");
378 
379         // TODO: cache value
380 
381         RadioResponseInfo rsp = mService.makeSolRsp(serial);
382         try {
383             mRadioModemResponse.sendDeviceStateResponse(rsp);
384         } catch (RemoteException ex) {
385             Log.e(mTag, "Failed to sendDeviceState from AIDL. Exception" + ex);
386         }
387     }
388 
389     @Override
responseAcknowledgement()390     public void responseAcknowledgement() {
391         Log.d(mTag, "responseAcknowledgement");
392     }
393 
394     @Override
setRadioCapability(int serial, android.hardware.radio.modem.RadioCapability rc)395     public void setRadioCapability(int serial, android.hardware.radio.modem.RadioCapability rc) {
396         Log.d(mTag, "setRadioCapability");
397 
398         // TODO: cache value
399         android.hardware.radio.modem.RadioCapability respRc =
400                 new android.hardware.radio.modem.RadioCapability();
401 
402         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
403         try {
404             mRadioModemResponse.setRadioCapabilityResponse(rsp, respRc);
405         } catch (RemoteException ex) {
406             Log.e(mTag, "Failed to setRadioCapability from AIDL. Exception" + ex);
407         }
408     }
409 
410     @Override
setRadioPower( int serial, boolean powerOn, boolean forEmergencyCall, boolean preferredForEmergencyCall)411     public void setRadioPower(
412             int serial,
413             boolean powerOn,
414             boolean forEmergencyCall,
415             boolean preferredForEmergencyCall) {
416         Log.d(mTag, "setRadioPower");
417         RadioResponseInfo rsp = null;
418 
419         // Check if the error response needs to be modified
420         if (mForceRadioPowerError != -1) {
421             rsp = mService.makeSolRsp(serial, mForceRadioPowerError);
422         } else {
423             synchronized (mCacheUpdateMutex) {
424                 if (powerOn) {
425                     mRadioState = MockModemConfigInterface.RADIO_STATE_ON;
426                 } else {
427                     mRadioState = MockModemConfigInterface.RADIO_STATE_OFF;
428                 }
429                 mMockModemConfigInterface.setRadioState(mSubId, mRadioState, mTag);
430             }
431             rsp = mService.makeSolRsp(serial);
432         }
433 
434         try {
435             mRadioModemResponse.setRadioPowerResponse(rsp);
436         } catch (RemoteException ex) {
437             Log.e(mTag, "Failed to setRadioPower from AIDL. Exception" + ex);
438         }
439 
440         if (rsp.error == RadioError.NONE) {
441             if (powerOn) {
442                 radioStateChanged(RadioState.ON);
443             } else {
444                 radioStateChanged(RadioState.OFF);
445             }
446         }
447     }
448 
449     /**
450      * Sent when setRadioCapability() completes. Returns the same RadioCapability as
451      * getRadioCapability() and is the same as the one sent by setRadioCapability().
452      *
453      * @param radioCapability Current radio capability
454      */
radioCapabilityIndication( android.hardware.radio.modem.RadioCapability radioCapability)455     public void radioCapabilityIndication(
456             android.hardware.radio.modem.RadioCapability radioCapability) {
457         Log.d(mTag, "radioCapabilityIndication");
458 
459         if (mRadioModemIndication != null) {
460             try {
461                 mRadioModemIndication.radioCapabilityIndication(
462                         RadioIndicationType.UNSOLICITED, radioCapability);
463             } catch (RemoteException ex) {
464                 Log.e(mTag, "Failed to radioCapabilityIndication from AIDL. Exception" + ex);
465             }
466         } else {
467 
468             Log.e(mTag, "null mRadioModemIndication");
469         }
470     }
471 
472     /**
473      * Sent when there is a change in the IMEI mapping with respect to sim slot.
474      *
475      * @param imeiInfo : change in the imeiInfo value
476      */
onImeiMappingChanged(ImeiInfo imeiInfo)477     public void onImeiMappingChanged(ImeiInfo imeiInfo) {
478         Log.d(TAG, "onImeiMappingChanged");
479 
480         if (mRadioModemIndication != null) {
481             try {
482                 mRadioModemIndication.onImeiMappingChanged(RadioIndicationType.UNSOLICITED,
483                         imeiInfo);
484             } catch (RemoteException ex) {
485                 Log.e(TAG, "Failed to onImeiMappingChanged from AIDL. Exception" + ex);
486             }
487         } else {
488             Log.e(TAG, "null mRadioModemIndication");
489         }
490     }
491 
492     /**
493      * Indicates when radio state changes.
494      *
495      * @param radioState Current radio state
496      */
radioStateChanged(int radioState)497     public void radioStateChanged(int radioState) {
498         Log.d(mTag, "radioStateChanged");
499 
500         if (mRadioModemIndication != null) {
501             try {
502                 mRadioModemIndication.radioStateChanged(
503                         RadioIndicationType.UNSOLICITED, radioState);
504             } catch (RemoteException ex) {
505                 Log.e(mTag, "Failed to radioStateChanged from AIDL. Exception" + ex);
506             }
507         } else {
508 
509             Log.e(mTag, "null mRadioModemIndication");
510         }
511     }
512 
513     /** Indicates the ril connects and returns the version. */
rilConnected()514     public void rilConnected() {
515         Log.d(mTag, "rilConnected");
516 
517         if (mRadioModemIndication != null) {
518             try {
519                 mRadioModemIndication.rilConnected(RadioIndicationType.UNSOLICITED);
520             } catch (RemoteException ex) {
521                 Log.e(mTag, "Failed to rilConnected from AIDL. Exception" + ex);
522             }
523         } else {
524 
525             Log.e(mTag, "null mRadioModemIndication");
526         }
527     }
528 
forceErrorResponse(int requestId, int error)529     public void forceErrorResponse(int requestId, int error) {
530         switch (requestId) {
531             case RIL_REQUEST_RADIO_POWER:
532                 mForceRadioPowerError = error;
533                 break;
534             default:
535                 break;
536         }
537     }
538 
539     @Override
getInterfaceHash()540     public String getInterfaceHash() {
541         return IRadioModem.HASH;
542     }
543 
544     @Override
getInterfaceVersion()545     public int getInterfaceVersion() {
546         return IRadioModem.VERSION;
547     }
548 
549     /**
550      * Helper method that will change the Imei mapping to another slot.
551      */
changeImeiMapping()552     public void changeImeiMapping() {
553         if (mImeiType == ImeiInfo.ImeiType.PRIMARY
554                 && MockModemConfigInterface.DEFAULT_PHONE1_IMEI.equals(mImei)) {
555             mImeiType = ImeiInfo.ImeiType.SECONDARY;
556             mImei = MockModemConfigInterface.DEFAULT_PHONE2_IMEI;
557         } else {
558             mImeiType = ImeiInfo.ImeiType.PRIMARY;
559             mImei = MockModemConfigInterface.DEFAULT_PHONE1_IMEI;
560         }
561 
562         Log.d(TAG, "changeImeiMapping");
563         android.hardware.radio.modem.ImeiInfo imeiInfo =
564                 new android.hardware.radio.modem.ImeiInfo();
565         synchronized (mCacheUpdateMutex) {
566             imeiInfo.type = mImeiType;
567             imeiInfo.imei = mImei;
568             imeiInfo.svn = mImeiSv;
569         }
570         onImeiMappingChanged(imeiInfo);
571     }
572 
573     /**
574      * Waits for the event of voice service.
575      *
576      * @param latchIndex The index of the event.
577      * @param waitMs The timeout in milliseconds.
578      * @return {@code true} if the event happens.
579      */
waitForLatchCountdown(int latchIndex, long waitMs)580     public boolean waitForLatchCountdown(int latchIndex, long waitMs) {
581         boolean complete = false;
582         try {
583             CountDownLatch latch;
584             synchronized (mLatches) {
585                 latch = mLatches[latchIndex];
586             }
587             long startTime = System.currentTimeMillis();
588             complete = latch.await(waitMs, TimeUnit.MILLISECONDS);
589             Log.i(TAG, "Latch " + latchIndex + " took "
590                     + (System.currentTimeMillis() - startTime) + " ms to count down.");
591         } catch (InterruptedException e) {
592             Log.e(TAG, "Waiting latch " + latchIndex + " interrupted, e=" + e);
593         }
594         synchronized (mLatches) {
595             mLatches[latchIndex] = new CountDownLatch(1);
596         }
597         return complete;
598     }
599 
600     /**
601      * Resets the CountDownLatches
602      */
resetAllLatchCountdown()603     public void resetAllLatchCountdown() {
604         synchronized (mLatches) {
605             for (int i = 0; i < LATCH_MAX; i++) {
606                 mLatches[i] = new CountDownLatch(1);
607             }
608         }
609     }
610 }
611