blob: b9554086b240b206756cbf5ea785cfaebc1cd1e0 [file] [log] [blame] [raw]
package org.libdll.so.am;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.util.TypedValue;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
class DialogResultHandler extends Handler {
@Override
public void handleMessage(Message msg) {
throw new RuntimeException();
//switch(msg.what
//Looper.getMainLooper().quit();
}
}
public class RootShell {
private RootShell() throws IOException, InterruptedException {
process = Runtime.getRuntime().exec("su");
shell_stdin = new DataOutputStream(process.getOutputStream());
shell_stdout = new DataInputStream(process.getInputStream());
shell_stderr = new DataInputStream(process.getErrorStream());
for(int i=0; i<100; i++) try {
Thread.sleep(10);
Log.i("shell_stdout available", String.valueOf(shell_stdout.available()));
if(shell_stdout.available() > 0) break;
} catch(InterruptedException e) {
e.printStackTrace();
}
byte[] buffer = new byte[32];
if(shell_stdout.available() == 0) try {
int status = process.exitValue();
//String msg = String.format("shell terminated with status %d", status);
StringBuilder msg = new StringBuilder();
if(shell_stderr.available() > 0) {
if(shell_stderr.read(buffer) > 0) {
//msg = new String(buffer);
msg.append(new String(buffer));
}
}
msg.append(String.format(context.getString(R.string.shell_terminated), status));
throw new IOException(msg.toString());
} catch(IllegalThreadStateException e) {
// Still running
//Thread.sleep(100);
return;
}
if(shell_stdout.read(buffer) < 1) return;
String result = new String(buffer);
//Log.i("result", result.substring(0, 9));
//int count = 0;
if(result.substring(0, 9).equals("Password:")) {
Log.w("RootShell", "su is require a password");
/*
final android.os.Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//throw new RuntimeException();
Looper.getMainLooper().quit();
}
};
*/
final DialogResultHandler handler = new DialogResultHandler();
//final StringBuffer pw = new StringBuffer();
//final Boolean ok = new Boolean();
//final Boolean ok = new Boolean(false);
//final Boolean ok = Boolean.valueOf(false);
//View text_entry = LayoutInflater.from(context).inflate()
TextView text_view = new TextView(context);
text_view.setText(R.string.password_for_root);
text_view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
final EditText password_entry = new EditText(context);
//password_entry.setTransformationMethod(PasswordTransformationMethod.getInstance());
password_entry.setSingleLine(true);
password_entry.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(text_view);
layout.addView(password_entry);
//if(context == null) throw new RuntimeException("context ???");
AlertDialog.Builder dialog_builder = new AlertDialog.Builder(context);
dialog_builder.setTitle(R.string.title_run_as_root);
dialog_builder.setView(layout);
DialogInterface.OnClickListener on_ok_click_listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//pw.append(password_entry.getText().toString());
password = password_entry.getText().toString();
handler.sendEmptyMessage(1);
//handler.sendMessage(new Message());
/*
Bundle b = new Bundle();
b.putString("password", password_entry.getText().toString());
Message message = new Message();
message.setData(b);
handler.sendMessage(message);
*/
}
};
DialogInterface.OnClickListener on_cancel_click_listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
password = null;
handler.sendEmptyMessage(0);
//handler.sendMessage(new Message());
//handler.sendMessage(handler.obtainMessage());
}
};
DialogInterface.OnCancelListener on_cancel_listener = new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
password = null;
handler.sendEmptyMessage(0);
}
};
dialog_builder.setPositiveButton(R.string.action_ok, on_ok_click_listener);
dialog_builder.setNegativeButton(R.string.action_cancel, on_cancel_click_listener);
dialog_builder.setOnCancelListener(on_cancel_listener);
dialog_builder.create().show();
//Intent intent = new Intent(null, PasswordActivity.class);
//intent.setClass();
//context.startActivity();
try {
Looper.loop();
} catch(RuntimeException e) {
e.printStackTrace();
}
//Log.i("RootShell", password);
//Toast.makeText(context, "No password?", Toast.LENGTH_SHORT).show();
if(password == null) {
//Log.i("RootShell", "give up on password");
terminate();
instance = null;
//return;
//throw new IOException("Authentication failure");
throw new InterruptedException();
}
shell_stdin.writeBytes(password);
password = null;
shell_stdin.write('\n');
System.gc();
for(int i = 0; i < 100; i++) try {
Thread.sleep(10);
Log.i("shell_stdout available", String.valueOf(shell_stderr.available()));
if(shell_stderr.available() > 0) break;
} catch(InterruptedException e) {
e.printStackTrace();
}
if(shell_stderr.available() == 0) return;
int s = shell_stderr.read(buffer);
if(s < 1) return;
Log.i("RootShell", String.format("s = %d", s));
result = new String(buffer, 0, s);
Log.i("RootShell", result);
//if(s != 23) return;
//if(++count > 3) throw new IOException("Authentication failure");
if(s == 23 && result.equals("su: incorrect password\n")) {
throw new IOException(context.getString(R.string.authentication_failure));
}
}
//} while(result.equals("su: incorrect password\n"));
/*
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
ReadableByteChannel shell_stderr_channel = Channels.newChannel(shell_stdout);
Log.i("shell_stderr_channel", shell_stderr_channel.toString());
shell_stderr_channel.read(buffer);
Log.i("buffer", buffer.toString());
*/
/*
while(true) Log.i("shell_stderr available", String.valueOf(shell_stderr.available()));
int c = shell_stdout.read();
Log.i("First char", String.valueOf((char)c));
*/
}
private static RootShell instance;
private Process process;
private DataOutputStream shell_stdin;
private DataInputStream shell_stdout;
private DataInputStream shell_stderr;
static private Context context;
private String password;
private boolean is_terminated() {
try {
process.exitValue();
return true;
} catch(IllegalThreadStateException e) {
//e.printStackTrace();
return false;
}
}
public void terminate() {
process.destroy();
}
public void quit() {
if(is_terminated()) return;
try {
//shell_stdin.writeBytes("exit\n");
write_line("exit");
} catch(IOException e) {
e.printStackTrace();
}
terminate();
}
public static RootShell get_instance(Context context) throws IOException {
RootShell.context = context;
try {
if(instance == null || instance.is_terminated()) instance = new RootShell();
} catch(InterruptedException e) {
instance = null;
}
return instance;
}
public void write_line(String line) throws IOException {
shell_stdin.writeBytes(line);
shell_stdin.write('\n');
}
}