Android Open Source - ZShaolin Term Session






From Project

Back to project page ZShaolin.

License

The source code is released under:

GNU General Public License

If you think the Android project ZShaolin listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2007 The Android Open Source Project
 */*from   ww  w  .ja  va2  s .  co  m*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.spartacusrex.spartacuside.session;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.spartacusrex.spartacuside.Exec;
import com.spartacusrex.spartacuside.TermDebug;
import com.spartacusrex.spartacuside.model.UpdateCallback;
import com.spartacusrex.spartacuside.util.ByteQueue;
import com.spartacusrex.spartacuside.util.TermSettings;

/**
 * A terminal session, consisting of a TerminalEmulator, a TranscriptScreen,
 * the PID of the process attached to the session, and the I/O streams used to
 * talk to the process.
 */
public class TermSession {
    private TermSettings mSettings;
    private UpdateCallback mNotify;

    private int mProcId;
    private FileDescriptor mTermFd;
    private FileOutputStream mTermOut;
    private FileInputStream mTermIn;

    private TranscriptScreen mTranscriptScreen;
    private TerminalEmulator mEmulator;

    private Thread mPollingThread;
    private ByteQueue mByteQueue;
    private byte[] mReceiveBuffer;

    private static final int DEFAULT_COLUMNS = 80;
    private static final int DEFAULT_ROWS = 24;
    private static final String DEFAULT_SHELL = "/system/bin/zsh -";
    private static final String DEFAULT_INITIAL_COMMAND = "export HOME=/data/data/org.dyne.zshaolin/files;cd $HOME;export PATH=$HOME/system/bin:$HOME/system/busybox/bin:$PATH;zsh";

    // Number of rows in the transcript
    private static final int TRANSCRIPT_ROWS = 10000;

    private static final int NEW_INPUT = 1;

    private String mHomeFilesDir;

    private boolean mIsRunning = false;
    private Handler mMsgHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (!mIsRunning) {
                return;
            }
            if (msg.what == NEW_INPUT) {
                readFromProcess();
            }
        }
    };

    public TermSession(String zHomeFilesDir,TermSettings settings, UpdateCallback notify, String initialCommand) {
        mSettings = settings;
        mNotify = notify;
        mHomeFilesDir = zHomeFilesDir;
        
        int[] processId = new int[1];

        createSubprocess(processId);
        mProcId = processId[0];
        mTermOut = new FileOutputStream(mTermFd);
        mTermIn = new FileInputStream(mTermFd);

        mTranscriptScreen = new TranscriptScreen(DEFAULT_COLUMNS, TRANSCRIPT_ROWS, DEFAULT_ROWS, 0, 7);
        mEmulator = new TerminalEmulator(mTranscriptScreen, DEFAULT_COLUMNS, DEFAULT_ROWS, mTermOut);

        mIsRunning = true;

        Thread watcher = new Thread() {
             @Override
             public void run() {
                Log.i(TermDebug.LOG_TAG, "waiting for: " + mProcId);
                int result = Exec.waitFor(mProcId);
                Log.i(TermDebug.LOG_TAG, "Subprocess exited: " + result);
                mMsgHandler.sendEmptyMessage(result);
             }
        };
        watcher.setName("Process watcher");
        watcher.start();

        mReceiveBuffer = new byte[4 * 1024];
        mByteQueue = new ByteQueue(4 * 1024);

        mPollingThread = new Thread() {
            private byte[] mBuffer = new byte[4096];

            @Override
            public void run() {
                try {
                    while(true) {
                        int read = mTermIn.read(mBuffer);
                        if (read == -1) {
                            // EOF -- process exited
                            return;
                        }
                        mByteQueue.write(mBuffer, 0, read);
                        mMsgHandler.sendMessage(
                                mMsgHandler.obtainMessage(NEW_INPUT));
                    }
                } catch (IOException e) {
                } catch (InterruptedException e) {
                }
            }
        };
        mPollingThread.setName("Input reader");
        mPollingThread.start();

        //No INITIAL Command for now..
        //sendInitialCommand(initialCommand);
    }

    private void sendInitialCommand(String initialCommand) {
        if (initialCommand == null || initialCommand.equals("")) {
            initialCommand = DEFAULT_INITIAL_COMMAND;
        }
        if (initialCommand.length() > 0) {
            write(initialCommand + '\r');
        }
    }

    public void write(String data) {
        try {
            mTermOut.write(data.getBytes());
            mTermOut.flush();
        } catch (IOException e) {
            // Ignore exception
            // We don't really care if the receiver isn't listening.
            // We just make a best effort to answer the query.
        }
    }

    private void createSubprocess(int[] processId) {
        String shell = mSettings.getShell();
        if (shell == null || shell.equals("")) {
            shell = DEFAULT_SHELL;
        }

        //Start the console's shell here
        File home  = new File(mHomeFilesDir);
        File shellf = new File(home,"system/bin/zsh");
        File initf  = new File(home,"system/etc/zlogin");
        if(shellf.exists()){
            //Start Bash
            shell = shellf.getPath() + " " + initf.getPath();
        }

        //Now start it..
        ArrayList<String> args = parse(shell);
        String arg0 = args.get(0);
        String arg1 = null;
        String arg2 = null;
        if (args.size() >= 2) {
            arg1 = args.get(1);
        }
        if (args.size() >= 3) {
            arg2 = args.get(2);
        }
        
        mTermFd = Exec.createSubprocess(arg0, arg1, arg2, processId);
    }

    private ArrayList<String> parse(String cmd) {
        final int PLAIN = 0;
        final int WHITESPACE = 1;
        final int INQUOTE = 2;
        int state = WHITESPACE;
        ArrayList<String> result =  new ArrayList<String>();
        int cmdLen = cmd.length();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < cmdLen; i++) {
            char c = cmd.charAt(i);
            if (state == PLAIN) {
                if (Character.isWhitespace(c)) {
                    result.add(builder.toString());
                    builder.delete(0,builder.length());
                    state = WHITESPACE;
                } else if (c == '"') {
                    state = INQUOTE;
                } else {
                    builder.append(c);
                }
            } else if (state == WHITESPACE) {
                if (Character.isWhitespace(c)) {
                    // do nothing
                } else if (c == '"') {
                    state = INQUOTE;
                } else {
                    state = PLAIN;
                    builder.append(c);
                }
            } else if (state == INQUOTE) {
                if (c == '\\') {
                    if (i + 1 < cmdLen) {
                        i += 1;
                        builder.append(cmd.charAt(i));
                    }
                } else if (c == '"') {
                    state = PLAIN;
                } else {
                    builder.append(c);
                }
            }
        }
        if (builder.length() > 0) {
            result.add(builder.toString());
        }
        return result;
    }

    public FileOutputStream getTermOut() {
        return mTermOut;
    }

    public TranscriptScreen getTranscriptScreen() {
        return mTranscriptScreen;
    }

    public TerminalEmulator getEmulator() {
        return mEmulator;
    }

    public void setUpdateCallback(UpdateCallback notify) {
        mNotify = notify;
    }

    public void updateSize(int columns, int rows) {
        // Inform the attached pty of our new size:
        Exec.setPtyWindowSize(mTermFd, rows, columns, 0, 0);
        mEmulator.updateSize(columns, rows);
    }

    public String getTranscriptText() {
        return mTranscriptScreen.getTranscriptText();
    }

    /**
     * Look for new input from the ptty, send it to the terminal emulator.
     */
    private void readFromProcess() {
        int bytesAvailable = mByteQueue.getBytesAvailable();
        int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
        try {
            int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
            mEmulator.append(mReceiveBuffer, 0, bytesRead);
        } catch (InterruptedException e) {
        }

        if (mNotify != null) {
            mNotify.onUpdate();
        }
    }

    public void finish() {
        Exec.hangupProcessGroup(mProcId);
        Exec.close(mTermFd);
        mIsRunning = false;
        mTranscriptScreen.finish();
    }
}




Java Source Code List

com.spartacusrex.spartacuside.EmulatorView.java
com.spartacusrex.spartacuside.Exec.java
com.spartacusrex.spartacuside.TermDebug.java
com.spartacusrex.spartacuside.TermPreferences.java
com.spartacusrex.spartacuside.TermService.java
com.spartacusrex.spartacuside.TermViewFlipper.java
com.spartacusrex.spartacuside.Term.java
com.spartacusrex.spartacuside.WindowList.java
com.spartacusrex.spartacuside.keyboard.CandidateView.java
com.spartacusrex.spartacuside.keyboard.KeyboardPrefs.java
com.spartacusrex.spartacuside.keyboard.KeyboardSwitcher.java
com.spartacusrex.spartacuside.keyboard.LatinKeyboardView.java
com.spartacusrex.spartacuside.keyboard.LatinKeyboard.java
com.spartacusrex.spartacuside.keyboard.MyKeyboardView.java
com.spartacusrex.spartacuside.keyboard.TerminalKeyboard.java
com.spartacusrex.spartacuside.model.Screen.java
com.spartacusrex.spartacuside.model.TextRenderer.java
com.spartacusrex.spartacuside.model.UpdateCallback.java
com.spartacusrex.spartacuside.session.TermSession.java
com.spartacusrex.spartacuside.session.TerminalEmulator.java
com.spartacusrex.spartacuside.session.TranscriptScreen.java
com.spartacusrex.spartacuside.util.ByteQueue.java
com.spartacusrex.spartacuside.util.ServiceForegroundCompat.java
com.spartacusrex.spartacuside.util.TermSettings.java
com.spartacusrex.spartacuside.util.dialogpref.java
com.spartacusrex.spartacuside.util.hardkeymappings.java
com.spartacusrex.spartacuside.util.keydata.java
com.spartacusrex.spartacuside.web.sockethandler.java
com.spartacusrex.spartacuside.web.webserver.java
org.dyne.zshaolin.Start.java
org.dyne.zshaolin.startup.TerminalIDEPrefs.java
org.dyne.zshaolin.startup.setup.assetextract.java
org.dyne.zshaolin.startup.setup.filemanager.java
org.dyne.zshaolin.startup.installer.java
org.dyne.zshaolin.startup.introscreen.java