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 
Relay(TerminalBridge bridge, AbsTransport transport, vt320 buffer, String encoding)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 
setCharset(String encoding)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 
run()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