update source for tizen_2.1
[sdk/emulator/qemu.git] / tizen / src / skin / client / src / org / tizen / emulator / skin / comm / sock / SocketCommunicator.java
1 /**
2  * 
3  *
4  * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:
7  * GiWoong Kim <giwoong.kim@samsung.com>
8  * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
9  * HyunJun Son
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24  *
25  * Contributors:
26  * - S-Core Co., Ltd
27  *
28  */
29
30 package org.tizen.emulator.skin.comm.sock;
31
32 import java.io.BufferedInputStream;
33 import java.io.ByteArrayOutputStream;
34 import java.io.DataInputStream;
35 import java.io.DataOutputStream;
36 import java.io.IOException;
37 import java.net.Socket;
38 import java.net.UnknownHostException;
39 import java.util.ArrayList;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.Timer;
43 import java.util.TimerTask;
44 import java.util.concurrent.atomic.AtomicInteger;
45 import java.util.logging.Level;
46 import java.util.logging.Logger;
47
48 import org.tizen.emulator.skin.EmulatorSkin;
49 import org.tizen.emulator.skin.comm.ICommunicator;
50 import org.tizen.emulator.skin.comm.ICommunicator.SendCommand;
51 import org.tizen.emulator.skin.comm.sock.data.ISendData;
52 import org.tizen.emulator.skin.comm.sock.data.StartData;
53 import org.tizen.emulator.skin.config.EmulatorConfig;
54 import org.tizen.emulator.skin.config.EmulatorConfig.ArgsConstants;
55 import org.tizen.emulator.skin.log.SkinLogger;
56 import org.tizen.emulator.skin.util.IOUtil;
57 import org.tizen.emulator.skin.util.SkinUtil;
58
59
60 /**
61  * 
62  * 
63  */
64 public class SocketCommunicator implements ICommunicator {
65
66         public class DataTranfer {
67
68                 private boolean isTransferState;
69                 private byte[] receivedData;
70
71                 private long sleep;
72                 private long maxWaitTime;
73                 private Timer timer;
74                 
75                 private DataTranfer() {
76                         /* do nothing */
77                 }
78
79                 private void setData(byte[] data) {
80                         this.receivedData = data;
81                         isTransferState = false;
82                 }
83
84         }
85
86         public static final int HEART_BEAT_INTERVAL = 1; //second
87         public static final int HEART_BEAT_EXPIRE = 15;
88
89         public final static int SCREENSHOT_WAIT_INTERVAL = 3; // milli-seconds
90         public final static int SCREENSHOT_WAIT_LIMIT = 3000; // milli-seconds
91         public final static int DETAIL_INFO_WAIT_INTERVAL = 1; // milli-seconds
92         public final static int DETAIL_INFO_WAIT_LIMIT = 3000; // milli-seconds
93
94         public final static int MAX_SEND_QUEUE_SIZE = 100000;
95
96         private static int reqId;
97
98         private Logger logger =
99                         SkinLogger.getSkinLogger(SocketCommunicator.class).getLogger();
100
101         private EmulatorConfig config;
102         private int uId;
103         private long initialData;
104         private EmulatorSkin skin;
105
106         private Socket socket;
107         private DataInputStream dis;
108         private DataOutputStream dos;
109
110         private AtomicInteger heartbeatCount;
111         private boolean isTerminated;
112         private boolean isSensorDaemonStarted;
113         private boolean isRamdump;
114         private TimerTask heartbeatExecutor;
115         private Timer heartbeatTimer;
116
117         private DataTranfer screenShotDataTransfer;
118         private DataTranfer detailInfoTransfer;
119         private DataTranfer progressDataTransfer;
120         private DataTranfer brightnessDataTransfer;
121
122         private Thread sendThread;
123         private LinkedList<SkinSendData> sendQueue;
124
125         private ByteArrayOutputStream bao;
126         private DataOutputStream dataOutputStream;
127
128         public SocketCommunicator(EmulatorConfig config, int uId, EmulatorSkin skin) {
129
130                 this.config = config;
131                 this.uId = uId;
132                 this.skin = skin;
133
134                 this.screenShotDataTransfer = new DataTranfer();
135                 this.screenShotDataTransfer.sleep = SCREENSHOT_WAIT_INTERVAL;
136                 this.screenShotDataTransfer.maxWaitTime = SCREENSHOT_WAIT_LIMIT;
137
138                 this.detailInfoTransfer = new DataTranfer();
139                 this.detailInfoTransfer.sleep = DETAIL_INFO_WAIT_INTERVAL;
140                 this.detailInfoTransfer.maxWaitTime = DETAIL_INFO_WAIT_LIMIT;
141
142                 this.progressDataTransfer = new DataTranfer();
143                 this.progressDataTransfer.sleep = SCREENSHOT_WAIT_INTERVAL;
144                 this.progressDataTransfer.maxWaitTime = SCREENSHOT_WAIT_LIMIT;
145
146                 this.brightnessDataTransfer = new DataTranfer();
147                 this.brightnessDataTransfer.sleep = SCREENSHOT_WAIT_INTERVAL;
148                 this.brightnessDataTransfer.maxWaitTime = SCREENSHOT_WAIT_LIMIT;
149
150                 this.heartbeatCount = new AtomicInteger(0);
151                 //this.heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
152                 this.heartbeatTimer = new Timer();
153
154                 try {
155
156                         int port = config.getArgInt( ArgsConstants.SERVER_PORT );
157                         socket = new Socket( "127.0.0.1", port );
158                         logger.info( "socket.isConnected():" + socket.isConnected() );
159
160                 } catch ( UnknownHostException e ) {
161                         logger.log( Level.SEVERE, e.getMessage(), e );
162                 } catch ( IOException e ) {
163                         logger.log( Level.SEVERE, e.getMessage(), e );
164                 }
165
166                 this.bao = new ByteArrayOutputStream();
167                 this.dataOutputStream = new DataOutputStream(bao);
168         }
169
170         public void setInitialData(long data) {
171                 this.initialData = data;
172         }
173
174         @Override
175         public void run() {
176
177                 sendQueue = new LinkedList<SkinSendData>();
178
179                 sendThread = new Thread("sendThread") {
180
181                         List<SkinSendData> list = new ArrayList<SkinSendData>();
182
183                         @Override
184                         public void run() {
185
186                                 while ( true ) {
187
188                                         synchronized ( sendQueue ) {
189
190                                                 if ( sendQueue.isEmpty() ) {
191                                                         try {
192                                                                 sendQueue.wait();
193                                                         } catch ( InterruptedException e ) {
194                                                                 logger.log( Level.SEVERE, e.getMessage(), e );
195                                                         }
196                                                 }
197
198                                                 SkinSendData sendData = null;
199                                                 while ( true ) {
200                                                         sendData = sendQueue.poll();
201                                                         if ( null != sendData ) {
202                                                                 list.add( sendData );
203                                                         } else {
204                                                                 break;
205                                                         }
206                                                 }
207
208                                         }
209
210                                         for ( SkinSendData data : list ) {
211                                                 sendToQEMUInternal( data );
212                                         }
213
214                                         list.clear();
215
216                                         if ( isTerminated ) {
217                                                 break;
218                                         }
219
220                                 }
221
222                         }
223                 };
224
225                 sendThread.start();
226
227                 try {
228
229                         dis = new DataInputStream( socket.getInputStream() );
230                         dos = new DataOutputStream( socket.getOutputStream() );
231
232                         int width = config.getArgInt( ArgsConstants.RESOLUTION_WIDTH );
233                         int height = config.getArgInt( ArgsConstants.RESOLUTION_HEIGHT );
234                         int scale = SkinUtil.getValidScale( config );
235 //                      short rotation = config.getSkinPropertyShort( SkinPropertiesConstants.WINDOW_ROTATION,
236 //                                      EmulatorConfig.DEFAULT_WINDOW_ROTATION );
237                         // has to be portrait mode at first booting time
238                         short rotation = EmulatorConfig.DEFAULT_WINDOW_ROTATION;
239
240                         StartData startData = new StartData(initialData, width, height, scale, rotation);
241                         logger.info("StartData" + startData);
242
243                         sendToQEMU( SendCommand.SEND_START, startData );
244
245                 } catch ( IOException e ) {
246                         logger.log( Level.SEVERE, e.getMessage(), e );
247                         terminate();
248                         return;
249                 }
250
251                 boolean ignoreHeartbeat = config.getArgBoolean( ArgsConstants.TEST_HEART_BEAT_IGNORE );
252
253                 if (ignoreHeartbeat) {
254                         logger.info("Ignore Skin heartbeat.");
255                 } else {
256                         heartbeatExecutor = new TimerTask() {
257                                 @Override
258                                 public void run() {
259                                         increaseHeartbeatCount();
260
261                                         if (isHeartbeatExpired()) {
262                                                 logger.info("heartbeat was expired");
263                                                 terminate();
264                                         }
265                                 }
266                         };
267
268                         heartbeatTimer.schedule(heartbeatExecutor, 1, HEART_BEAT_INTERVAL * 1000);
269                 }
270
271                 while (true) {
272                         if (isTerminated) {
273                                 break;
274                         }
275
276                         try {
277
278                                 int reqId = dis.readInt();
279                                 short cmd = dis.readShort();
280                                 int length = dis.readInt();
281
282                                 if (logger.isLoggable(Level.FINE)) {
283                                         logger.fine("[Socket] read - reqId:" + reqId +
284                                                         ", command:" + cmd + ", dataLength:" + length);
285                                 }
286
287                                 ReceiveCommand command = null;
288
289                                 try {
290                                         command = ReceiveCommand.getValue(cmd);
291                                 } catch (IllegalArgumentException e) {
292                                         logger.severe("unknown command:" + cmd);
293                                         continue;
294                                 }
295
296                                 switch ( command ) {
297                                 case HEART_BEAT: {
298                                         resetHeartbeatCount();
299                                         if ( logger.isLoggable( Level.FINE ) ) {
300                                                 logger.fine( "received HEAR_BEAT from QEMU." );
301                                         }
302                                         sendToQEMU( SendCommand.RESPONSE_HEART_BEAT, null );
303                                         break;
304                                 }
305                                 case SCREEN_SHOT_DATA: {
306                                         logger.info( "received SCREEN_SHOT_DATA from QEMU." );
307                                         receiveData( screenShotDataTransfer, length );
308
309                                         break;
310                                 }
311                                 case DETAIL_INFO_DATA: {
312                                         logger.info( "received DETAIL_INFO_DATA from QEMU." );
313                                         receiveData( detailInfoTransfer, length );
314
315                                         break;
316                                 }
317                                 case RAMDUMP_COMPLETE: {
318                                         logger.info("received RAMDUMP_COMPLETE from QEMU.");
319                                         setRamdumpFlag(false);
320                                         break;
321                                 }
322                                 case BOOTING_PROGRESS: {
323                                         //logger.info("received BOOTING_PROGRESS from QEMU.");
324
325                                         resetDataTransfer(progressDataTransfer);
326                                         receiveData(progressDataTransfer, length);
327
328                                         byte[] receivedData = getReceivedData(progressDataTransfer);
329                                         if (null != receivedData) {
330                                                 String strValue = new String(receivedData, 0, length - 1);
331
332                                                 int value = 0;
333                                                 try {
334                                                         value = Integer.parseInt(strValue);
335                                                 } catch (NumberFormatException e) {
336                                                         e.printStackTrace();
337                                                 }
338
339                                                 /* draw progress bar */
340                                                 if (skin.bootingProgress != null) {
341                                                         skin.bootingProgress.setSelection(value);
342
343                                                         if (value == 100 | value == 0) {
344                                                                 /* this means progressbar will be
345                                                                 disposed soon */
346                                                                 if (skin.bootingProgress != null) {
347                                                                         skin.bootingProgress = null;
348                                                                 }
349                                                         }
350                                                 }
351                                         }
352
353                                         break;
354                                 }
355                                 case BRIGHTNESS_VALUE: {
356                                         //logger.info("received BRIGHTNESS_VALUE from QEMU.");
357
358                                         resetDataTransfer(brightnessDataTransfer);
359                                         receiveData(brightnessDataTransfer, length);
360
361                                         byte[] receivedData = getReceivedData(brightnessDataTransfer);
362                                         if (null != receivedData) {
363                                                 String strValue = new String(receivedData, 0, length - 1);
364
365                                                 int value = 1;
366                                                 try {
367                                                         value = Integer.parseInt(strValue);
368                                                 } catch (NumberFormatException e) {
369                                                         e.printStackTrace();
370                                                 }
371
372                                                 if (value == 0) {
373                                                         skin.dispalyBrightness(false);
374                                                 } else {
375                                                         skin.dispalyBrightness(true);
376                                                 }
377                                         }
378
379                                         break;
380                                 }
381                                 case SENSOR_DAEMON_START: {
382                                         logger.info("received SENSOR_DAEMON_START from QEMU.");
383                                         synchronized (this) {
384                                                 isSensorDaemonStarted = true;
385                                         }
386                                         break;
387                                 }
388                                 case SHUTDOWN: {
389                                         logger.info("received RESPONSE_SHUTDOWN from QEMU.");
390                                         sendToQEMU(SendCommand.RESPONSE_SHUTDOWN, null);
391                                         terminate();
392                                         break;
393                                 }
394                                 default: {
395                                         logger.severe("Unknown command from QEMU. command:" + cmd);
396                                         break;
397                                 }
398                                 }
399
400                         } catch (IOException e) {
401                                 logger.log(Level.SEVERE, e.getMessage(), e);
402                                 break;
403                         }
404
405                 }
406
407         }
408
409         private void receiveData(
410                         DataTranfer dataTransfer, int length) throws IOException {
411                 synchronized (dataTransfer) {
412
413                         if (null != dataTransfer.timer) {
414                                 dataTransfer.timer.cancel();
415                         }
416
417                         byte[] data = readData(dis, length);
418
419                         if (null != data) {
420                                 logger.info("finished receiving data from QEMU.");
421                         } else {
422                                 logger.severe("Fail to receiving data from QEMU.");
423                         }
424
425                         dataTransfer.isTransferState = false;
426                         dataTransfer.timer = null;
427                         
428                         dataTransfer.setData(data);
429                         dataTransfer.notifyAll();
430                 }
431
432         }
433
434         private byte[] readData( DataInputStream is, int length ) throws IOException {
435
436                 if ( 0 >= length ) {
437                         return null;
438                 }
439
440                 BufferedInputStream bfis = new BufferedInputStream( is, length );
441                 byte[] data = new byte[length];
442
443                 int read = 0;
444                 int total = 0;
445
446                 while ( true ) {
447
448                         if ( total == length ) {
449                                 break;
450                         }
451
452                         read = bfis.read( data, total, length - total );
453
454                         if ( 0 > read ) {
455                                 if ( total < length ) {
456                                         continue;
457                                 }
458                         } else {
459                                 total += read;
460                         }
461
462                 }
463
464                 logger.info( "finished reading stream. read:" + total );
465
466                 return data;
467
468         }
469
470         public synchronized DataTranfer sendToQEMU(
471                         SendCommand command, ISendData data, boolean useDataTransfer) {
472
473                 DataTranfer dataTranfer = null;
474                 
475                 if ( useDataTransfer ) {
476
477                         if ( SendCommand.SCREEN_SHOT.equals( command ) ) {
478                                 dataTranfer = resetDataTransfer( screenShotDataTransfer );
479                         } else if ( SendCommand.DETAIL_INFO.equals( command ) ) {
480                                 dataTranfer = resetDataTransfer( detailInfoTransfer );
481                         }
482                 }
483
484                 sendToQEMU( command, data );
485                 
486                 return dataTranfer;
487
488         }
489         
490         private DataTranfer resetDataTransfer( final DataTranfer dataTransfer ) {
491                 
492                 synchronized ( dataTransfer ) {
493                         
494                         if ( dataTransfer.isTransferState ) {
495                                 logger.severe( "Already transter state for getting data." );
496                                 return null;
497                         }
498
499                         dataTransfer.isTransferState = true;
500
501                         Timer timer = new Timer();
502                         dataTransfer.timer = timer;
503
504                         TimerTask timerTask = new TimerTask() {
505                                 @Override
506                                 public void run() {
507                                         synchronized ( dataTransfer ) {
508                                                 dataTransfer.isTransferState = false;
509                                                 dataTransfer.timer = null;
510                                                 dataTransfer.receivedData = null;
511                                         }
512                                 }
513                         };
514                         timer.schedule(timerTask, dataTransfer.maxWaitTime + 1000);
515
516                         return dataTransfer;
517
518                 }
519
520         }
521         
522         @Override
523         public void sendToQEMU(SendCommand command, ISendData data) {
524                 synchronized(sendQueue) {
525                         if (MAX_SEND_QUEUE_SIZE < sendQueue.size()) {
526                                 logger.warning(
527                                                 "Send queue size exceeded max value, do not push data into send queue.");
528                         } else {
529                                 sendQueue.add(new SkinSendData(command, data));
530                                 sendQueue.notifyAll();
531                         }
532                 }
533         }
534         
535         private void sendToQEMUInternal(SkinSendData sendData) {
536                 try {
537
538                         if (null == sendData) {
539                                 return;
540                         }
541                         
542                         SendCommand command = sendData.getCommand();
543                         ISendData data = sendData.getSendData();
544
545                         reqId = (Integer.MAX_VALUE == reqId) ? 0 : ++reqId;
546
547                         dataOutputStream.writeInt(uId);
548                         dataOutputStream.writeInt(reqId);
549                         dataOutputStream.writeShort(command.value());
550
551                         short length = 0;
552                         if (null == data) {
553                                 length = 0;
554                                 dataOutputStream.writeShort(length);
555                         } else {
556                                 byte[] byteData = data.serialize();
557                                 length = (short) byteData.length;
558                                 dataOutputStream.writeShort(length);
559                                 dataOutputStream.write(byteData);
560                         }
561
562                         dataOutputStream.flush();
563
564                         dos.write(bao.toByteArray());
565                         dos.flush();
566
567                         bao.reset();
568
569                         if (logger.isLoggable(Level.FINE)) {
570                                 logger.fine("[Socket] write - uid:" + uId +
571                                                 ", reqId:" + reqId + ", command:" + command.value()
572                                                 + " - " + command.toString() + ", length:" + length);
573                         }
574
575                         if (0 < length) {
576                                 if (logger.isLoggable(Level.FINE)) {
577                                         logger.fine("[Socket] data  - " + data.toString());
578                                 }
579                         }
580
581                 } catch (IOException e) {
582                         logger.log(Level.SEVERE, e.getMessage(), e);
583                 }
584
585         }
586
587         public byte[] getReceivedData( DataTranfer dataTranfer ) {
588
589                 if (null == dataTranfer) {
590                         return null;
591                 }
592
593                 synchronized (dataTranfer) {
594
595                         int count = 0;
596                         byte[] receivedData = null;
597                         long sleep = dataTranfer.sleep;
598                         long maxWaitTime = dataTranfer.maxWaitTime;
599                         int limitCount = (int) ( maxWaitTime / sleep );
600
601                         while ( dataTranfer.isTransferState ) {
602
603                                 if ( limitCount < count ) {
604                                         logger.severe( "time out for receiving data from skin server." );
605                                         dataTranfer.receivedData = null;
606                                         break;
607                                 }
608
609                                 try {
610                                         dataTranfer.wait( sleep );
611                                 } catch ( InterruptedException e ) {
612                                         logger.log( Level.SEVERE, e.getMessage(), e );
613                                 }
614
615                                 count++;
616                                 logger.info( "wait data... count:" + count );
617
618                         }
619
620                         receivedData = dataTranfer.receivedData;
621                         dataTranfer.receivedData = null;
622                         
623                         return receivedData;
624
625                 }
626
627         }
628         
629         public Socket getSocket() {
630                 return socket;
631         }
632
633         public synchronized boolean isSensorDaemonStarted() {
634                 return isSensorDaemonStarted;
635         }
636
637         public synchronized void setRamdumpFlag(boolean flag) {
638                 isRamdump = flag;
639         }
640
641         public synchronized boolean getRamdumpFlag() {
642                 return isRamdump;
643         }
644
645         private void increaseHeartbeatCount() {
646                 int count = heartbeatCount.incrementAndGet();
647
648                 if (logger.isLoggable(Level.FINE)) {
649                         logger.info("HB count : " + count);
650                 }
651         }
652
653         private boolean isHeartbeatExpired() {
654                 return HEART_BEAT_EXPIRE < heartbeatCount.get();
655         }
656
657         private void resetHeartbeatCount() {
658                 heartbeatCount.set(0);
659
660                 if (logger.isLoggable(Level.FINE)) {
661                         logger.info("HB count reset");
662                 }
663         }
664
665         @Override
666         public void terminate() {
667                 isTerminated = true;
668                 logger.info("terminated");
669
670                 if (null != sendQueue) {
671                         synchronized (sendQueue) {
672                                 sendQueue.notifyAll();
673                         }
674                 }
675
676                 if (null != heartbeatTimer) {
677                         heartbeatTimer.cancel();
678                 }
679
680                 IOUtil.closeSocket(socket);
681
682                 try {
683                         bao.close();
684                         dataOutputStream.close();
685                 } catch (IOException e) {
686                         e.printStackTrace();
687                 }
688
689                 synchronized (this) {
690                         skin.shutdown();
691                 }
692         }
693
694         public void resetSkin( EmulatorSkin skin ) {
695                 synchronized ( this ) {
696                         this.skin = skin;
697                 }
698         }
699
700 }
701
702 class SkinSendData {
703
704         private SendCommand command;
705         private ISendData data;
706
707         public SkinSendData(SendCommand command, ISendData data) {
708                 this.command = command;
709                 this.data = data;
710         }
711
712         public SendCommand getCommand() {
713                 return command;
714         }
715
716         public ISendData getSendData() {
717                 return data;
718         }
719
720 }