Home | History | Annotate | Download | only in service
      1 /*
      2  * ConnectBot: simple, powerful, open-source SSH client for Android
      3  * Copyright 2007 Kenny Root, Jeffrey Sharkey
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package org.connectbot.service;
     19 
     20 import com.googlecode.android_scripting.Log;
     21 
     22 import de.mud.terminal.vt320;
     23 
     24 import org.apache.harmony.niochar.charset.additional.IBM437;
     25 import org.connectbot.transport.AbsTransport;
     26 import org.connectbot.util.EastAsianWidth;
     27 
     28 import java.io.IOException;
     29 import java.nio.ByteBuffer;
     30 import java.nio.CharBuffer;
     31 import java.nio.charset.Charset;
     32 import java.nio.charset.CharsetDecoder;
     33 import java.nio.charset.CoderResult;
     34 import java.nio.charset.CodingErrorAction;
     35 
     36 /**
     37  */
     38 public class Relay implements Runnable {
     39 
     40   private static final int BUFFER_SIZE = 4096;
     41 
     42   private static boolean useJNI = true;
     43 
     44   private TerminalBridge bridge;
     45 
     46   private Charset currentCharset;
     47   private CharsetDecoder decoder;
     48   private boolean isLegacyEastAsian = false;
     49 
     50   private AbsTransport transport;
     51 
     52   private vt320 buffer;
     53 
     54   private ByteBuffer byteBuffer;
     55   private CharBuffer charBuffer;
     56 
     57   private byte[] byteArray;
     58   private char[] charArray;
     59 
     60   static {
     61     useJNI = EastAsianWidth.useJNI;
     62   }
     63 
     64   public Relay(TerminalBridge bridge, AbsTransport transport, vt320 buffer, String encoding) {
     65     setCharset(encoding);
     66     this.bridge = bridge;
     67     this.transport = transport;
     68     this.buffer = buffer;
     69   }
     70 
     71   public void setCharset(String encoding) {
     72     Log.d("changing charset to " + encoding);
     73     Charset charset;
     74     if (encoding.equals("CP437")) {
     75       charset = new IBM437("IBM437", new String[] { "IBM437", "CP437" });
     76     } else {
     77       charset = Charset.forName(encoding);
     78     }
     79 
     80     if (charset == currentCharset || charset == null) {
     81       return;
     82     }
     83 
     84     CharsetDecoder newCd = charset.newDecoder();
     85     newCd.onUnmappableCharacter(CodingErrorAction.REPLACE);
     86     newCd.onMalformedInput(CodingErrorAction.REPLACE);
     87 
     88     currentCharset = charset;
     89     synchronized (this) {
     90       decoder = newCd;
     91     }
     92   }
     93 
     94   public void run() {
     95     byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
     96     charBuffer = CharBuffer.allocate(BUFFER_SIZE);
     97 
     98     /* for both JNI and non-JNI method */
     99     byte[] wideAttribute = new byte[BUFFER_SIZE];
    100 
    101     /* non-JNI fallback method */
    102     float[] widths = null;
    103 
    104     if (!useJNI) {
    105       widths = new float[BUFFER_SIZE];
    106     }
    107 
    108     byteArray = byteBuffer.array();
    109     charArray = charBuffer.array();
    110 
    111     CoderResult result;
    112 
    113     int bytesRead = 0;
    114     byteBuffer.limit(0);
    115     int bytesToRead;
    116     int offset;
    117     int charWidth;
    118 
    119     try {
    120       while (true) {
    121         charWidth = bridge.charWidth;
    122         bytesToRead = byteBuffer.capacity() - byteBuffer.limit();
    123         offset = byteBuffer.arrayOffset() + byteBuffer.limit();
    124         bytesRead = transport.read(byteArray, offset, bytesToRead);
    125 
    126         if (bytesRead > 0) {
    127           byteBuffer.limit(byteBuffer.limit() + bytesRead);
    128 
    129           synchronized (this) {
    130             result = decoder.decode(byteBuffer, charBuffer, false);
    131           }
    132 
    133           if (result.isUnderflow() && byteBuffer.limit() == byteBuffer.capacity()) {
    134             byteBuffer.compact();
    135             byteBuffer.limit(byteBuffer.position());
    136             byteBuffer.position(0);
    137           }
    138 
    139           offset = charBuffer.position();
    140 
    141           if (!useJNI) {
    142             bridge.getPaint().getTextWidths(charArray, 0, offset, widths);
    143             for (int i = 0; i < offset; i++) {
    144               wideAttribute[i] = (byte) (((int) widths[i] != charWidth) ? 1 : 0);
    145             }
    146           } else {
    147             EastAsianWidth.measure(charArray, 0, charBuffer.position(), wideAttribute,
    148                 isLegacyEastAsian);
    149           }
    150           buffer.putString(charArray, wideAttribute, 0, charBuffer.position());
    151           charBuffer.clear();
    152           bridge.redraw();
    153         }
    154       }
    155     } catch (IOException e) {
    156       Log.e("Problem while handling incoming data in relay thread", e);
    157     }
    158   }
    159 }
    160