Android Open Source - Calma Shell






From Project

Back to project page Calma.

License

The source code is released under:

Apache License

If you think the Android project Calma 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

/* 
 * This file is part of the RootTools Project: http://code.google.com/p/roottools/
 *  //w w  w .j  a  v a 2  s . c  o m
 * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Dominik Schuermann, Adam Shanks
 *  
 * This code is dual-licensed under the terms of the Apache License Version 2.0 and
 * the terms of the General Public License (GPL) Version 2.
 * You may use this code according to either of these licenses as is most appropriate
 * for your project on a case-by-case basis.
 * 
 * The terms of each license can be found in the root directory of this project's repository as well as at:
 * 
 * * http://www.apache.org/licenses/LICENSE-2.0
 * * http://www.gnu.org/licenses/gpl-2.0.txt
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under these Licenses is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See each License for the specific language governing permissions and
 * limitations under that License.
 */
package com.stericson.RootTools.execution;

import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import android.content.Context;
import com.stericson.RootTools.RootTools;
import com.stericson.RootTools.exceptions.RootDeniedException;

public class Shell {

    private final Process proc;
    private final BufferedReader in;
    private final OutputStreamWriter out;
    private final List<Command> commands = new ArrayList<Command>();

    //indicates whether or not to close the shell
    private boolean close = false;

    private static String error = "";
    private static final String token = "F*D^W@#FGF";
    private static Shell rootShell = null;
    private static Shell shell = null;
    private static Shell customShell = null;

    private static int shellTimeout = 25000;
    public static boolean isExecuting = false;
    public static boolean isReading = false;

    private int maxCommands = 1000;
    private int read = 0;
    private int write = 0;
    private int totalExecuted = 0;
    private int totalRead = 0;
    private boolean isCleaning = false;

    //private constructor responsible for opening/constructing the shell
    private Shell(String cmd) throws IOException, TimeoutException, RootDeniedException {

        RootTools.log("Starting shell: " + cmd);

        proc = new ProcessBuilder(cmd).redirectErrorStream(true).start();
        in = new BufferedReader(new InputStreamReader(proc.getInputStream(), "UTF-8"));
        out = new OutputStreamWriter(proc.getOutputStream(), "UTF-8");

        /**
         * Thread responsible for carrying out the requested operations
         */
        Worker worker = new Worker(proc, in, out);
        worker.start();

        try {
            /**
             * The flow of execution will wait for the thread to die or wait until the
             * given timeout has expired.
             *
             * The result of the worker, which is determined by the exit code of the worker,
             * will tell us if the operation was completed successfully or it the operation
             * failed.
             */
            worker.join(shellTimeout);

            /**
             * The operation could not be completed before the timeout occured.
             */
            if (worker.exit == -911) {

                try {
                    proc.destroy();
                } catch (Exception e) {}

                closeQuietly(in);
                closeQuietly(out);

                throw new TimeoutException(error);
            }
            /**
             * Root access denied?
             */
            else if (worker.exit == -42) {

                try {
                    proc.destroy();
                } catch (Exception e) {}

                closeQuietly(in);
                closeQuietly(out);

                throw new RootDeniedException("Root Access Denied");
            }
            /**
             * Normal exit
             */
            else {
                /**
                 * The shell is open.
                 *
                 * Start two threads, one to handle the input and one to handle the output.
                 *
                 * input, and output are runnables that the threads execute.
                 */
                Thread si = new Thread(input, "Shell Input");
                si.setPriority(Thread.NORM_PRIORITY);
                si.start();

                Thread so = new Thread(output, "Shell Output");
                so.setPriority(Thread.NORM_PRIORITY);
                so.start();
            }
        } catch (InterruptedException ex) {
            worker.interrupt();
            Thread.currentThread().interrupt();
            throw new TimeoutException();
        }
    }


    public Command add(Command command) throws IOException {
        if (close)
            throw new IllegalStateException(
                    "Unable to add commands to a closed shell");

        while (isCleaning) {
            //Don't add commands while cleaning
            ;
        }
        commands.add(command);

        notifyThreads();

        return command;
    }

    public void useCWD(Context context) throws IOException, TimeoutException, RootDeniedException {
        add(
                new CommandCapture(
                        -1,
                        false,
                        "cd " + context.getApplicationInfo().dataDir));
    }

    private void cleanCommands() {
        isCleaning = true;
        int toClean = Math.abs(maxCommands - (maxCommands / 4));
        RootTools.log("Cleaning up: " + toClean);
        for (int i = 0; i < toClean; i++) {
            commands.remove(0);
        }

        read = commands.size() - 1;
        write = commands.size() - 1;
        isCleaning = false;
    }

    private void closeQuietly(final Reader input) {
        try {
            if (input != null) {
                input.close();
            }
        } catch (Exception ignore) {}
    }

    private void closeQuietly(final Writer output) {
        try {
            if (output != null) {
                output.close();
            }
        } catch (Exception ignore) {}
    }

    public void close() throws IOException {
        if (this == rootShell)
            rootShell = null;
        else if (this == shell)
            shell = null;
        else if (this == customShell)
            customShell = null;
        synchronized (commands) {
            /**
             * instruct the two threads monitoring input and output
             * of the shell to close.
             */
            this.close = true;
            notifyThreads();
        }
    }

    public static void closeCustomShell() throws IOException {
        if (customShell == null)
            return;
        customShell.close();
    }

    public static void closeRootShell() throws IOException {
        if (rootShell == null)
            return;
        rootShell.close();
    }

    public static void closeShell() throws IOException {
        if (shell == null)
            return;
        shell.close();
    }

    public static void closeAll() throws IOException {
        closeShell();
        closeRootShell();
        closeCustomShell();
    }

    public int getCommandQueuePosition(Command cmd) {
        return commands.indexOf(cmd);
    }

    public String getCommandQueuePositionString(Command cmd) {
        return "Command is in position " + getCommandQueuePosition(cmd) + " currently executing command at position " + write;
    }

    public static Shell getOpenShell() {
        if (customShell != null)
            return customShell;
        else if (rootShell != null)
            return rootShell;
        else
            return shell;
    }

    public static boolean isShellOpen() {
        if (shell == null)
            return false;
        else
            return true;
    }

    public static boolean isCustomShellOpen() {
        if (customShell == null)
            return false;
        else
            return true;
    }

    public static boolean isRootShellOpen() {
        if (rootShell == null)
            return false;
        else
            return true;
    }

    public static boolean isAnyShellOpen() {
        if (shell != null)
            return true;
        else if (rootShell != null)
            return true;
        else if (customShell != null)
            return true;
        else
            return false;
    }

    /**
     * Runnable to write commands to the open shell.
     * <p/>
     * When writing commands we stay in a loop and wait for new
     * commands to added to "commands"
     * <p/>
     * The notification of a new command is handled by the method add in this class
     */
    private Runnable input = new Runnable() {
        public void run() {
            try {
                while (true) {

                    synchronized (commands) {
                        /**
                         * While loop is used in the case that notifyAll is called
                         * and there are still no commands to be written, a rare
                         * case but one that could happen.
                         */
                        while (!close && write >= commands.size()) {
                            isExecuting = false;
                            commands.wait();
                        }
                    }

                    if (write >= maxCommands) {

                        /**
                         * wait for the read to catch up.
                         */
                        while (read != write)
                        {
                            RootTools.log("Waiting for read and write to catch up before cleanup.");
                        }
                        /**
                         * Clean up the commands, stay neat.
                         */
                        cleanCommands();
                    }

                    /**
                     * Write the new command
                     *
                     * We write the command followed by the token to indicate
                     * the end of the command execution
                     */
                    if (write < commands.size()) {
                        isExecuting = true;
                        Command cmd = commands.get(write);
                        cmd.startExecution();
                        RootTools.log("Executing: " + cmd.getCommand());

                        out.write(cmd.getCommand());
                        String line = "\necho " + token + " " + totalExecuted + " $?\n";
                        out.write(line);
                        out.flush();
                        write++;
                        totalExecuted++;
                    } else if (close) {
                        /**
                         * close the thread, the shell is closing.
                         */
                        isExecuting = false;
                        out.write("\nexit 0\n");
                        out.flush();
                        RootTools.log("Closing shell");
                        return;
                    }
                }
            } catch (IOException e) {
                RootTools.log(e.getMessage(), 2, e);
            } catch (InterruptedException e) {
                RootTools.log(e.getMessage(), 2, e);
            } finally {
                write = 0;
                closeQuietly(out);
            }
        }
    };

    protected void notifyThreads() {
        Thread t = new Thread() {
            public void run() {
                synchronized (commands) {
                    commands.notifyAll();
                }
            }
        };

        t.start();
    }

    /**
     * Runnable to monitor the responses from the open shell.
     */
    private Runnable output = new Runnable() {
        public void run() {
            try {
                Command command = null;

                while (!close) {
                    isReading = false;
                    String line = in.readLine();
                    isReading = true;

                    /**
                     * If we recieve EOF then the shell closed
                     */
                    if (line == null)
                        break;

                    if (command == null) {
                        if (read >= commands.size()) {
                            if (close)
                                break;

                            continue;
                        }
                        command = commands.get(read);
                    }

                    /**
                     * trying to determine if all commands have been completed.
                     *
                     * if the token is present then the command has finished execution.
                     */
                    int pos = line.indexOf(token);


                    if (pos == -1) {
                        /**
                         * send the output for the implementer to process
                         */
                        command.output(command.id, line);
                    } else {

                        String fields[] = line.split(" ");

                        if (fields.length >= 2 && fields[1] != null) {
                            int id = 0;

                            try {
                                id = Integer.parseInt(fields[1]);
                            } catch (NumberFormatException e) {
                            }

                            int exitCode = -1;

                            try {
                                exitCode = Integer.parseInt(fields[2]);
                            } catch (NumberFormatException e) {
                            }

                            if (id == totalRead) {
                                command.setExitCode(exitCode);
                                command.commandFinished();
                                command = null;

                                read++;
                                totalRead++;
                                continue;
                            }
                        }
                    }
                }

                RootTools.log("Read all output");
                try {
                    proc.waitFor();
                    proc.destroy();
                } catch (Exception e) {}

                closeQuietly(out);
                closeQuietly(in);

                RootTools.log("Shell destroyed");

                while (read < commands.size()) {
                    if (command == null)
                        command = commands.get(read);

                    command.terminated("Unexpected Termination.");
                    command = null;
                    read++;
                }

                read = 0;

            } catch (IOException e) {
                RootTools.log(e.getMessage(), 2, e);
            }
        }
    };

    public static void runRootCommand(Command command) throws IOException, TimeoutException, RootDeniedException {
        startRootShell().add(command);
    }

    public static void runCommand(Command command) throws IOException, TimeoutException {
        startShell().add(command);
    }

    public static Shell startRootShell() throws IOException, TimeoutException, RootDeniedException {
        return Shell.startRootShell(20000, 3);
    }

    public static Shell startRootShell(int timeout) throws IOException, TimeoutException, RootDeniedException {
        return Shell.startRootShell(timeout, 3);
    }

    public static Shell startRootShell(int timeout, int retry) throws IOException, TimeoutException, RootDeniedException {

        Shell.shellTimeout = timeout;

        if (rootShell == null) {
            RootTools.log("Starting Root Shell!");
            String cmd = "su";
            // keep prompting the user until they accept for x amount of times...
            int retries = 0;
            while (rootShell == null) {
                try {
                    rootShell = new Shell(cmd);
                } catch (IOException e) {
                    if (retries++ >= retry) {
                        RootTools.log("IOException, could not start shell");
                        throw e;
                    }
                }
            }
        } else {
            RootTools.log("Using Existing Root Shell!");
        }

        return rootShell;
    }

    public static Shell startCustomShell(String shellPath) throws IOException, TimeoutException, RootDeniedException {
        return Shell.startCustomShell(shellPath, 20000);
    }

    public static Shell startCustomShell(String shellPath, int timeout) throws IOException, TimeoutException, RootDeniedException {
        Shell.shellTimeout = timeout;

        if (customShell == null) {
            RootTools.log("Starting Custom Shell!");
            customShell = new Shell(shellPath);
        } else
            RootTools.log("Using Existing Custom Shell!");

        return customShell;
    }

    public static Shell startShell() throws IOException, TimeoutException {
        return Shell.startShell(20000);
    }

    public static Shell startShell(int timeout) throws IOException, TimeoutException {
        Shell.shellTimeout = timeout;

        try {
            if (shell == null) {
                RootTools.log("Starting Shell!");
                shell = new Shell("/system/bin/sh");
            } else
                RootTools.log("Using Existing Shell!");
            return shell;
        } catch (RootDeniedException e) {
            //Root Denied should never be thrown.
            throw new IOException();
        }
    }

    protected static class Worker extends Thread {
        public int exit = -911;

        public Process proc;
        public BufferedReader in;
        public OutputStreamWriter out;

        private Worker(Process proc, BufferedReader in, OutputStreamWriter out) {
            this.proc = proc;
            this.in = in;
            this.out = out;
        }

        public void run() {

            /**
             * Trying to open the shell.
             *
             * We echo "Started" and we look for it in the output.
             *
             * If we find the output then the shell is open and we return.
             *
             * If we do not find it then we determine the error and report
             * it by setting the value of the variable exit
             */
            try {
                out.write("echo Started\n");
                out.flush();

                while (true) {
                    String line = in.readLine();
                    if (line == null) {
                        throw new EOFException();
                    }
                    if ("".equals(line))
                        continue;
                    if ("Started".equals(line)) {
                        this.exit = 1;
                        setShellOom();
                        break;
                    }

                    Shell.error = "unkown error occured.";
                }
            } catch (IOException e) {
                exit = -42;
                if (e.getMessage() != null)
                    Shell.error = e.getMessage();
                else
                    Shell.error = "RootAccess denied?.";
            }

        }

        /*
         * setOom for shell processes (sh and su if root shell)
         * and discard outputs
         * 
         */
        private void setShellOom() {
      try {
        Class<?> processClass = proc.getClass();
        Field field = null;
        try {
          field = processClass.getDeclaredField("pid");
        } catch (NoSuchFieldException e) {
          field = processClass.getDeclaredField("id");
        }
        field.setAccessible(true);
        int pid = (Integer) field.get(proc);
        out.write("(echo -17 > /proc/" + pid + "/oom_adj) &> /dev/null\n");
        out.write("(echo -17 > /proc/$$/oom_adj) &> /dev/null\n");
        out.flush();
      } catch (Exception e) {
      }
    }
    }
}




Java Source Code List

com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences.java
com.espian.showcaseview.OnShowcaseEventListener.java
com.espian.showcaseview.ShowcaseViewBuilder.java
com.espian.showcaseview.ShowcaseView.java
com.espian.showcaseview.ShowcaseViews.java
com.espian.showcaseview.actionbar.ActionBarViewWrapper.java
com.espian.showcaseview.actionbar.reflection.ActionBarReflector.java
com.espian.showcaseview.actionbar.reflection.AppCompatReflector.java
com.espian.showcaseview.actionbar.reflection.BaseReflector.java
com.espian.showcaseview.actionbar.reflection.SherlockReflector.java
com.espian.showcaseview.anim.AnimationUtils.java
com.espian.showcaseview.drawing.ClingDrawerImpl.java
com.espian.showcaseview.drawing.ClingDrawer.java
com.espian.showcaseview.drawing.TextDrawerImpl.java
com.espian.showcaseview.drawing.TextDrawer.java
com.espian.showcaseview.targets.ActionItemTarget.java
com.espian.showcaseview.targets.ActionViewTarget.java
com.espian.showcaseview.targets.PointTarget.java
com.espian.showcaseview.targets.Target.java
com.espian.showcaseview.targets.ViewTarget.java
com.espian.showcaseview.utils.Calculator.java
com.espian.showcaseview.utils.PointAnimator.java
com.espian.showcaseview.utils.ShowcaseAreaCalculator.java
com.scto.android.calma.CalmaApp.java
com.scto.android.calma.activities.BaseActivity.java
com.scto.android.calma.activities.CalmaActivity.java
com.scto.android.calma.activities.SettingsActivity.java
com.scto.android.calma.activities.preferences.AboutPreferenceFragment.java
com.scto.android.calma.activities.preferences.DisplayPreferenceFragment.java
com.scto.android.calma.activities.preferences.GeneralPreferenceFragment.java
com.scto.android.calma.activities.preferences.NavigationPreferenceFragment.java
com.scto.android.calma.activities.preferences.SettingsActivity.java
com.scto.android.calma.activities.preferences.TitlePreferenceFragment.java
com.scto.android.calma.adapter.NavDrawerListAdapter.java
com.scto.android.calma.fragments.CommunityFragment.java
com.scto.android.calma.fragments.FindPeopleFragment.java
com.scto.android.calma.fragments.HomeFragment.java
com.scto.android.calma.fragments.PagesFragment.java
com.scto.android.calma.fragments.PhotosFragment.java
com.scto.android.calma.fragments.WhatsHotFragment.java
com.scto.android.calma.model.NavDrawerItem.java
com.scto.android.calma.preferences.AccessMode.java
com.scto.android.calma.preferences.CalmaSettings.java
com.scto.android.calma.preferences.ConfigurationListener.java
com.scto.android.calma.preferences.ObjectIdentifier.java
com.scto.android.calma.preferences.ObjectStringIdentifier.java
com.scto.android.calma.preferences.Preferences.java
com.scto.android.calma.ui.MyPrerference.java
com.scto.android.calma.utils.CalmaUtils.java
com.scto.android.calma.utils.Compress.java
com.scto.android.calma.utils.Decompress.java
com.scto.android.calma.utils.FileUtils.java
com.scto.android.calma.utils.LinuxShell.java
com.scto.android.calma.utils.PreferenceUtils.java
com.scto.android.calma.utils.SharedPreferencesCompat.java
com.scto.android.calma.utils.SortOrder.java
com.scto.android.calma.utils.Utils.java
com.stericson.RootTools.Constants.java
com.stericson.RootTools.RootTools.java
com.stericson.RootToolsTests.NativeJavaClass.java
com.stericson.RootToolsTests.SanityCheckRootTools.java
com.stericson.RootTools.containers.Mount.java
com.stericson.RootTools.containers.Permissions.java
com.stericson.RootTools.containers.RootClass.java
com.stericson.RootTools.containers.Symlink.java
com.stericson.RootTools.exceptions.RootDeniedException.java
com.stericson.RootTools.execution.CommandCapture.java
com.stericson.RootTools.execution.Command.java
com.stericson.RootTools.execution.JavaCommandCapture.java
com.stericson.RootTools.execution.Shell.java
com.stericson.RootTools.internal.Installer.java
com.stericson.RootTools.internal.InternalVariables.java
com.stericson.RootTools.internal.Remounter.java
com.stericson.RootTools.internal.RootToolsInternalMethods.java
com.stericson.RootTools.internal.Runner.java