1/*
2 * Copyright (C) 2006 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
17package com.android.internal.telephony.test;
18
19import android.os.AsyncResult;
20import android.os.HandlerThread;
21import android.os.Looper;
22import android.os.Message;
23import android.util.Log;
24
25import com.android.internal.telephony.BaseCommands;
26import com.android.internal.telephony.CommandException;
27import com.android.internal.telephony.CommandsInterface;
28import com.android.internal.telephony.DataCallState;
29import com.android.internal.telephony.Phone;
30import com.android.internal.telephony.UUSInfo;
31import com.android.internal.telephony.gsm.CallFailCause;
32import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
33import com.android.internal.telephony.gsm.SuppServiceNotification;
34
35import java.util.ArrayList;
36
37public final class SimulatedCommands extends BaseCommands
38        implements CommandsInterface, SimulatedRadioControl {
39    private final static String LOG_TAG = "SIM";
40
41    private enum SimLockState {
42        NONE,
43        REQUIRE_PIN,
44        REQUIRE_PUK,
45        SIM_PERM_LOCKED
46    }
47
48    private enum SimFdnState {
49        NONE,
50        REQUIRE_PIN2,
51        REQUIRE_PUK2,
52        SIM_PERM_LOCKED
53    }
54
55    private final static SimLockState INITIAL_LOCK_STATE = SimLockState.NONE;
56    private final static String DEFAULT_SIM_PIN_CODE = "1234";
57    private final static String SIM_PUK_CODE = "12345678";
58    private final static SimFdnState INITIAL_FDN_STATE = SimFdnState.NONE;
59    private final static String DEFAULT_SIM_PIN2_CODE = "5678";
60    private final static String SIM_PUK2_CODE = "87654321";
61
62    //***** Instance Variables
63
64    SimulatedGsmCallState simulatedCallState;
65    HandlerThread mHandlerThread;
66    SimLockState mSimLockedState;
67    boolean mSimLockEnabled;
68    int mPinUnlockAttempts;
69    int mPukUnlockAttempts;
70    String mPinCode;
71    SimFdnState mSimFdnEnabledState;
72    boolean mSimFdnEnabled;
73    int mPin2UnlockAttempts;
74    int mPuk2UnlockAttempts;
75    int mNetworkType;
76    String mPin2Code;
77    boolean mSsnNotifyOn = false;
78
79    int pausedResponseCount;
80    ArrayList<Message> pausedResponses = new ArrayList<Message>();
81
82    int nextCallFailCause = CallFailCause.NORMAL_CLEARING;
83
84    //***** Constructor
85
86    public
87    SimulatedCommands() {
88        super(null);  // Don't log statistics
89        mHandlerThread = new HandlerThread("SimulatedCommands");
90        mHandlerThread.start();
91        Looper looper = mHandlerThread.getLooper();
92
93        simulatedCallState = new SimulatedGsmCallState(looper);
94
95        setRadioState(RadioState.RADIO_OFF);
96        mSimLockedState = INITIAL_LOCK_STATE;
97        mSimLockEnabled = (mSimLockedState != SimLockState.NONE);
98        mPinCode = DEFAULT_SIM_PIN_CODE;
99        mSimFdnEnabledState = INITIAL_FDN_STATE;
100        mSimFdnEnabled = (mSimFdnEnabledState != SimFdnState.NONE);
101        mPin2Code = DEFAULT_SIM_PIN2_CODE;
102    }
103
104    //***** CommandsInterface implementation
105
106    public void getIccCardStatus(Message result) {
107        unimplemented(result);
108    }
109
110    public void supplyIccPin(String pin, Message result)  {
111        if (mSimLockedState != SimLockState.REQUIRE_PIN) {
112            Log.i(LOG_TAG, "[SimCmd] supplyIccPin: wrong state, state=" +
113                    mSimLockedState);
114            CommandException ex = new CommandException(
115                    CommandException.Error.PASSWORD_INCORRECT);
116            AsyncResult.forMessage(result, null, ex);
117            result.sendToTarget();
118            return;
119        }
120
121        if (pin != null && pin.equals(mPinCode)) {
122            Log.i(LOG_TAG, "[SimCmd] supplyIccPin: success!");
123            mPinUnlockAttempts = 0;
124            mSimLockedState = SimLockState.NONE;
125            mIccStatusChangedRegistrants.notifyRegistrants();
126
127            if (result != null) {
128                AsyncResult.forMessage(result, null, null);
129                result.sendToTarget();
130            }
131
132            return;
133        }
134
135        if (result != null) {
136            mPinUnlockAttempts ++;
137
138            Log.i(LOG_TAG, "[SimCmd] supplyIccPin: failed! attempt=" +
139                    mPinUnlockAttempts);
140            if (mPinUnlockAttempts >= 3) {
141                Log.i(LOG_TAG, "[SimCmd] supplyIccPin: set state to REQUIRE_PUK");
142                mSimLockedState = SimLockState.REQUIRE_PUK;
143            }
144
145            CommandException ex = new CommandException(
146                    CommandException.Error.PASSWORD_INCORRECT);
147            AsyncResult.forMessage(result, null, ex);
148            result.sendToTarget();
149        }
150    }
151
152    public void supplyIccPuk(String puk, String newPin, Message result)  {
153        if (mSimLockedState != SimLockState.REQUIRE_PUK) {
154            Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: wrong state, state=" +
155                    mSimLockedState);
156            CommandException ex = new CommandException(
157                    CommandException.Error.PASSWORD_INCORRECT);
158            AsyncResult.forMessage(result, null, ex);
159            result.sendToTarget();
160            return;
161        }
162
163        if (puk != null && puk.equals(SIM_PUK_CODE)) {
164            Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: success!");
165            mSimLockedState = SimLockState.NONE;
166            mPukUnlockAttempts = 0;
167            mIccStatusChangedRegistrants.notifyRegistrants();
168
169            if (result != null) {
170                AsyncResult.forMessage(result, null, null);
171                result.sendToTarget();
172            }
173
174            return;
175        }
176
177        if (result != null) {
178            mPukUnlockAttempts ++;
179
180            Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: failed! attempt=" +
181                    mPukUnlockAttempts);
182            if (mPukUnlockAttempts >= 10) {
183                Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: set state to SIM_PERM_LOCKED");
184                mSimLockedState = SimLockState.SIM_PERM_LOCKED;
185            }
186
187            CommandException ex = new CommandException(
188                    CommandException.Error.PASSWORD_INCORRECT);
189            AsyncResult.forMessage(result, null, ex);
190            result.sendToTarget();
191        }
192    }
193
194    public void supplyIccPin2(String pin2, Message result)  {
195        if (mSimFdnEnabledState != SimFdnState.REQUIRE_PIN2) {
196            Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: wrong state, state=" +
197                    mSimFdnEnabledState);
198            CommandException ex = new CommandException(
199                    CommandException.Error.PASSWORD_INCORRECT);
200            AsyncResult.forMessage(result, null, ex);
201            result.sendToTarget();
202            return;
203        }
204
205        if (pin2 != null && pin2.equals(mPin2Code)) {
206            Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: success!");
207            mPin2UnlockAttempts = 0;
208            mSimFdnEnabledState = SimFdnState.NONE;
209
210            if (result != null) {
211                AsyncResult.forMessage(result, null, null);
212                result.sendToTarget();
213            }
214
215            return;
216        }
217
218        if (result != null) {
219            mPin2UnlockAttempts ++;
220
221            Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: failed! attempt=" +
222                    mPin2UnlockAttempts);
223            if (mPin2UnlockAttempts >= 3) {
224                Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: set state to REQUIRE_PUK2");
225                mSimFdnEnabledState = SimFdnState.REQUIRE_PUK2;
226            }
227
228            CommandException ex = new CommandException(
229                    CommandException.Error.PASSWORD_INCORRECT);
230            AsyncResult.forMessage(result, null, ex);
231            result.sendToTarget();
232        }
233    }
234
235    public void supplyIccPuk2(String puk2, String newPin2, Message result)  {
236        if (mSimFdnEnabledState != SimFdnState.REQUIRE_PUK2) {
237            Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: wrong state, state=" +
238                    mSimLockedState);
239            CommandException ex = new CommandException(
240                    CommandException.Error.PASSWORD_INCORRECT);
241            AsyncResult.forMessage(result, null, ex);
242            result.sendToTarget();
243            return;
244        }
245
246        if (puk2 != null && puk2.equals(SIM_PUK2_CODE)) {
247            Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: success!");
248            mSimFdnEnabledState = SimFdnState.NONE;
249            mPuk2UnlockAttempts = 0;
250
251            if (result != null) {
252                AsyncResult.forMessage(result, null, null);
253                result.sendToTarget();
254            }
255
256            return;
257        }
258
259        if (result != null) {
260            mPuk2UnlockAttempts ++;
261
262            Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: failed! attempt=" +
263                    mPuk2UnlockAttempts);
264            if (mPuk2UnlockAttempts >= 10) {
265                Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: set state to SIM_PERM_LOCKED");
266                mSimFdnEnabledState = SimFdnState.SIM_PERM_LOCKED;
267            }
268
269            CommandException ex = new CommandException(
270                    CommandException.Error.PASSWORD_INCORRECT);
271            AsyncResult.forMessage(result, null, ex);
272            result.sendToTarget();
273        }
274    }
275
276    public void changeIccPin(String oldPin, String newPin, Message result)  {
277        if (oldPin != null && oldPin.equals(mPinCode)) {
278            mPinCode = newPin;
279            if (result != null) {
280                AsyncResult.forMessage(result, null, null);
281                result.sendToTarget();
282            }
283
284            return;
285        }
286
287        if (result != null) {
288            Log.i(LOG_TAG, "[SimCmd] changeIccPin: pin failed!");
289
290            CommandException ex = new CommandException(
291                    CommandException.Error.PASSWORD_INCORRECT);
292            AsyncResult.forMessage(result, null, ex);
293            result.sendToTarget();
294        }
295    }
296
297    public void changeIccPin2(String oldPin2, String newPin2, Message result)  {
298        if (oldPin2 != null && oldPin2.equals(mPin2Code)) {
299            mPin2Code = newPin2;
300            if (result != null) {
301                AsyncResult.forMessage(result, null, null);
302                result.sendToTarget();
303            }
304
305            return;
306        }
307
308        if (result != null) {
309            Log.i(LOG_TAG, "[SimCmd] changeIccPin2: pin2 failed!");
310
311            CommandException ex = new CommandException(
312                    CommandException.Error.PASSWORD_INCORRECT);
313            AsyncResult.forMessage(result, null, ex);
314            result.sendToTarget();
315        }
316    }
317
318    public void
319    changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) {
320        unimplemented(result);
321    }
322
323    public void
324    setSuppServiceNotifications(boolean enable, Message result) {
325        resultSuccess(result, null);
326
327        if (enable && mSsnNotifyOn) {
328            Log.w(LOG_TAG, "Supp Service Notifications already enabled!");
329        }
330
331        mSsnNotifyOn = enable;
332    }
333
334    @Override
335    public void queryFacilityLock(String facility, String pin,
336                                   int serviceClass, Message result) {
337        queryFacilityLockForApp(facility, pin, serviceClass, null, result);
338    }
339
340    @Override
341    public void queryFacilityLockForApp(String facility, String pin, int serviceClass,
342            String appId, Message result) {
343        if (facility != null && facility.equals(CommandsInterface.CB_FACILITY_BA_SIM)) {
344            if (result != null) {
345                int[] r = new int[1];
346                r[0] = (mSimLockEnabled ? 1 : 0);
347                Log.i(LOG_TAG, "[SimCmd] queryFacilityLock: SIM is "
348                        + (r[0] == 0 ? "unlocked" : "locked"));
349                AsyncResult.forMessage(result, r, null);
350                result.sendToTarget();
351            }
352            return;
353        } else if (facility != null && facility.equals(CommandsInterface.CB_FACILITY_BA_FD)) {
354            if (result != null) {
355                int[] r = new int[1];
356                r[0] = (mSimFdnEnabled ? 1 : 0);
357                Log.i(LOG_TAG, "[SimCmd] queryFacilityLock: FDN is "
358                        + (r[0] == 0 ? "disabled" : "enabled"));
359                AsyncResult.forMessage(result, r, null);
360                result.sendToTarget();
361            }
362            return;
363        }
364
365        unimplemented(result);
366    }
367
368    @Override
369    public void setFacilityLock(String facility, boolean lockEnabled, String pin, int serviceClass,
370            Message result) {
371        setFacilityLockForApp(facility, lockEnabled, pin, serviceClass, null, result);
372    }
373
374    @Override
375    public void setFacilityLockForApp(String facility, boolean lockEnabled,
376                                 String pin, int serviceClass, String appId,
377                                 Message result) {
378        if (facility != null &&
379                facility.equals(CommandsInterface.CB_FACILITY_BA_SIM)) {
380            if (pin != null && pin.equals(mPinCode)) {
381                Log.i(LOG_TAG, "[SimCmd] setFacilityLock: pin is valid");
382                mSimLockEnabled = lockEnabled;
383
384                if (result != null) {
385                    AsyncResult.forMessage(result, null, null);
386                    result.sendToTarget();
387                }
388
389                return;
390            }
391
392            if (result != null) {
393                Log.i(LOG_TAG, "[SimCmd] setFacilityLock: pin failed!");
394
395                CommandException ex = new CommandException(
396                        CommandException.Error.GENERIC_FAILURE);
397                AsyncResult.forMessage(result, null, ex);
398                result.sendToTarget();
399            }
400
401            return;
402        }  else if (facility != null &&
403                facility.equals(CommandsInterface.CB_FACILITY_BA_FD)) {
404            if (pin != null && pin.equals(mPin2Code)) {
405                Log.i(LOG_TAG, "[SimCmd] setFacilityLock: pin2 is valid");
406                mSimFdnEnabled = lockEnabled;
407
408                if (result != null) {
409                    AsyncResult.forMessage(result, null, null);
410                    result.sendToTarget();
411                }
412
413                return;
414            }
415
416            if (result != null) {
417                Log.i(LOG_TAG, "[SimCmd] setFacilityLock: pin2 failed!");
418
419                CommandException ex = new CommandException(
420                        CommandException.Error.GENERIC_FAILURE);
421                AsyncResult.forMessage(result, null, ex);
422                result.sendToTarget();
423            }
424
425            return;
426        }
427
428        unimplemented(result);
429    }
430
431    public void supplyNetworkDepersonalization(String netpin, Message result)  {
432        unimplemented(result);
433    }
434
435    /**
436     *  returned message
437     *  retMsg.obj = AsyncResult ar
438     *  ar.exception carries exception on failure
439     *  ar.userObject contains the original value of result.obj
440     *  ar.result contains a List of DriverCall
441     *      The ar.result List is sorted by DriverCall.index
442     */
443    public void getCurrentCalls (Message result) {
444        if ((mState == RadioState.RADIO_ON) && !isSimLocked()) {
445            //Log.i("GSM", "[SimCmds] getCurrentCalls");
446            resultSuccess(result, simulatedCallState.getDriverCalls());
447        } else {
448            //Log.i("GSM", "[SimCmds] getCurrentCalls: RADIO_OFF or SIM not ready!");
449            resultFail(result,
450                new CommandException(
451                    CommandException.Error.RADIO_NOT_AVAILABLE));
452        }
453    }
454
455    /**
456     *  @deprecated
457     */
458    public void getPDPContextList(Message result) {
459        getDataCallList(result);
460    }
461
462    /**
463     *  returned message
464     *  retMsg.obj = AsyncResult ar
465     *  ar.exception carries exception on failure
466     *  ar.userObject contains the original value of result.obj
467     *  ar.result contains a List of DataCallState
468     */
469    public void getDataCallList(Message result) {
470        resultSuccess(result, new ArrayList<DataCallState>(0));
471    }
472
473    /**
474     *  returned message
475     *  retMsg.obj = AsyncResult ar
476     *  ar.exception carries exception on failure
477     *  ar.userObject contains the original value of result.obj
478     *  ar.result is null on success and failure
479     *
480     * CLIR_DEFAULT     == on "use subscription default value"
481     * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation)
482     * CLIR_INVOCATION  == on "CLIR invocation" (restrict CLI presentation)
483     */
484    public void dial (String address, int clirMode, Message result) {
485        simulatedCallState.onDial(address);
486
487        resultSuccess(result, null);
488    }
489
490    /**
491     *  returned message
492     *  retMsg.obj = AsyncResult ar
493     *  ar.exception carries exception on failure
494     *  ar.userObject contains the original value of result.obj
495     *  ar.result is null on success and failure
496     *
497     * CLIR_DEFAULT     == on "use subscription default value"
498     * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation)
499     * CLIR_INVOCATION  == on "CLIR invocation" (restrict CLI presentation)
500     */
501    public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
502        simulatedCallState.onDial(address);
503
504        resultSuccess(result, null);
505    }
506
507    public void getIMSI(Message result) {
508        getIMSIForApp(null, result);
509    }
510    /**
511     *  returned message
512     *  retMsg.obj = AsyncResult ar
513     *  ar.exception carries exception on failure
514     *  ar.userObject contains the original value of result.obj
515     *  ar.result is String containing IMSI on success
516     */
517    public void getIMSIForApp(String aid, Message result) {
518        resultSuccess(result, "012345678901234");
519    }
520
521    /**
522     *  returned message
523     *  retMsg.obj = AsyncResult ar
524     *  ar.exception carries exception on failure
525     *  ar.userObject contains the original value of result.obj
526     *  ar.result is String containing IMEI on success
527     */
528    public void getIMEI(Message result) {
529        resultSuccess(result, "012345678901234");
530    }
531
532    /**
533     *  returned message
534     *  retMsg.obj = AsyncResult ar
535     *  ar.exception carries exception on failure
536     *  ar.userObject contains the original value of result.obj
537     *  ar.result is String containing IMEISV on success
538     */
539    public void getIMEISV(Message result) {
540        resultSuccess(result, "99");
541    }
542
543    /**
544     * Hang up one individual connection.
545     *  returned message
546     *  retMsg.obj = AsyncResult ar
547     *  ar.exception carries exception on failure
548     *  ar.userObject contains the original value of result.obj
549     *  ar.result is null on success and failure
550     *
551     *  3GPP 22.030 6.5.5
552     *  "Releases a specific active call X"
553     */
554    public void hangupConnection (int gsmIndex, Message result) {
555        boolean success;
556
557        success = simulatedCallState.onChld('1', (char)('0'+gsmIndex));
558
559        if (!success){
560            Log.i("GSM", "[SimCmd] hangupConnection: resultFail");
561            resultFail(result, new RuntimeException("Hangup Error"));
562        } else {
563            Log.i("GSM", "[SimCmd] hangupConnection: resultSuccess");
564            resultSuccess(result, null);
565        }
566    }
567
568    /**
569     * 3GPP 22.030 6.5.5
570     *  "Releases all held calls or sets User Determined User Busy (UDUB)
571     *   for a waiting call."
572     *  ar.exception carries exception on failure
573     *  ar.userObject contains the original value of result.obj
574     *  ar.result is null on success and failure
575     */
576    public void hangupWaitingOrBackground (Message result) {
577        boolean success;
578
579        success = simulatedCallState.onChld('0', '\0');
580
581        if (!success){
582            resultFail(result, new RuntimeException("Hangup Error"));
583        } else {
584            resultSuccess(result, null);
585        }
586    }
587
588    /**
589     * 3GPP 22.030 6.5.5
590     * "Releases all active calls (if any exist) and accepts
591     *  the other (held or waiting) call."
592     *
593     *  ar.exception carries exception on failure
594     *  ar.userObject contains the original value of result.obj
595     *  ar.result is null on success and failure
596     */
597    public void hangupForegroundResumeBackground (Message result) {
598        boolean success;
599
600        success = simulatedCallState.onChld('1', '\0');
601
602        if (!success){
603            resultFail(result, new RuntimeException("Hangup Error"));
604        } else {
605            resultSuccess(result, null);
606        }
607    }
608
609    /**
610     * 3GPP 22.030 6.5.5
611     * "Places all active calls (if any exist) on hold and accepts
612     *  the other (held or waiting) call."
613     *
614     *  ar.exception carries exception on failure
615     *  ar.userObject contains the original value of result.obj
616     *  ar.result is null on success and failure
617     */
618    public void switchWaitingOrHoldingAndActive (Message result) {
619        boolean success;
620
621        success = simulatedCallState.onChld('2', '\0');
622
623        if (!success){
624            resultFail(result, new RuntimeException("Hangup Error"));
625        } else {
626            resultSuccess(result, null);
627        }
628    }
629
630    /**
631     * 3GPP 22.030 6.5.5
632     * "Adds a held call to the conversation"
633     *
634     *  ar.exception carries exception on failure
635     *  ar.userObject contains the original value of result.obj
636     *  ar.result is null on success and failure
637     */
638    public void conference (Message result) {
639        boolean success;
640
641        success = simulatedCallState.onChld('3', '\0');
642
643        if (!success){
644            resultFail(result, new RuntimeException("Hangup Error"));
645        } else {
646            resultSuccess(result, null);
647        }
648    }
649
650    /**
651     * 3GPP 22.030 6.5.5
652     * "Connects the two calls and disconnects the subscriber from both calls"
653     *
654     *  ar.exception carries exception on failure
655     *  ar.userObject contains the original value of result.obj
656     *  ar.result is null on success and failure
657     */
658    public void explicitCallTransfer (Message result) {
659        boolean success;
660
661        success = simulatedCallState.onChld('4', '\0');
662
663        if (!success){
664            resultFail(result, new RuntimeException("Hangup Error"));
665        } else {
666            resultSuccess(result, null);
667        }
668    }
669
670    /**
671     * 3GPP 22.030 6.5.5
672     * "Places all active calls on hold except call X with which
673     *  communication shall be supported."
674     */
675    public void separateConnection (int gsmIndex, Message result) {
676        boolean success;
677
678        char ch = (char)(gsmIndex + '0');
679        success = simulatedCallState.onChld('2', ch);
680
681        if (!success){
682            resultFail(result, new RuntimeException("Hangup Error"));
683        } else {
684            resultSuccess(result, null);
685        }
686    }
687
688    /**
689     *
690     *  ar.exception carries exception on failure
691     *  ar.userObject contains the original value of result.obj
692     *  ar.result is null on success and failure
693     */
694    public void acceptCall (Message result) {
695        boolean success;
696
697        success = simulatedCallState.onAnswer();
698
699        if (!success){
700            resultFail(result, new RuntimeException("Hangup Error"));
701        } else {
702            resultSuccess(result, null);
703        }
704    }
705
706    /**
707     *  also known as UDUB
708     *  ar.exception carries exception on failure
709     *  ar.userObject contains the original value of result.obj
710     *  ar.result is null on success and failure
711     */
712    public void rejectCall (Message result) {
713        boolean success;
714
715        success = simulatedCallState.onChld('0', '\0');
716
717        if (!success){
718            resultFail(result, new RuntimeException("Hangup Error"));
719        } else {
720            resultSuccess(result, null);
721        }
722    }
723
724    /**
725     * cause code returned as Integer in Message.obj.response
726     * Returns integer cause code defined in TS 24.008
727     * Annex H or closest approximation.
728     * Most significant codes:
729     * - Any defined in 22.001 F.4 (for generating busy/congestion)
730     * - Cause 68: ACM >= ACMMax
731     */
732    public void getLastCallFailCause (Message result) {
733        int[] ret = new int[1];
734
735        ret[0] = nextCallFailCause;
736        resultSuccess(result, ret);
737    }
738
739    /**
740     * @deprecated
741     */
742    public void getLastPdpFailCause (Message result) {
743        unimplemented(result);
744    }
745
746    public void getLastDataCallFailCause(Message result) {
747        //
748        unimplemented(result);
749    }
750
751    public void setMute (boolean enableMute, Message result) {unimplemented(result);}
752
753    public void getMute (Message result) {unimplemented(result);}
754
755    /**
756     * response.obj is an AsyncResult
757     * response.obj.result is an int[2]
758     * response.obj.result[0] is received signal strength (0-31, 99)
759     * response.obj.result[1] is  bit error rate (0-7, 99)
760     * as defined in TS 27.007 8.5
761     */
762    public void getSignalStrength (Message result) {
763        int ret[] = new int[2];
764
765        ret[0] = 23;
766        ret[1] = 0;
767
768        resultSuccess(result, ret);
769    }
770
771     /**
772     * Assign a specified band for RF configuration.
773     *
774     * @param bandMode one of BM_*_BAND
775     * @param result is callback message
776     */
777    public void setBandMode (int bandMode, Message result) {
778        resultSuccess(result, null);
779    }
780
781    /**
782     * Query the list of band mode supported by RF.
783     *
784     * @param result is callback message
785     *        ((AsyncResult)response.obj).result  is an int[] with every
786     *        element representing one available BM_*_BAND
787     */
788    public void queryAvailableBandMode (Message result) {
789        int ret[] = new int [4];
790
791        ret[0] = 4;
792        ret[1] = Phone.BM_US_BAND;
793        ret[2] = Phone.BM_JPN_BAND;
794        ret[3] = Phone.BM_AUS_BAND;
795
796        resultSuccess(result, ret);
797    }
798
799    /**
800     * {@inheritDoc}
801     */
802    public void sendTerminalResponse(String contents, Message response) {
803        resultSuccess(response, null);
804    }
805
806    /**
807     * {@inheritDoc}
808     */
809    public void sendEnvelope(String contents, Message response) {
810        resultSuccess(response, null);
811    }
812
813    /**
814     * {@inheritDoc}
815     */
816    public void sendEnvelopeWithStatus(String contents, Message response) {
817        resultSuccess(response, null);
818    }
819
820    /**
821     * {@inheritDoc}
822     */
823    public void handleCallSetupRequestFromSim(
824            boolean accept, Message response) {
825        resultSuccess(response, null);
826    }
827
828    /**
829     * response.obj.result is an String[14]
830     * See ril.h for details
831     *
832     * Please note that registration state 4 ("unknown") is treated
833     * as "out of service" above
834     */
835    public void getVoiceRegistrationState (Message result) {
836        String ret[] = new String[14];
837
838        ret[0] = "5"; // registered roam
839        ret[1] = null;
840        ret[2] = null;
841        ret[3] = null;
842        ret[4] = null;
843        ret[5] = null;
844        ret[6] = null;
845        ret[7] = null;
846        ret[8] = null;
847        ret[9] = null;
848        ret[10] = null;
849        ret[11] = null;
850        ret[12] = null;
851        ret[13] = null;
852
853        resultSuccess(result, ret);
854    }
855
856    /**
857     * response.obj.result is an String[4]
858     * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2
859     * response.obj.result[1] is LAC if registered or NULL if not
860     * response.obj.result[2] is CID if registered or NULL if not
861     * response.obj.result[3] indicates the available radio technology, where:
862     *      0 == unknown
863     *      1 == GPRS only
864     *      2 == EDGE
865     *      3 == UMTS
866     *
867     * valid LAC are 0x0000 - 0xffff
868     * valid CID are 0x00000000 - 0xffffffff
869     *
870     * Please note that registration state 4 ("unknown") is treated
871     * as "out of service" in the Android telephony system
872     */
873    public void getDataRegistrationState (Message result) {
874        String ret[] = new String[4];
875
876        ret[0] = "5"; // registered roam
877        ret[1] = null;
878        ret[2] = null;
879        ret[3] = "2";
880
881        resultSuccess(result, ret);
882    }
883
884    /**
885     * response.obj.result is a String[3]
886     * response.obj.result[0] is long alpha or null if unregistered
887     * response.obj.result[1] is short alpha or null if unregistered
888     * response.obj.result[2] is numeric or null if unregistered
889     */
890    public void getOperator(Message result) {
891        String[] ret = new String[3];
892
893        ret[0] = "El Telco Loco";
894        ret[1] = "Telco Loco";
895        ret[2] = "001001";
896
897        resultSuccess(result, ret);
898    }
899
900    /**
901     *  ar.exception carries exception on failure
902     *  ar.userObject contains the original value of result.obj
903     *  ar.result is null on success and failure
904     */
905    public void sendDtmf(char c, Message result) {
906        resultSuccess(result, null);
907    }
908
909    /**
910     *  ar.exception carries exception on failure
911     *  ar.userObject contains the original value of result.obj
912     *  ar.result is null on success and failure
913     */
914    public void startDtmf(char c, Message result) {
915        resultSuccess(result, null);
916    }
917
918    /**
919     *  ar.exception carries exception on failure
920     *  ar.userObject contains the original value of result.obj
921     *  ar.result is null on success and failure
922     */
923    public void stopDtmf(Message result) {
924        resultSuccess(result, null);
925    }
926
927    /**
928     *  ar.exception carries exception on failure
929     *  ar.userObject contains the original value of result.obj
930     *  ar.result is null on success and failure
931     */
932    public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
933        resultSuccess(result, null);
934    }
935
936    /**
937     * smscPDU is smsc address in PDU form GSM BCD format prefixed
938     *      by a length byte (as expected by TS 27.005) or NULL for default SMSC
939     * pdu is SMS in PDU format as an ASCII hex string
940     *      less the SMSC address
941     */
942    public void sendSMS (String smscPDU, String pdu, Message result) {unimplemented(result);}
943
944    public void deleteSmsOnSim(int index, Message response) {
945        Log.d(LOG_TAG, "Delete message at index " + index);
946        unimplemented(response);
947    }
948
949    public void deleteSmsOnRuim(int index, Message response) {
950        Log.d(LOG_TAG, "Delete RUIM message at index " + index);
951        unimplemented(response);
952    }
953
954    public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
955        Log.d(LOG_TAG, "Write SMS to SIM with status " + status);
956        unimplemented(response);
957    }
958
959    public void writeSmsToRuim(int status, String pdu, Message response) {
960        Log.d(LOG_TAG, "Write SMS to RUIM with status " + status);
961        unimplemented(response);
962    }
963
964    public void setupDataCall(String radioTechnology, String profile,
965            String apn, String user, String password, String authType,
966            String protocol, Message result) {
967        unimplemented(result);
968    }
969
970    public void deactivateDataCall(int cid, int reason, Message result) {unimplemented(result);}
971
972    public void setPreferredNetworkType(int networkType , Message result) {
973        mNetworkType = networkType;
974        resultSuccess(result, null);
975    }
976
977    public void getPreferredNetworkType(Message result) {
978        int ret[] = new int[1];
979
980        ret[0] = mNetworkType;
981        resultSuccess(result, ret);
982    }
983
984    public void getNeighboringCids(Message result) {
985        int ret[] = new int[7];
986
987        ret[0] = 6;
988        for (int i = 1; i<7; i++) {
989            ret[i] = i;
990        }
991        resultSuccess(result, ret);
992    }
993
994    public void setLocationUpdates(boolean enable, Message response) {
995        unimplemented(response);
996    }
997
998    public void getSmscAddress(Message result) {
999        unimplemented(result);
1000    }
1001
1002    public void setSmscAddress(String address, Message result) {
1003        unimplemented(result);
1004    }
1005
1006    public void reportSmsMemoryStatus(boolean available, Message result) {
1007        unimplemented(result);
1008    }
1009
1010    public void reportStkServiceIsRunning(Message result) {
1011        resultSuccess(result, null);
1012    }
1013
1014    @Override
1015    public void getCdmaSubscriptionSource(Message result) {
1016        unimplemented(result);
1017    }
1018
1019    private boolean isSimLocked() {
1020        if (mSimLockedState != SimLockState.NONE) {
1021            return true;
1022        }
1023        return false;
1024    }
1025
1026    public void setRadioPower(boolean on, Message result) {
1027        if(on) {
1028            setRadioState(RadioState.RADIO_ON);
1029        } else {
1030            setRadioState(RadioState.RADIO_OFF);
1031        }
1032    }
1033
1034
1035    public void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) {
1036        unimplemented(result);
1037    }
1038
1039    public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) {
1040        unimplemented(result);
1041    }
1042
1043    public void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu,
1044            Message result) {
1045        unimplemented(result);
1046    }
1047
1048    public void iccIO(int command, int fileid, String path, int p1, int p2, int p3, String data,
1049            String pin2, Message response) {
1050        iccIOForApp(command, fileid, path, p1, p2, p3, data,pin2, null, response);
1051    }
1052
1053    /**
1054     * parameters equivalent to 27.007 AT+CRSM command
1055     * response.obj will be an AsyncResult
1056     * response.obj.userObj will be a SimIoResult on success
1057     */
1058    public void iccIOForApp (int command, int fileid, String path, int p1, int p2,
1059                       int p3, String data, String pin2, String aid, Message result) {
1060        unimplemented(result);
1061    }
1062
1063    /**
1064     * (AsyncResult)response.obj).result is an int[] with element [0] set to
1065     * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned".
1066     *
1067     * @param response is callback message
1068     */
1069    public void queryCLIP(Message response) { unimplemented(response); }
1070
1071
1072    /**
1073     * response.obj will be a an int[2]
1074     *
1075     * response.obj[0] will be TS 27.007 +CLIR parameter 'n'
1076     *  0 presentation indicator is used according to the subscription of the CLIR service
1077     *  1 CLIR invocation
1078     *  2 CLIR suppression
1079     *
1080     * response.obj[1] will be TS 27.007 +CLIR parameter 'm'
1081     *  0 CLIR not provisioned
1082     *  1 CLIR provisioned in permanent mode
1083     *  2 unknown (e.g. no network, etc.)
1084     *  3 CLIR temporary mode presentation restricted
1085     *  4 CLIR temporary mode presentation allowed
1086     */
1087
1088    public void getCLIR(Message result) {unimplemented(result);}
1089
1090    /**
1091     * clirMode is one of the CLIR_* constants above
1092     *
1093     * response.obj is null
1094     */
1095
1096    public void setCLIR(int clirMode, Message result) {unimplemented(result);}
1097
1098    /**
1099     * (AsyncResult)response.obj).result is an int[] with element [0] set to
1100     * 0 for disabled, 1 for enabled.
1101     *
1102     * @param serviceClass is a sum of SERVICE_CLASS_*
1103     * @param response is callback message
1104     */
1105
1106    public void queryCallWaiting(int serviceClass, Message response) {
1107        unimplemented(response);
1108    }
1109
1110    /**
1111     * @param enable is true to enable, false to disable
1112     * @param serviceClass is a sum of SERVICE_CLASS_*
1113     * @param response is callback message
1114     */
1115
1116    public void setCallWaiting(boolean enable, int serviceClass,
1117            Message response) {
1118        unimplemented(response);
1119    }
1120
1121    /**
1122     * @param action is one of CF_ACTION_*
1123     * @param cfReason is one of CF_REASON_*
1124     * @param serviceClass is a sum of SERVICE_CLASSS_*
1125     */
1126    public void setCallForward(int action, int cfReason, int serviceClass,
1127            String number, int timeSeconds, Message result) {unimplemented(result);}
1128
1129    /**
1130     * cfReason is one of CF_REASON_*
1131     *
1132     * ((AsyncResult)response.obj).result will be an array of
1133     * CallForwardInfo's
1134     *
1135     * An array of length 0 means "disabled for all codes"
1136     */
1137    public void queryCallForwardStatus(int cfReason, int serviceClass,
1138            String number, Message result) {unimplemented(result);}
1139
1140    public void setNetworkSelectionModeAutomatic(Message result) {unimplemented(result);}
1141    public void exitEmergencyCallbackMode(Message result) {unimplemented(result);}
1142    public void setNetworkSelectionModeManual(
1143            String operatorNumeric, Message result) {unimplemented(result);}
1144
1145    /**
1146     * Queries whether the current network selection mode is automatic
1147     * or manual
1148     *
1149     * ((AsyncResult)response.obj).result  is an int[] with element [0] being
1150     * a 0 for automatic selection and a 1 for manual selection
1151     */
1152
1153    public void getNetworkSelectionMode(Message result) {
1154        int ret[] = new int[1];
1155
1156        ret[0] = 0;
1157        resultSuccess(result, ret);
1158    }
1159
1160    /**
1161     * Queries the currently available networks
1162     *
1163     * ((AsyncResult)response.obj).result  is a List of NetworkInfo objects
1164     */
1165    public void getAvailableNetworks(Message result) {unimplemented(result);}
1166
1167    public void getBasebandVersion (Message result) {
1168        resultSuccess(result, "SimulatedCommands");
1169    }
1170
1171    /**
1172     * Simulates an incoming USSD message
1173     * @param statusCode  Status code string. See <code>setOnUSSD</code>
1174     * in CommandsInterface.java
1175     * @param message Message text to send or null if none
1176     */
1177    public void triggerIncomingUssd(String statusCode, String message) {
1178        if (mUSSDRegistrant != null) {
1179            String[] result = {statusCode, message};
1180            mUSSDRegistrant.notifyResult(result);
1181        }
1182    }
1183
1184
1185    public void sendUSSD (String ussdString, Message result) {
1186
1187        // We simulate this particular sequence
1188        if (ussdString.equals("#646#")) {
1189            resultSuccess(result, null);
1190
1191            // 0 == USSD-Notify
1192            triggerIncomingUssd("0", "You have NNN minutes remaining.");
1193        } else {
1194            resultSuccess(result, null);
1195
1196            triggerIncomingUssd("0", "All Done");
1197        }
1198    }
1199
1200    // inherited javadoc suffices
1201    public void cancelPendingUssd (Message response) {
1202        resultSuccess(response, null);
1203    }
1204
1205
1206    public void resetRadio(Message result) {
1207        unimplemented(result);
1208    }
1209
1210    public void invokeOemRilRequestRaw(byte[] data, Message response) {
1211        // Just echo back data
1212        if (response != null) {
1213            AsyncResult.forMessage(response).result = data;
1214            response.sendToTarget();
1215        }
1216    }
1217
1218    public void invokeOemRilRequestStrings(String[] strings, Message response) {
1219        // Just echo back data
1220        if (response != null) {
1221            AsyncResult.forMessage(response).result = strings;
1222            response.sendToTarget();
1223        }
1224    }
1225
1226    //***** SimulatedRadioControl
1227
1228
1229    /** Start the simulated phone ringing */
1230    public void
1231    triggerRing(String number) {
1232        simulatedCallState.triggerRing(number);
1233        mCallStateRegistrants.notifyRegistrants();
1234    }
1235
1236    public void
1237    progressConnectingCallState() {
1238        simulatedCallState.progressConnectingCallState();
1239        mCallStateRegistrants.notifyRegistrants();
1240    }
1241
1242    /** If a call is DIALING or ALERTING, progress it all the way to ACTIVE */
1243    public void
1244    progressConnectingToActive() {
1245        simulatedCallState.progressConnectingToActive();
1246        mCallStateRegistrants.notifyRegistrants();
1247    }
1248
1249    /** automatically progress mobile originated calls to ACTIVE.
1250     *  default to true
1251     */
1252    public void
1253    setAutoProgressConnectingCall(boolean b) {
1254        simulatedCallState.setAutoProgressConnectingCall(b);
1255    }
1256
1257    public void
1258    setNextDialFailImmediately(boolean b) {
1259        simulatedCallState.setNextDialFailImmediately(b);
1260    }
1261
1262    public void
1263    setNextCallFailCause(int gsmCause) {
1264        nextCallFailCause = gsmCause;
1265    }
1266
1267    public void
1268    triggerHangupForeground() {
1269        simulatedCallState.triggerHangupForeground();
1270        mCallStateRegistrants.notifyRegistrants();
1271    }
1272
1273    /** hangup holding calls */
1274    public void
1275    triggerHangupBackground() {
1276        simulatedCallState.triggerHangupBackground();
1277        mCallStateRegistrants.notifyRegistrants();
1278    }
1279
1280    public void triggerSsn(int type, int code) {
1281        SuppServiceNotification not = new SuppServiceNotification();
1282        not.notificationType = type;
1283        not.code = code;
1284        mSsnRegistrant.notifyRegistrant(new AsyncResult(null, not, null));
1285    }
1286
1287    public void
1288    shutdown() {
1289        setRadioState(RadioState.RADIO_UNAVAILABLE);
1290        Looper looper = mHandlerThread.getLooper();
1291        if (looper != null) {
1292            looper.quit();
1293        }
1294    }
1295
1296    /** hangup all */
1297
1298    public void
1299    triggerHangupAll() {
1300        simulatedCallState.triggerHangupAll();
1301        mCallStateRegistrants.notifyRegistrants();
1302    }
1303
1304    public void
1305    triggerIncomingSMS(String message) {
1306        //TODO
1307    }
1308
1309    public void
1310    pauseResponses() {
1311        pausedResponseCount++;
1312    }
1313
1314    public void
1315    resumeResponses() {
1316        pausedResponseCount--;
1317
1318        if (pausedResponseCount == 0) {
1319            for (int i = 0, s = pausedResponses.size(); i < s ; i++) {
1320                pausedResponses.get(i).sendToTarget();
1321            }
1322            pausedResponses.clear();
1323        } else {
1324            Log.e("GSM", "SimulatedCommands.resumeResponses < 0");
1325        }
1326    }
1327
1328    //***** Private Methods
1329
1330    private void unimplemented(Message result) {
1331        if (result != null) {
1332            AsyncResult.forMessage(result).exception
1333                = new RuntimeException("Unimplemented");
1334
1335            if (pausedResponseCount > 0) {
1336                pausedResponses.add(result);
1337            } else {
1338                result.sendToTarget();
1339            }
1340        }
1341    }
1342
1343    private void resultSuccess(Message result, Object ret) {
1344        if (result != null) {
1345            AsyncResult.forMessage(result).result = ret;
1346            if (pausedResponseCount > 0) {
1347                pausedResponses.add(result);
1348            } else {
1349                result.sendToTarget();
1350            }
1351        }
1352    }
1353
1354    private void resultFail(Message result, Throwable tr) {
1355        if (result != null) {
1356            AsyncResult.forMessage(result).exception = tr;
1357            if (pausedResponseCount > 0) {
1358                pausedResponses.add(result);
1359            } else {
1360                result.sendToTarget();
1361            }
1362        }
1363    }
1364
1365    // ***** Methods for CDMA support
1366    public void
1367    getDeviceIdentity(Message response) {
1368        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1369        unimplemented(response);
1370    }
1371
1372    public void
1373    getCDMASubscription(Message response) {
1374        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1375        unimplemented(response);
1376    }
1377
1378    public void
1379    setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response) {
1380        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1381        unimplemented(response);
1382    }
1383
1384    public void queryCdmaRoamingPreference(Message response) {
1385        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1386        unimplemented(response);
1387    }
1388
1389    public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
1390        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1391        unimplemented(response);
1392    }
1393
1394    public void
1395    setPhoneType(int phoneType) {
1396        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1397    }
1398
1399    public void getPreferredVoicePrivacy(Message result) {
1400        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1401        unimplemented(result);
1402    }
1403
1404    public void setPreferredVoicePrivacy(boolean enable, Message result) {
1405        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1406        unimplemented(result);
1407    }
1408
1409    /**
1410     *  Set the TTY mode
1411     *
1412     * @param ttyMode is one of the following:
1413     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
1414     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
1415     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
1416     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
1417     * @param response is callback message
1418     */
1419    public void setTTYMode(int ttyMode, Message response) {
1420        Log.w(LOG_TAG, "Not implemented in SimulatedCommands");
1421        unimplemented(response);
1422    }
1423
1424    /**
1425     *  Query the TTY mode
1426     * (AsyncResult)response.obj).result is an int[] with element [0] set to
1427     * tty mode:
1428     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
1429     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
1430     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
1431     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
1432     * @param response is callback message
1433     */
1434    public void queryTTYMode(Message response) {
1435        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1436        unimplemented(response);
1437    }
1438
1439    /**
1440     * {@inheritDoc}
1441     */
1442    public void sendCDMAFeatureCode(String FeatureCode, Message response) {
1443        Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1444        unimplemented(response);
1445    }
1446
1447    /**
1448     * {@inheritDoc}
1449     */
1450    public void sendCdmaSms(byte[] pdu, Message response){
1451       Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands");
1452    }
1453
1454    public void setCdmaBroadcastActivation(boolean activate, Message response) {
1455        unimplemented(response);
1456
1457    }
1458
1459    public void getCdmaBroadcastConfig(Message response) {
1460        unimplemented(response);
1461
1462    }
1463
1464    public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) {
1465        unimplemented(response);
1466
1467    }
1468
1469    public void forceDataDormancy(Message response) {
1470        unimplemented(response);
1471    }
1472
1473
1474    public void setGsmBroadcastActivation(boolean activate, Message response) {
1475        unimplemented(response);
1476    }
1477
1478
1479    public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
1480        unimplemented(response);
1481    }
1482
1483    public void getGsmBroadcastConfig(Message response) {
1484        unimplemented(response);
1485    }
1486
1487    @Override
1488    public void supplyIccPinForApp(String pin, String aid, Message response) {
1489        unimplemented(response);
1490    }
1491
1492    @Override
1493    public void supplyIccPukForApp(String puk, String newPin, String aid, Message response) {
1494        unimplemented(response);
1495    }
1496
1497    @Override
1498    public void supplyIccPin2ForApp(String pin2, String aid, Message response) {
1499        unimplemented(response);
1500    }
1501
1502    @Override
1503    public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message response) {
1504        unimplemented(response);
1505    }
1506
1507    @Override
1508    public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message response) {
1509        unimplemented(response);
1510    }
1511
1512    @Override
1513    public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr,
1514            Message response) {
1515        unimplemented(response);
1516    }
1517
1518    public void requestIsimAuthentication(String nonce, Message response) {
1519        unimplemented(response);
1520    }
1521
1522    public void getVoiceRadioTechnology(Message response) {
1523        unimplemented(response);
1524    }
1525}
1526