• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.telephony.test;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.HandlerThread;
21 import android.os.Looper;
22 
23 import com.android.telephony.Rlog;
24 
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.io.UnsupportedEncodingException;
29 import java.net.InetSocketAddress;
30 import java.net.ServerSocket;
31 import java.net.Socket;
32 import java.util.List;
33 
34 // Also in ATChannel.java
35 class LineReader
36 {
37     /**
38      * Not threadsafe
39      * Assumes input is ASCII
40      */
41 
42     //***** Constants
43 
44     // For what it's worth, this is also the size of an
45     // OMAP CSMI mailbox
46     static final int BUFFER_SIZE = 0x1000;
47 
48     // just to prevent constant allocations
49     byte mBuffer[] = new byte[BUFFER_SIZE];
50 
51     //***** Instance Variables
52 
53     InputStream mInStream;
54 
LineReader(InputStream s)55     LineReader (InputStream s)
56     {
57         mInStream = s;
58     }
59 
60     String
getNextLine()61     getNextLine()
62     {
63         return getNextLine(false);
64     }
65 
66     String
getNextLineCtrlZ()67     getNextLineCtrlZ()
68     {
69         return getNextLine(true);
70     }
71 
72     /**
73      * Note: doesn't return the last incomplete line read on EOF, since
74      * it doesn't typically matter anyway
75      *
76      * Returns NULL on EOF
77      */
78 
79     String
getNextLine(boolean ctrlZ)80     getNextLine(boolean ctrlZ)
81     {
82         int i = 0;
83 
84         try {
85             for (;;) {
86                 int result;
87 
88                 result = mInStream.read();
89 
90                 if (result < 0) {
91                     return null;
92                 }
93 
94                 if (ctrlZ && result == 0x1a) {
95                     break;
96                 } else if (result == '\r' || result == '\n') {
97                     if (i == 0) {
98                         // Skip leading cr/lf
99                         continue;
100                     } else {
101                         break;
102                     }
103                 }
104 
105                 mBuffer[i++] = (byte)result;
106             }
107         } catch (IOException ex) {
108             return null;
109         } catch (IndexOutOfBoundsException ex) {
110             System.err.println("ATChannel: buffer overflow");
111         }
112 
113         try {
114             return new String(mBuffer, 0, i, "US-ASCII");
115         } catch (UnsupportedEncodingException ex) {
116             System.err.println("ATChannel: implausable UnsupportedEncodingException");
117             return null;
118         }
119     }
120 }
121 
122 
123 
124 class InterpreterEx extends Exception
125 {
126     @UnsupportedAppUsage
127     public
InterpreterEx(String result)128     InterpreterEx (String result)
129     {
130         mResult = result;
131     }
132 
133     String mResult;
134 }
135 
136 public class ModelInterpreter
137             implements Runnable, SimulatedRadioControl
138 {
139     static final int MAX_CALLS = 6;
140 
141     /** number of msec between dialing -> alerting and alerting->active */
142     static final int CONNECTING_PAUSE_MSEC = 5 * 100;
143 
144     static final String LOG_TAG = "ModelInterpreter";
145 
146     //***** Instance Variables
147 
148     InputStream mIn;
149     OutputStream mOut;
150     LineReader mLineReader;
151     ServerSocket mSS;
152 
153     private String mFinalResponse;
154 
155     SimulatedGsmCallState mSimulatedCallState;
156 
157     HandlerThread mHandlerThread;
158 
159     int mPausedResponseCount;
160     Object mPausedResponseMonitor = new Object();
161 
162     //***** Events
163 
164     static final int PROGRESS_CALL_STATE        = 1;
165 
166     //***** Constructor
167 
168     public
ModelInterpreter(InputStream in, OutputStream out)169     ModelInterpreter (InputStream in, OutputStream out)
170     {
171         mIn = in;
172         mOut = out;
173 
174         init();
175     }
176 
177     public
ModelInterpreter(InetSocketAddress sa)178     ModelInterpreter (InetSocketAddress sa) throws java.io.IOException
179     {
180         mSS = new ServerSocket();
181 
182         mSS.setReuseAddress(true);
183         mSS.bind(sa);
184 
185         init();
186     }
187 
188     private void
init()189     init()
190     {
191         new Thread(this, "ModelInterpreter").start();
192         mHandlerThread = new HandlerThread("ModelInterpreter");
193         mHandlerThread.start();
194         Looper looper = mHandlerThread.getLooper();
195         mSimulatedCallState = new SimulatedGsmCallState(looper);
196     }
197 
198     //***** Runnable Implementation
199 
200     @Override
run()201     public void run()
202     {
203         for (;;) {
204             if (mSS != null) {
205                 Socket s;
206 
207                 try {
208                     s = mSS.accept();
209                 } catch (java.io.IOException ex) {
210                     Rlog.w(LOG_TAG,
211                         "IOException on socket.accept(); stopping", ex);
212                     return;
213                 }
214 
215                 try {
216                     mIn = s.getInputStream();
217                     mOut = s.getOutputStream();
218                 } catch (java.io.IOException ex) {
219                     Rlog.w(LOG_TAG,
220                         "IOException on accepted socket(); re-listening", ex);
221                     continue;
222                 }
223 
224                 Rlog.i(LOG_TAG, "New connection accepted");
225             }
226 
227 
228             mLineReader = new LineReader (mIn);
229 
230             println ("Welcome");
231 
232             for (;;) {
233                 String line;
234 
235                 line = mLineReader.getNextLine();
236 
237                 //System.out.println("MI<< " + line);
238 
239                 if (line == null) {
240                     break;
241                 }
242 
243                 synchronized(mPausedResponseMonitor) {
244                     while (mPausedResponseCount > 0) {
245                         try {
246                             mPausedResponseMonitor.wait();
247                         } catch (InterruptedException ex) {
248                         }
249                     }
250                 }
251 
252                 synchronized (this) {
253                     try {
254                         mFinalResponse = "OK";
255                         processLine(line);
256                         println(mFinalResponse);
257                     } catch (InterpreterEx ex) {
258                         println(ex.mResult);
259                     } catch (RuntimeException ex) {
260                         ex.printStackTrace();
261                         println("ERROR");
262                     }
263                 }
264             }
265 
266             Rlog.i(LOG_TAG, "Disconnected");
267 
268             if (mSS == null) {
269                 // no reconnect in this case
270                 break;
271             }
272         }
273     }
274 
275 
276     //***** Instance Methods
277 
278     /** Start the simulated phone ringing */
279     @Override
280     public void
triggerRing(String number)281     triggerRing(String number)
282     {
283         synchronized (this) {
284             boolean success;
285 
286             success = mSimulatedCallState.triggerRing(number);
287 
288             if (success) {
289                 println ("RING");
290             }
291         }
292     }
293 
294     /** If a call is DIALING or ALERTING, progress it to the next state */
295     @Override
296     public void
progressConnectingCallState()297     progressConnectingCallState()
298     {
299         mSimulatedCallState.progressConnectingCallState();
300     }
301 
302 
303     /** If a call is DIALING or ALERTING, progress it all the way to ACTIVE */
304     @Override
305     public void
progressConnectingToActive()306     progressConnectingToActive()
307     {
308         mSimulatedCallState.progressConnectingToActive();
309     }
310 
311     /** automatically progress mobile originated calls to ACTIVE.
312      *  default to true
313      */
314     @Override
315     public void
setAutoProgressConnectingCall(boolean b)316     setAutoProgressConnectingCall(boolean b)
317     {
318         mSimulatedCallState.setAutoProgressConnectingCall(b);
319     }
320 
321     @Override
322     public void
setNextDialFailImmediately(boolean b)323     setNextDialFailImmediately(boolean b)
324     {
325         mSimulatedCallState.setNextDialFailImmediately(b);
326     }
327 
328     @Override
setNextCallFailCause(int gsmCause)329     public void setNextCallFailCause(int gsmCause)
330     {
331         //FIXME implement
332     }
333 
334 
335     /** hangup ringing, dialing, or actuve calls */
336     @Override
337     public void
triggerHangupForeground()338     triggerHangupForeground()
339     {
340         boolean success;
341 
342         success = mSimulatedCallState.triggerHangupForeground();
343 
344         if (success) {
345             println ("NO CARRIER");
346         }
347     }
348 
349     /** hangup holding calls */
350     @Override
351     public void
triggerHangupBackground()352     triggerHangupBackground()
353     {
354         boolean success;
355 
356         success = mSimulatedCallState.triggerHangupBackground();
357 
358         if (success) {
359             println ("NO CARRIER");
360         }
361     }
362 
363     /** hangup all */
364 
365     @Override
366     public void
triggerHangupAll()367     triggerHangupAll()
368     {
369         boolean success;
370 
371         success = mSimulatedCallState.triggerHangupAll();
372 
373         if (success) {
374             println ("NO CARRIER");
375         }
376     }
377 
378     public void
sendUnsolicited(String unsol)379     sendUnsolicited (String unsol)
380     {
381         synchronized (this) {
382             println(unsol);
383         }
384     }
385 
386     @Override
triggerSsn(int a, int b)387     public void triggerSsn(int a, int b) {}
388     @Override
triggerIncomingUssd(String statusCode, String message)389     public void triggerIncomingUssd(String statusCode, String message) {}
390 
391     @Override
392     public void
triggerIncomingSMS(String message)393     triggerIncomingSMS(String message)
394     {
395 /**************
396         StringBuilder pdu = new StringBuilder();
397 
398         pdu.append ("00");      //SMSC address - 0 bytes
399 
400         pdu.append ("04");      // Message type indicator
401 
402         // source address: +18005551212
403         pdu.append("918100551521F0");
404 
405         // protocol ID and data coding scheme
406         pdu.append("0000");
407 
408         Calendar c = Calendar.getInstance();
409 
410         pdu.append (c.
411 
412 
413 
414         synchronized (this) {
415             println("+CMT: ,1\r" + pdu.toString());
416         }
417 
418 **************/
419     }
420 
421     @Override
422     public void
pauseResponses()423     pauseResponses()
424     {
425         synchronized(mPausedResponseMonitor) {
426             mPausedResponseCount++;
427         }
428     }
429 
430     @Override
431     public void
resumeResponses()432     resumeResponses()
433     {
434         synchronized(mPausedResponseMonitor) {
435             mPausedResponseCount--;
436 
437             if (mPausedResponseCount == 0) {
438                 mPausedResponseMonitor.notifyAll();
439             }
440         }
441     }
442 
443     //***** Private Instance Methods
444 
445     private void
onAnswer()446     onAnswer() throws InterpreterEx
447     {
448         boolean success;
449 
450         success = mSimulatedCallState.onAnswer();
451 
452         if (!success) {
453             throw new InterpreterEx("ERROR");
454         }
455     }
456 
457     private void
onHangup()458     onHangup() throws InterpreterEx
459     {
460         boolean success = false;
461 
462         success = mSimulatedCallState.onAnswer();
463 
464         if (!success) {
465             throw new InterpreterEx("ERROR");
466         }
467 
468         mFinalResponse = "NO CARRIER";
469     }
470 
471     private void
onCHLD(String command)472     onCHLD(String command) throws InterpreterEx
473     {
474         // command starts with "+CHLD="
475         char c0;
476         char c1 = 0;
477         boolean success;
478 
479         c0 = command.charAt(6);
480 
481         if (command.length() >= 8) {
482             c1 = command.charAt(7);
483         }
484 
485         success = mSimulatedCallState.onChld(c0, c1);
486 
487         if (!success) {
488             throw new InterpreterEx("ERROR");
489         }
490     }
491 
492     private void
onDial(String command)493     onDial(String command) throws InterpreterEx
494     {
495         boolean success;
496 
497         success = mSimulatedCallState.onDial(command.substring(1));
498 
499         if (!success) {
500             throw new InterpreterEx("ERROR");
501         }
502     }
503 
504     private void
onCLCC()505     onCLCC()
506     {
507         List<String> lines;
508 
509         lines = mSimulatedCallState.getClccLines();
510 
511         for (int i = 0, s = lines.size() ; i < s ; i++) {
512             println (lines.get(i));
513         }
514     }
515 
516     private void
onSMSSend(String command)517     onSMSSend(String command)
518     {
519         String pdu;
520 
521         print ("> ");
522         pdu = mLineReader.getNextLineCtrlZ();
523 
524         println("+CMGS: 1");
525     }
526 
527     void
processLine(String line)528     processLine (String line) throws InterpreterEx
529     {
530         String[] commands;
531 
532         commands = splitCommands(line);
533 
534         for (int i = 0; i < commands.length ; i++) {
535             String command = commands[i];
536 
537             if (command.equals("A")) {
538                 onAnswer();
539             } else if (command.equals("H")) {
540                 onHangup();
541             } else if (command.startsWith("+CHLD=")) {
542                 onCHLD(command);
543             } else if (command.equals("+CLCC")) {
544                 onCLCC();
545             } else if (command.startsWith("D")) {
546                 onDial(command);
547             } else if (command.startsWith("+CMGS=")) {
548                 onSMSSend(command);
549             } else {
550                 boolean found = false;
551 
552                 for (int j = 0; j < sDefaultResponses.length ; j++) {
553                     if (command.equals(sDefaultResponses[j][0])) {
554                         String r = sDefaultResponses[j][1];
555                         if (r != null) {
556                             println(r);
557                         }
558                         found = true;
559                         break;
560                     }
561                 }
562 
563                 if (!found) {
564                     throw new InterpreterEx ("ERROR");
565                 }
566             }
567         }
568     }
569 
570 
571     String[]
splitCommands(String line)572     splitCommands(String line) throws InterpreterEx
573     {
574         if (!line.startsWith ("AT")) {
575             throw new InterpreterEx("ERROR");
576         }
577 
578         if (line.length() == 2) {
579             // Just AT by itself
580             return new String[0];
581         }
582 
583         String ret[] = new String[1];
584 
585         //TODO fix case here too
586         ret[0] = line.substring(2);
587 
588         return ret;
589 /****
590         try {
591             // i = 2 to skip over AT
592             for (int i = 2, s = line.length() ; i < s ; i++) {
593                 // r"|([A-RT-Z]\d?)" # Normal commands eg ATA or I0
594                 // r"|(&[A-Z]\d*)" # & commands eg &C
595                 // r"|(S\d+(=\d+)?)" # S registers
596                 // r"((\+|%)\w+(\?|=([^;]+(;|$)))?)" # extended command eg +CREG=2
597 
598 
599             }
600         } catch (StringIndexOutOfBoundsException ex) {
601             throw new InterpreterEx ("ERROR");
602         }
603 ***/
604     }
605 
606     void
println(String s)607     println (String s)
608     {
609         synchronized(this) {
610             try {
611                 byte[] bytes =  s.getBytes("US-ASCII");
612 
613                 //System.out.println("MI>> " + s);
614 
615                 mOut.write(bytes);
616                 mOut.write('\r');
617             } catch (IOException ex) {
618                 ex.printStackTrace();
619             }
620         }
621     }
622 
623     void
print(String s)624     print (String s)
625     {
626         synchronized(this) {
627             try {
628                 byte[] bytes =  s.getBytes("US-ASCII");
629 
630                 //System.out.println("MI>> " + s + " (no <cr>)");
631 
632                 mOut.write(bytes);
633             } catch (IOException ex) {
634                 ex.printStackTrace();
635             }
636         }
637     }
638 
639 
640     @Override
641     public void
shutdown()642     shutdown()
643     {
644         Looper looper = mHandlerThread.getLooper();
645         if (looper != null) {
646             looper.quit();
647         }
648 
649         try {
650             mIn.close();
651         } catch (IOException ex) {
652         }
653         try {
654             mOut.close();
655         } catch (IOException ex) {
656         }
657     }
658 
659 
660     static final String [][] sDefaultResponses = {
661         {"E0Q0V1",   null},
662         {"+CMEE=2",  null},
663         {"+CREG=2",  null},
664         {"+CGREG=2", null},
665         {"+CCWA=1",  null},
666         {"+COPS=0",  null},
667         {"+CFUN=1",  null},
668         {"+CGMI",    "+CGMI: Android Model AT Interpreter\r"},
669         {"+CGMM",    "+CGMM: Android Model AT Interpreter\r"},
670         {"+CGMR",    "+CGMR: 1.0\r"},
671         {"+CGSN",    "000000000000000\r"},
672         {"+CIMI",    "320720000000000\r"},
673         {"+CSCS=?",  "+CSCS: (\"HEX\",\"UCS2\")\r"},
674         {"+CFUN?",   "+CFUN: 1\r"},
675         {"+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?",
676                 "+COPS: 0,0,\"Android\"\r"
677                 + "+COPS: 0,1,\"Android\"\r"
678                 + "+COPS: 0,2,\"310995\"\r"},
679         {"+CREG?",   "+CREG: 2,5, \"0113\", \"6614\"\r"},
680         {"+CGREG?",  "+CGREG: 2,0\r"},
681         {"+CSQ",     "+CSQ: 16,99\r"},
682         {"+CNMI?",   "+CNMI: 1,2,2,1,1\r"},
683         {"+CLIR?",   "+CLIR: 1,3\r"},
684         {"%CPVWI=2", "%CPVWI: 0\r"},
685         {"+CUSD=1,\"#646#\"",  "+CUSD=0,\"You have used 23 minutes\"\r"},
686         {"+CRSM=176,12258,0,0,10", "+CRSM: 144,0,981062200050259429F6\r"},
687         {"+CRSM=192,12258,0,0,15", "+CRSM: 144,0,0000000A2FE204000FF55501020000\r"},
688 
689         /* EF[ADN] */
690         {"+CRSM=192,28474,0,0,15", "+CRSM: 144,0,0000005a6f3a040011f5220102011e\r"},
691         {"+CRSM=178,28474,1,4,30", "+CRSM: 144,0,437573746f6d65722043617265ffffff07818100398799f7ffffffffffff\r"},
692         {"+CRSM=178,28474,2,4,30", "+CRSM: 144,0,566f696365204d61696cffffffffffff07918150367742f3ffffffffffff\r"},
693         {"+CRSM=178,28474,3,4,30", "+CRSM: 144,0,4164676a6dffffffffffffffffffffff0b918188551512c221436587ff01\r"},
694         {"+CRSM=178,28474,4,4,30", "+CRSM: 144,0,810101c1ffffffffffffffffffffffff068114455245f8ffffffffffffff\r"},
695         /* EF[EXT1] */
696         {"+CRSM=192,28490,0,0,15", "+CRSM: 144,0,000000416f4a040011f5550102010d\r"},
697         {"+CRSM=178,28490,1,4,13", "+CRSM: 144,0,0206092143658709ffffffffff\r"}
698     };
699 }
700