4 * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
7 * GiWoong Kim <giwoong.kim@samsung.com>
8 * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
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.
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.
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.
30 package org.tizen.emulator.skin.comm.sock;
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;
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;
64 public class SocketCommunicator implements ICommunicator {
66 public class DataTranfer {
68 private boolean isTransferState;
69 private byte[] receivedData;
72 private long maxWaitTime;
75 private DataTranfer() {
79 private void setData(byte[] data) {
80 this.receivedData = data;
81 isTransferState = false;
86 public static final int HEART_BEAT_INTERVAL = 1; //second
87 public static final int HEART_BEAT_EXPIRE = 15;
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
94 public final static int MAX_SEND_QUEUE_SIZE = 100000;
96 private static int reqId;
98 private Logger logger =
99 SkinLogger.getSkinLogger(SocketCommunicator.class).getLogger();
101 private EmulatorConfig config;
103 private long initialData;
104 private EmulatorSkin skin;
106 private Socket socket;
107 private DataInputStream dis;
108 private DataOutputStream dos;
110 private AtomicInteger heartbeatCount;
111 private boolean isTerminated;
112 private boolean isSensorDaemonStarted;
113 private boolean isRamdump;
114 private TimerTask heartbeatExecutor;
115 private Timer heartbeatTimer;
117 private DataTranfer screenShotDataTransfer;
118 private DataTranfer detailInfoTransfer;
119 private DataTranfer progressDataTransfer;
120 private DataTranfer brightnessDataTransfer;
122 private Thread sendThread;
123 private LinkedList<SkinSendData> sendQueue;
125 private ByteArrayOutputStream bao;
126 private DataOutputStream dataOutputStream;
128 public SocketCommunicator(EmulatorConfig config, int uId, EmulatorSkin skin) {
130 this.config = config;
134 this.screenShotDataTransfer = new DataTranfer();
135 this.screenShotDataTransfer.sleep = SCREENSHOT_WAIT_INTERVAL;
136 this.screenShotDataTransfer.maxWaitTime = SCREENSHOT_WAIT_LIMIT;
138 this.detailInfoTransfer = new DataTranfer();
139 this.detailInfoTransfer.sleep = DETAIL_INFO_WAIT_INTERVAL;
140 this.detailInfoTransfer.maxWaitTime = DETAIL_INFO_WAIT_LIMIT;
142 this.progressDataTransfer = new DataTranfer();
143 this.progressDataTransfer.sleep = SCREENSHOT_WAIT_INTERVAL;
144 this.progressDataTransfer.maxWaitTime = SCREENSHOT_WAIT_LIMIT;
146 this.brightnessDataTransfer = new DataTranfer();
147 this.brightnessDataTransfer.sleep = SCREENSHOT_WAIT_INTERVAL;
148 this.brightnessDataTransfer.maxWaitTime = SCREENSHOT_WAIT_LIMIT;
150 this.heartbeatCount = new AtomicInteger(0);
151 //this.heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
152 this.heartbeatTimer = new Timer();
156 int port = config.getArgInt( ArgsConstants.SERVER_PORT );
157 socket = new Socket( "127.0.0.1", port );
158 logger.info( "socket.isConnected():" + socket.isConnected() );
160 } catch ( UnknownHostException e ) {
161 logger.log( Level.SEVERE, e.getMessage(), e );
162 } catch ( IOException e ) {
163 logger.log( Level.SEVERE, e.getMessage(), e );
166 this.bao = new ByteArrayOutputStream();
167 this.dataOutputStream = new DataOutputStream(bao);
170 public void setInitialData(long data) {
171 this.initialData = data;
177 sendQueue = new LinkedList<SkinSendData>();
179 sendThread = new Thread("sendThread") {
181 List<SkinSendData> list = new ArrayList<SkinSendData>();
188 synchronized ( sendQueue ) {
190 if ( sendQueue.isEmpty() ) {
193 } catch ( InterruptedException e ) {
194 logger.log( Level.SEVERE, e.getMessage(), e );
198 SkinSendData sendData = null;
200 sendData = sendQueue.poll();
201 if ( null != sendData ) {
202 list.add( sendData );
210 for ( SkinSendData data : list ) {
211 sendToQEMUInternal( data );
216 if ( isTerminated ) {
229 dis = new DataInputStream( socket.getInputStream() );
230 dos = new DataOutputStream( socket.getOutputStream() );
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;
240 StartData startData = new StartData(initialData, width, height, scale, rotation);
241 logger.info("StartData" + startData);
243 sendToQEMU( SendCommand.SEND_START, startData );
245 } catch ( IOException e ) {
246 logger.log( Level.SEVERE, e.getMessage(), e );
251 boolean ignoreHeartbeat = config.getArgBoolean( ArgsConstants.TEST_HEART_BEAT_IGNORE );
253 if (ignoreHeartbeat) {
254 logger.info("Ignore Skin heartbeat.");
256 heartbeatExecutor = new TimerTask() {
259 increaseHeartbeatCount();
261 if (isHeartbeatExpired()) {
262 logger.info("heartbeat was expired");
268 heartbeatTimer.schedule(heartbeatExecutor, 1, HEART_BEAT_INTERVAL * 1000);
278 int reqId = dis.readInt();
279 short cmd = dis.readShort();
280 int length = dis.readInt();
282 if (logger.isLoggable(Level.FINE)) {
283 logger.fine("[Socket] read - reqId:" + reqId +
284 ", command:" + cmd + ", dataLength:" + length);
287 ReceiveCommand command = null;
290 command = ReceiveCommand.getValue(cmd);
291 } catch (IllegalArgumentException e) {
292 logger.severe("unknown command:" + cmd);
298 resetHeartbeatCount();
299 if ( logger.isLoggable( Level.FINE ) ) {
300 logger.fine( "received HEAR_BEAT from QEMU." );
302 sendToQEMU( SendCommand.RESPONSE_HEART_BEAT, null );
305 case SCREEN_SHOT_DATA: {
306 logger.info( "received SCREEN_SHOT_DATA from QEMU." );
307 receiveData( screenShotDataTransfer, length );
311 case DETAIL_INFO_DATA: {
312 logger.info( "received DETAIL_INFO_DATA from QEMU." );
313 receiveData( detailInfoTransfer, length );
317 case RAMDUMP_COMPLETE: {
318 logger.info("received RAMDUMP_COMPLETE from QEMU.");
319 setRamdumpFlag(false);
322 case BOOTING_PROGRESS: {
323 //logger.info("received BOOTING_PROGRESS from QEMU.");
325 resetDataTransfer(progressDataTransfer);
326 receiveData(progressDataTransfer, length);
328 byte[] receivedData = getReceivedData(progressDataTransfer);
329 if (null != receivedData) {
330 String strValue = new String(receivedData, 0, length - 1);
334 value = Integer.parseInt(strValue);
335 } catch (NumberFormatException e) {
339 /* draw progress bar */
340 if (skin.bootingProgress != null) {
341 skin.bootingProgress.setSelection(value);
343 if (value == 100 | value == 0) {
344 /* this means progressbar will be
346 if (skin.bootingProgress != null) {
347 skin.bootingProgress = null;
355 case BRIGHTNESS_VALUE: {
356 //logger.info("received BRIGHTNESS_VALUE from QEMU.");
358 resetDataTransfer(brightnessDataTransfer);
359 receiveData(brightnessDataTransfer, length);
361 byte[] receivedData = getReceivedData(brightnessDataTransfer);
362 if (null != receivedData) {
363 String strValue = new String(receivedData, 0, length - 1);
367 value = Integer.parseInt(strValue);
368 } catch (NumberFormatException e) {
373 skin.dispalyBrightness(false);
375 skin.dispalyBrightness(true);
381 case SENSOR_DAEMON_START: {
382 logger.info("received SENSOR_DAEMON_START from QEMU.");
383 synchronized (this) {
384 isSensorDaemonStarted = true;
389 logger.info("received RESPONSE_SHUTDOWN from QEMU.");
390 sendToQEMU(SendCommand.RESPONSE_SHUTDOWN, null);
395 logger.severe("Unknown command from QEMU. command:" + cmd);
400 } catch (IOException e) {
401 logger.log(Level.SEVERE, e.getMessage(), e);
409 private void receiveData(
410 DataTranfer dataTransfer, int length) throws IOException {
411 synchronized (dataTransfer) {
413 if (null != dataTransfer.timer) {
414 dataTransfer.timer.cancel();
417 byte[] data = readData(dis, length);
420 logger.info("finished receiving data from QEMU.");
422 logger.severe("Fail to receiving data from QEMU.");
425 dataTransfer.isTransferState = false;
426 dataTransfer.timer = null;
428 dataTransfer.setData(data);
429 dataTransfer.notifyAll();
434 private byte[] readData( DataInputStream is, int length ) throws IOException {
440 BufferedInputStream bfis = new BufferedInputStream( is, length );
441 byte[] data = new byte[length];
448 if ( total == length ) {
452 read = bfis.read( data, total, length - total );
455 if ( total < length ) {
464 logger.info( "finished reading stream. read:" + total );
470 public synchronized DataTranfer sendToQEMU(
471 SendCommand command, ISendData data, boolean useDataTransfer) {
473 DataTranfer dataTranfer = null;
475 if ( useDataTransfer ) {
477 if ( SendCommand.SCREEN_SHOT.equals( command ) ) {
478 dataTranfer = resetDataTransfer( screenShotDataTransfer );
479 } else if ( SendCommand.DETAIL_INFO.equals( command ) ) {
480 dataTranfer = resetDataTransfer( detailInfoTransfer );
484 sendToQEMU( command, data );
490 private DataTranfer resetDataTransfer( final DataTranfer dataTransfer ) {
492 synchronized ( dataTransfer ) {
494 if ( dataTransfer.isTransferState ) {
495 logger.severe( "Already transter state for getting data." );
499 dataTransfer.isTransferState = true;
501 Timer timer = new Timer();
502 dataTransfer.timer = timer;
504 TimerTask timerTask = new TimerTask() {
507 synchronized ( dataTransfer ) {
508 dataTransfer.isTransferState = false;
509 dataTransfer.timer = null;
510 dataTransfer.receivedData = null;
514 timer.schedule(timerTask, dataTransfer.maxWaitTime + 1000);
523 public void sendToQEMU(SendCommand command, ISendData data) {
524 synchronized(sendQueue) {
525 if (MAX_SEND_QUEUE_SIZE < sendQueue.size()) {
527 "Send queue size exceeded max value, do not push data into send queue.");
529 sendQueue.add(new SkinSendData(command, data));
530 sendQueue.notifyAll();
535 private void sendToQEMUInternal(SkinSendData sendData) {
538 if (null == sendData) {
542 SendCommand command = sendData.getCommand();
543 ISendData data = sendData.getSendData();
545 reqId = (Integer.MAX_VALUE == reqId) ? 0 : ++reqId;
547 dataOutputStream.writeInt(uId);
548 dataOutputStream.writeInt(reqId);
549 dataOutputStream.writeShort(command.value());
554 dataOutputStream.writeShort(length);
556 byte[] byteData = data.serialize();
557 length = (short) byteData.length;
558 dataOutputStream.writeShort(length);
559 dataOutputStream.write(byteData);
562 dataOutputStream.flush();
564 dos.write(bao.toByteArray());
569 if (logger.isLoggable(Level.FINE)) {
570 logger.fine("[Socket] write - uid:" + uId +
571 ", reqId:" + reqId + ", command:" + command.value()
572 + " - " + command.toString() + ", length:" + length);
576 if (logger.isLoggable(Level.FINE)) {
577 logger.fine("[Socket] data - " + data.toString());
581 } catch (IOException e) {
582 logger.log(Level.SEVERE, e.getMessage(), e);
587 public byte[] getReceivedData( DataTranfer dataTranfer ) {
589 if (null == dataTranfer) {
593 synchronized (dataTranfer) {
596 byte[] receivedData = null;
597 long sleep = dataTranfer.sleep;
598 long maxWaitTime = dataTranfer.maxWaitTime;
599 int limitCount = (int) ( maxWaitTime / sleep );
601 while ( dataTranfer.isTransferState ) {
603 if ( limitCount < count ) {
604 logger.severe( "time out for receiving data from skin server." );
605 dataTranfer.receivedData = null;
610 dataTranfer.wait( sleep );
611 } catch ( InterruptedException e ) {
612 logger.log( Level.SEVERE, e.getMessage(), e );
616 logger.info( "wait data... count:" + count );
620 receivedData = dataTranfer.receivedData;
621 dataTranfer.receivedData = null;
629 public Socket getSocket() {
633 public synchronized boolean isSensorDaemonStarted() {
634 return isSensorDaemonStarted;
637 public synchronized void setRamdumpFlag(boolean flag) {
641 public synchronized boolean getRamdumpFlag() {
645 private void increaseHeartbeatCount() {
646 int count = heartbeatCount.incrementAndGet();
648 if (logger.isLoggable(Level.FINE)) {
649 logger.info("HB count : " + count);
653 private boolean isHeartbeatExpired() {
654 return HEART_BEAT_EXPIRE < heartbeatCount.get();
657 private void resetHeartbeatCount() {
658 heartbeatCount.set(0);
660 if (logger.isLoggable(Level.FINE)) {
661 logger.info("HB count reset");
666 public void terminate() {
668 logger.info("terminated");
670 if (null != sendQueue) {
671 synchronized (sendQueue) {
672 sendQueue.notifyAll();
676 if (null != heartbeatTimer) {
677 heartbeatTimer.cancel();
680 IOUtil.closeSocket(socket);
684 dataOutputStream.close();
685 } catch (IOException e) {
689 synchronized (this) {
694 public void resetSkin( EmulatorSkin skin ) {
695 synchronized ( this ) {
704 private SendCommand command;
705 private ISendData data;
707 public SkinSendData(SendCommand command, ISendData data) {
708 this.command = command;
712 public SendCommand getCommand() {
716 public ISendData getSendData() {