package com.ghostsq.commander.root;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.EditText;
import com.ghostsq.commander.Commander;
import com.ghostsq.commander.R;
import com.ghostsq.commander.TextViewer;
import com.ghostsq.commander.adapters.CA;
import com.ghostsq.commander.adapters.CommanderAdapter;
import com.ghostsq.commander.adapters.CommanderAdapterBase;
import com.ghostsq.commander.adapters.Engines;
import com.ghostsq.commander.adapters.FSAdapter;
import com.ghostsq.commander.utils.LsItem;
import com.ghostsq.commander.utils.Permissions;
import com.ghostsq.commander.utils.Utils;
import com.ghostsq.commander.utils.LsItem.LsItemPropComparator;
import com.ghostsq.commander.root.MountsListEngine;
import com.ghostsq.commander.root.MountsListEngine.MountItem;
public class RootAdapter extends CommanderAdapterBase {
// Java compiler creates a thunk function to access to the private owner class member from a subclass
// to avoid that all the member accessible from the subclasses are public
private final static String TAG = "RootAdapter";
public static final String DEFAULT_LOC = "root:";
private final static int CHMOD_CMD = 36793, CMD_CMD = 39716;
private Uri uri = null;
private LsItem[] items = null;
private int attempts = 0;
private MountsListEngine systemMountReader;
private String systemMountMode;
private final static String SYSTEM_PATH = "/system";
private ContentEngine contentEngine;
private File tmp_f, dst_f;
public RootAdapter( Context ctx_ ) {
super( ctx_, SHOW_ATTR | NARROW_MODE );
}
@Override
public int setMode( int mask, int val ) {
if( ( mask & ( MODE_WIDTH ) ) == 0 )
return super.setMode( mask, val );
return mode;
}
@Override
public int getType() {
return CA.ROOT;
}
class ListEngine extends ExecEngine {
private LsItem[] items_tmp;
private String pass_back_on_done;
private Uri src;
private ArrayList<LsItem> array;
private final static String EOL = "_EOL_";
ListEngine( Context ctx, Handler h, Uri src_, String pass_back_on_done_ ) {
super( ctx );
setHandler( h );
src = src_;
pass_back_on_done = pass_back_on_done_;
}
public LsItem[] getItems() {
return items_tmp;
}
public Uri getUri() {
return src;
}
@Override
public void run() {
String msg = null;
if( !getList( true ) ) {
Log.w( TAG, "su failed. let's try just sh" );
errMsg = null;
msg = commander.getContext().getString( R.string.no_root );
if( !getList( false ) )
error( commander.getContext().getString( R.string.cant_cd, src.getPath() ) );
}
doneReading( msg, pass_back_on_done );
}
private boolean getList( boolean su ) {
if( !su ) sh = "sh";
String path = src.getPath();
if( path == null ) {
path = SLS;
src = src.buildUpon().encodedPath( path ).build();
}
parentLink = path == null || path.length() == 0 || path.equals( SLS ) ? SLS : "..";
array = new ArrayList<LsItem>();
// the option -s is not supported on some releases (1.6)
String to_execute = "ls " + ( ( mode & MODE_HIDDEN ) != HIDE_MODE ? "-a ":"" ) + "-l " + ExecEngine.prepFileName( path ) + " ; echo " + EOL;
if( !execute( to_execute, false, su ? 5000 : 500 ) ) // 'busybox -l' always outs UID/GID as numbers, not names!
return false;
if( !isStopReq() ) {
int sz = array != null ? array.size() : 0;
items_tmp = new LsItem[sz];
if( sz > 0 ) {
array.toArray( items_tmp );
LsItem.LsItemPropComparator comp =
items_tmp[0].new LsItemPropComparator( mode & MODE_SORTING, (mode & MODE_CASE) != 0, ascending );
Arrays.sort( items_tmp, comp );
}
return true;
}
return false;
}
@Override
protected void procInput( BufferedReader br ) throws IOException, Exception {
while( br.ready() ) {
if( isStopReq() ) break;
String ln = br.readLine();
if( ln == null || ln.startsWith( EOL ) ) break;
LsItem item = new LsItem( ln );
if( item.isValid() ) {
if( !"..".equals( item.getName() ) && !".".equals( item.getName() ) )
array.add( item ); // a problem - if the item is a symlink - how to know it's a dir or a file???
}
}
}
}
@Override
protected void onReadComplete() {
try {
attempts = 0;
if( reader instanceof ListEngine ) {
ListEngine list_engine = (ListEngine)reader;
items = list_engine.getItems();
uri = list_engine.getUri();
numItems = items != null ? items.length + 1 : 1;
notifyDataSetChanged();
String path = uri.getPath();
if( path != null && path.startsWith( SYSTEM_PATH ) ) {
// know the /system mount state
systemMountReader = new MountsListEngine( commander.getContext(), readerHandler, false );
systemMountReader.start();
}
} else
if( systemMountReader != null ) {
MountItem[] mounts = systemMountReader.getItems();
if( mounts != null ) {
boolean remount = systemMountReader.toRemount();
systemMountReader = null;
for( MountItem m : mounts ) {
String mp = m.getMountPoint();
if( SYSTEM_PATH.equals( mp ) ) {
if( remount ) {
RemountEngine re = new RemountEngine( commander.getContext(), simpleHandler, m );
re.start();
}
else
systemMountMode = m.getMode();
break;
}
}
}
}
} catch( Exception e ) {
e.printStackTrace();
}
}
@Override
public String toString() {
if( uri != null ) {
if( systemMountMode != null ) {
String path = uri.getPath();
try {
return uri.buildUpon().fragment( path != null && path.startsWith( SYSTEM_PATH ) ? systemMountMode : null ).build().toString();
} catch( Exception e ) {}
}
return uri.buildUpon().fragment( " " ).build().toString();
}
return "";
}
/*
* CommanderAdapter implementation
*/
@Override
public Uri getUri() {
return uri;
}
@Override
public void setUri( Uri uri_ ) {
uri = uri_;
}
@Override
public void setIdentities( String name, String pass ) {
// TODO: provide a password for su ?
}
@Override
public boolean readSource( Uri tmp_uri, String pass_back_on_done ) {
try {
if( tmp_uri == null )
tmp_uri = uri;
if( tmp_uri == null )
return false;
uri = tmp_uri; // since the Superuser application can break the execution,
// it's important to keep the uri
if( reader != null ) {
if( attempts++ < 2 ) {
commander.showInfo( "Busy..." );
return false;
}
if( reader.reqStop() ) { // that's not good.
Thread.sleep( 500 ); // will it end itself?
if( reader.isAlive() ) {
Log.e( TAG, "Busy!" );
return false;
}
}
}
notify( Commander.OPERATION_STARTED );
reader = new ListEngine( commander.getContext(), readerHandler, tmp_uri, pass_back_on_done );
reader.start();
return true;
}
catch( Exception e ) {
commander.showError( "Exception: " + e );
e.printStackTrace();
}
notify( "Fail", Commander.OPERATION_FAILED );
return false;
}
@Override
public void reqItemsSize( SparseBooleanArray cis ) {
if( uri == null ) return;
try {
LsItem[] s_items = bitsToItems( cis );
if( s_items != null && s_items.length > 0 ) {
String path = Utils.mbAddSl( uri.getPath() );
StringBuilder sb = new StringBuilder( 128 );
sb.append( "stat " );
for( int i = 0; i < s_items.length; i++ )
sb.append( " " ).append( ExecEngine.prepFileName( path + s_items[i].getName() ) );
sb.append( " ; df" );
ExecEngine ee = new ExecEngine( ctx, null, sb.toString(), true, 500 );
ee.setHandler( new Handler() {
@Override
public void handleMessage( Message msg ) {
try {
Intent in = new Intent( ctx, TextViewer.class );
in.setData( Uri.parse( TextViewer.STRURI ) );
if( msg.obj instanceof Bundle )
in.putExtra( TextViewer.STRKEY, ((Bundle)msg.obj).getString( Commander.MESSAGE_STRING ) );
commander.issue( in, 0 );
} catch( Exception e ) {
Log.e( TAG, null, e );
}
}
} );
ee.start();
}
} catch( Exception e ) {
e.printStackTrace();
}
}
@Override
public boolean copyItems( SparseBooleanArray cis, CommanderAdapter to, boolean move ) {
try {
LsItem[] subItems = bitsToItems( cis );
if( subItems != null ) {
Engines.IReciever recipient = null;
String to_path = null;
if( to instanceof FSAdapter || to instanceof RootAdapter ) {
Uri to_uri = to.getUri();
if( to_uri != null )
to_path = to_uri.getPath();
to = null;
} else {
to_path = createTempDir();
recipient = to.getReceiver();
}
if( to_path != null ) {
notify( Commander.OPERATION_STARTED );
commander.startEngine( new CopyFromEngine( commander.getContext(), subItems, to_path, move, recipient ) );
return true;
}
}
notify( "Failed to proceed.", Commander.OPERATION_FAILED );
}
catch( Exception e ) {
commander.showError( "Exception: " + e );
}
return false;
}
class CopyFromEngine extends ExecEngine {
private int counter = 0;
private LsItem[] list;
private String dest_folder;
private boolean move;
private String src_base_path;
private String uid;
CopyFromEngine( Context ctx, LsItem[] list_, String dest, boolean move_, Engines.IReciever recipient_ ) {
super( ctx );
recipient = recipient_;
list = list_;
dest_folder = dest;
move = move_;
src_base_path = uri.getPath();
if( src_base_path == null || src_base_path.length() == 0 )
src_base_path = SLS;
else
if( src_base_path.charAt( src_base_path.length()-1 ) != SLC )
src_base_path += SLS;
if( recipient != null )
try {
PackageManager pm = ctx.getPackageManager();
if( pm != null ) {
ApplicationInfo ai;
ai = pm.getApplicationInfo( ctx.getPackageName(), 0 );
if( ai != null )
uid = "" + ai.uid;
}
} catch( NameNotFoundException e ) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
boolean ok = execute();
if( counter > 0 && recipient != null ) {
File temp_dir = new File( dest_folder );
File[] temp_content = temp_dir.listFiles();
String[] paths = new String[temp_content.length];
for( int i = 0; i < temp_content.length; i++ )
paths[i] = temp_content[i].getAbsolutePath();
sendReceiveReq( paths );
return;
}
if( !ok )
counter = 0;
}
catch( Exception e ) {
error( "Exception: " + e );
}
sendResult( counter > 0 ? Utils.getOpReport( commander.getContext(), counter, move ? R.string.moved : R.string.copied ) : "" );
}
@Override
protected boolean cmdDialog( OutputStreamWriter os, BufferedReader is, BufferedReader es ) {
try {
int num = list.length;
double conv = 100./(double)num;
String esc_dest = ExecEngine.prepFileName( dest_folder );
for( int i = 0; i < num; i++ ) {
LsItem f = list[i];
if( f == null ) continue;
String file_name = f.getName();
String full_name = src_base_path + file_name;
String cmd = move ? " mv -f" : ( f.isDirectory() ? " cp -a" : " cp" );
String to_exec = cmd + " " + ExecEngine.prepFileName( full_name )
+ " " + esc_dest;
outCmd( true, to_exec, os );
if( procError( es ) ) return false;
try {
File dst_file = new File( dest_folder, f.getName() );
String dst_path = ExecEngine.prepFileName( dst_file.getAbsolutePath() );
Permissions perm = uid != null ? new Permissions( uid, uid, "-rw-rw----" ) :
new Permissions( f.getAttr() );
String chown_cmd = "chown " + perm.generateChownString().append(" ").append( dst_path ).toString();
outCmd( uid != null, chown_cmd, os );
String chmod_cmd = "chmod " + perm.generateChmodString().append(" ").append( dst_path ).toString();
outCmd( true, chmod_cmd, os );
procError( es );
} catch( Exception e ) {
Log.w( TAG, "chmod/chown failed", e );
}
sendProgress( "'" + file_name + "'", (int)(i * conv) );
counter++;
}
return true;
} catch( Exception e ) {
error( e.getMessage() );
}
return false;
}
}
@Override
public boolean createFile( String fileURI ) {
notify( "Operation is not supported.", Commander.OPERATION_FAILED );
return false;
}
@Override
public void createFolder( String new_name ) {
if( uri == null ) return;
MkDirEngine mde = new MkDirEngine( commander.getContext(), simpleHandler, new_name );
mde.start();
}
class MkDirEngine extends ExecEngine {
String new_name, full_name;
MkDirEngine( Context ctx, Handler h, String new_name_ ) {
super( ctx );
setHandler( h );
new_name = new_name_;
full_name = uri.getPath() + SLS + new_name;
}
@Override
public void run() {
try {
String cmd = "mkdir " + ExecEngine.prepFileName( full_name );
execute( cmd, true, 100 );
} catch( Exception e ) {
error( "Exception: " + e );
}
if( noErrors() )
sendRefrReq( new_name );
else
sendResult( ctx.getString( R.string.cant_md, full_name ) );
}
}
@Override
public boolean deleteItems( SparseBooleanArray cis ) {
try {
LsItem[] subItems = bitsToItems( cis );
if( subItems != null ) {
notify( Commander.OPERATION_STARTED );
commander.startEngine( new DelEngine( commander.getContext(), subItems ) );
return true;
}
}
catch( Exception e ) {
commander.showError( "Exception: " + e );
}
return false;
}
class DelEngine extends ExecEngine {
private String src_base_path;
private LsItem[] mList;
private int counter = 0;
DelEngine( Context ctx, LsItem[] list ) {
super( ctx );
mList = list;
src_base_path = uri.getPath();
if( src_base_path == null || src_base_path.length() == 0 )
src_base_path = SLS;
else
if( src_base_path.charAt( src_base_path.length()-1 ) != SLC )
src_base_path += SLS;
}
@Override
public void run() {
if( !execute() )
counter = 0;
sendResult( counter > 0 ? Utils.getOpReport( commander.getContext(), counter, R.string.deleted ) : "" );
}
@Override
protected boolean cmdDialog( OutputStreamWriter os, BufferedReader is, BufferedReader es ) {
try {
int num = mList.length;
double conv = 100./num;
for( int i = 0; i < num; i++ ) {
LsItem f = mList[i];
String full_name = src_base_path + f.getName();
sendProgress( "Deleting " + full_name, (int)(counter * conv) );
String to_exec = "rm " + ( f.isDirectory() ? "-r " : "" ) + prepFileName( full_name );
outCmd( false, to_exec, os );
if( procError( es ) ) return false;
counter++;
}
return true;
} catch( Exception e ) {
error( e.getMessage() );
}
return false;
}
}
@Override
public Uri getItemUri( int position ) {
if( uri == null ) return null;
return uri.buildUpon().appendEncodedPath( getItemName( position, false ) ).build();
}
@Override
public String getItemName( int position, boolean full ) {
if( items != null && position > 0 && position <= items.length ) {
if( full ) {
Uri item_uri = getItemUri( position );
if( item_uri != null )
return item_uri.toString();
}
else return items[position-1].getName();
}
return null;
}
@Override
public void openItem( int position ) {
if( position == 0 ) { // ..
if( uri != null && parentLink != SLS ) {
String path = uri.getPath();
int len_ = path.length()-1;
if( len_ > 0 ) {
if( path.charAt( len_ ) == SLC )
path = path.substring( 0, len_ );
path = path.substring( 0, path.lastIndexOf( SLC ) );
if( path.length() == 0 )
path = SLS;
commander.Navigate( uri.buildUpon().path( path ).build(), null, uri.getLastPathSegment() );
}
}
return;
}
if( items == null || position < 0 || position > items.length )
return;
LsItem item = items[position - 1];
if( item.isDirectory() ) {
String cur = uri.getPath();
if( cur == null || cur.length() == 0 )
cur = SLS;
else
if( cur.charAt( cur.length()-1 ) != SLC )
cur += SLS;
commander.Navigate( uri.buildUpon().appendEncodedPath( item.getName() ).build(), null, null );
}
else
new CmdDialog( ctx, item, this );
}
@Override
public boolean receiveItems( String[] full_names, int move_mode ) {
try {
if( full_names == null || full_names.length == 0 ) {
notify( "Nothing to copy", Commander.OPERATION_FAILED );
return false;
}
notify( Commander.OPERATION_STARTED );
commander.startEngine( new CopyToEngine( commander.getContext(), full_names,
( move_mode & MODE_MOVE ) != 0, uri.getPath(), false ) );
return true;
} catch( Exception e ) {
notify( "Exception: " + e, Commander.OPERATION_FAILED );
}
return false;
}
class CopyToEngine extends ExecEngine {
private String[] src_full_names;
private String dest;
private boolean move = false;
private boolean quiet;
private boolean permByDest = false;
private int counter = 0;
CopyToEngine( Context ctx, String[] list, boolean move_, String dest_, boolean quiet_ ) {
super( ctx );
src_full_names = list;
dest = dest_;
move = move_;
quiet = quiet_;
}
public final void setPermByDest() {
permByDest = true;
}
@Override
public void run() {
if( !execute() )
counter = 0;
if( quiet ) {
if( noErrors() ) {
File df = new File( dest );
sendRefrReq( df.getName() );
}
else
sendResult( null );
}
else
sendResult( counter > 0 ? Utils.getOpReport( commander.getContext(), counter, move ? R.string.moved : R.string.copied ) : "" );
}
@Override
protected boolean cmdDialog( OutputStreamWriter os, BufferedReader is, BufferedReader es ) {
try {
String cmd = move ? " mv" : " cp -a";
String esc_dest = prepFileName( dest );
int num = src_full_names.length;
double conv = 100./(double)num;
for( int i = 0; i < num; i++ ) {
String full_name = src_full_names[i];
if( full_name == null ) continue;
File src_file = new File( full_name );
File dst_file = new File( dest, src_file.getName() );
String esc_dst_fn = prepFileName( dst_file.getAbsolutePath() );
String esc_src_fn = prepFileName( full_name );
LsItem probe_item = null;
if( permByDest || !move ) {
String probe_fn = permByDest ? esc_dst_fn : esc_src_fn;
String ls_cmd = "ls -l " + probe_fn;
outCmd( false, ls_cmd, os );
String str = null;
while( is.ready() ) {
str = is.readLine();
if( str != null && str.trim().length() > 0 )
Log.v( TAG, ">>>" + str );
}
if( str != null )
probe_item = new LsItem( str );
}
String to_exec = cmd + " " + esc_src_fn + " " + esc_dest;
outCmd( true, to_exec, os );
if( procError( es ) ) return false;
if( probe_item != null ) {
Permissions src_p = new Permissions( probe_item.getAttr() );
String chown_cmd = "chown " + src_p.generateChownString().append(" ").append( esc_dst_fn ).toString();
outCmd( false, chown_cmd, os );
String chmod_cmd = "chmod " + src_p.generateChmodString().append(" ").append( esc_dst_fn ).toString();
outCmd( true, chmod_cmd, os );
}
if( !quiet ) sendProgress( full_name + " ", (int)(i * conv) );
counter++;
}
return true;
} catch( Exception e ) {
error( e.getMessage() );
}
return false;
}
}
@Override
public boolean renameItem( int position, String newName, boolean copy ) {
if( position <= 0 || position > items.length )
return false;
try {
LsItem from = items[position - 1];
String[] a = new String[1];
a[0] = uri.getPath() + SLS + from.getName();
String to = uri.getPath() + SLS + newName;
notify( Commander.OPERATION_STARTED );
if( copy ) {
// TODO
return false;
}
commander.startEngine( new CopyToEngine( commander.getContext(), a, true, to, true ) );
return true;
} catch( Exception e ) {
notify( "Exception: " + e, Commander.OPERATION_FAILED );
}
return false;
}
/*
* BaseAdapter implementation
*/
@Override
public Object getItem( int position ) {
Item item = new Item();
item.name = "???";
{
if( position == 0 ) {
item.name = parentLink;
}
else {
if( items != null && position > 0 && position <= items.length ) {
LsItem curItem;
curItem = items[position - 1];
item.dir = curItem.isDirectory();
item.name = item.dir ? SLS + curItem.getName() : curItem.getName();
String lnk = curItem.getLinkTarget();
if( lnk != null )
item.name += " -> " + lnk;
item.size = curItem.isDirectory() ? -1 : curItem.length();
item.date = curItem.getDate();
item.attr = curItem.getAttr();
if( ".apk".equals( Utils.getFileExt( item.name ) ) ) {
try {
PackageManager pm = ctx.getPackageManager();
String path = Utils.mbAddSl( uri.getPath() );
PackageInfo info = pm.getPackageArchiveInfo( path + item.name, 0 );
item.setIcon( info != null ? pm.getApplicationIcon( info.packageName ) :
pm.getDefaultActivityIcon() );
}
catch( Exception e ) {
}
}
}
}
}
return item;
}
@Override
protected int getPredictedAttributesLength() {
return 28; // "---------- system system"
}
private final LsItem[] bitsToItems( SparseBooleanArray cis ) {
try {
int counter = 0;
for( int i = 0; i < cis.size(); i++ )
if( cis.valueAt( i ) )
counter++;
LsItem[] subItems = new LsItem[counter];
int j = 0;
for( int i = 0; i < cis.size(); i++ )
if( cis.valueAt( i ) ) {
int k = cis.keyAt( i );
if( k > 0 )
subItems[j++] = items[ k - 1 ];
}
return subItems;
} catch( Exception e ) {
Log.e( TAG, "bitsToNames()'s Exception: " + e );
}
return null;
}
@Override
public void populateContextMenu( ContextMenu menu, AdapterView.AdapterContextMenuInfo acmi, int num ) {
try {
if( acmi.position > 0 )
menu.add( 0, CHMOD_CMD, 0, R.string.perms_label );
menu.add( 0, CMD_CMD, 0, commander.getContext().getString( R.string.execute_command ) );
super.populateContextMenu( menu, acmi, num );
} catch( Exception e ) {
Log.e( TAG, null, e );
}
}
@Override
public void doIt( int command_id, SparseBooleanArray cis ) {
try {
if( CHMOD_CMD == command_id || CMD_CMD == command_id ) {
LsItem[] items_todo = bitsToItems( cis );
boolean selected_one = items_todo != null && items_todo.length > 0 && items_todo[0] != null;
if( CHMOD_CMD == command_id ) {
if( selected_one ) {
Intent i = new Intent( ctx, EditRootPermissions.class );
i.putExtra( "perm", items_todo[0].getAttr() );
i.putExtra( "path", Utils.mbAddSl( uri.getPath() ) + items_todo[0].getName() );
commander.issue( i, Commander.ACTIVITY_REQUEST_FOR_NOTIFY_RESULT );
}
else
commander.showError( commander.getContext().getString( R.string.select_some ) );
}
else if( CMD_CMD == command_id )
new CmdDialog( commander.getContext(), selected_one ? items_todo[0] : null, this );
} else if( R.id.remount == command_id ) {
if( reader != null && reader.isAlive() ) {
commander.showError( commander.getContext().getString( R.string.busy ) );
return;
}
systemMountReader = new MountsListEngine( commander.getContext(), readerHandler, true );
systemMountReader.start();
}
} catch( Exception e ) {
Log.e( TAG, "Can't do the command " + command_id, e );
}
}
public void execute( String command, boolean bb ) {
commander.startEngine( new ExecEngine( commander.getContext(), uri.getPath(), command, bb, 500 ) );
}
public final String getSuPath() {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences( ctx );
return sharedPref.getString( "su_path", "su" );
}
public final String getBusyBoxPath() {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences( ctx );
return sharedPref.getString( "busybox_path", "busybox" ) + " ";
}
public void executeToViewer( String command, boolean bb ) {
ExecEngine ee = new ExecEngine( ctx, uri.getPath(), command, bb, 500 );
ee.setHandler( new Handler() {
@Override
public void handleMessage( Message msg ) {
try {
String str = ((Bundle)msg.obj).getString( Commander.MESSAGE_STRING );
if( !Utils.str( str ) ) {
msg.obj = ctx.getString( R.string.nothing );
commander.notifyMe( msg );
}
else {
Intent in = new Intent( ctx, TextViewer.class );
in.setData( Uri.parse( TextViewer.STRURI ) );
in.putExtra( TextViewer.STRKEY, str );
commander.issue( in, 0 );
}
} catch( Exception e ) {
e.printStackTrace();
}
}
} );
ee.start();
}
class CmdDialog implements OnClickListener {
private LsItem item;
private RootAdapter owner;
private EditText ctv;
private CheckBox bbc;
CmdDialog( Context c, LsItem item_, RootAdapter owner_ ) {
try {
if( uri == null ) return;
owner = owner_;
item = item_;
LayoutInflater factory = LayoutInflater.from( c );
View cdv = factory.inflate( R.layout.command, null );
if( cdv != null ) {
bbc = (CheckBox)cdv.findViewById( R.id.use_busybox );
ctv = (EditText)cdv.findViewById( R.id.command_text );
ctv.setText( item != null ? item.getName() : "" );
new AlertDialog.Builder( c )
.setTitle( "Run Command" )
.setView( cdv )
.setPositiveButton( R.string.dialog_ok, this )
.setNegativeButton( R.string.dialog_cancel, this )
.show();
}
} catch( Exception e ) {
Log.e( TAG, "CmdDialog()", e );
}
}
@Override
public void onClick( DialogInterface idialog, int whichButton ) {
if( whichButton == DialogInterface.BUTTON_POSITIVE )
owner.executeToViewer( ctv.getText().toString(), bbc.isChecked() );
idialog.dismiss();
}
}
@Override
protected void reSort() {
if( items == null || items.length < 1 ) return;
LsItemPropComparator comp = items[0].new LsItemPropComparator( mode & MODE_SORTING, (mode & MODE_CASE) != 0, ascending );
Arrays.sort( items, comp );
}
/* --- ContentEngine --- */
class ContentEngine extends Thread {
private String file_path;
private InputStream is = null;
private OutputStream os = null;
private boolean open_done = false;
private boolean may_close = false;
ContentEngine( String file_path_ ) {
file_path = file_path_;
}
@Override
public void run() {
setName( "ContentEngine" );
OutputStreamWriter osw = null;
BufferedReader ebr = null;
try {
Process process = Runtime.getRuntime().exec( getSuPath() );
os = process.getOutputStream();
ebr = new BufferedReader( new InputStreamReader( process.getErrorStream() ) );
osw = new OutputStreamWriter( os );
is = process.getInputStream();
osw.write( "cat " + ExecEngine.prepFileName( file_path ) + "\n" );
osw.flush();
for( int i = 0; i < 5; i++ ) {
Thread.sleep( 10 );
if( is.available() > 0 ) break;
//Log.v( TAG, "Waiting the stream starts " + i );
}
boolean empty = is.available() <= 0;
synchronized( this ) {
open_done = true;
}
for( int i = 0; i < 4; i++ ) {
//Log.v( TAG, "Waiting loop " + i );
synchronized( this ) {
//Log.v( TAG, "Waiting the stream can be closed " + i );
wait( 500 );
if( empty ) {
//Log.v( TAG, "We know the stream is empty, so won't let other thread waste precious time!" );
break;
}
if( may_close ) {
//Log.v( TAG, "Reading finished, now may be closed" );
break;
}
/*
try {
Log.v( TAG, "Checking is there any data " + i );
if( is.available() > 0 ) // there still data
i = 0;
}
catch( IOException e ) {
Log.e( TAG, "waiting " + i, e );
}
*/
}
}
osw.write( "exit\n" );
osw.flush();
//Log.v( TAG, "Waitng the process exits" );
process.waitFor();
//Log.v( TAG, "The process has exited" );
if( process.exitValue() != 0 ) {
Log.e( TAG, "Exit code " + process.exitValue() );
}
if( ebr.ready() ) {
String err_str = ebr.readLine();
if( err_str.trim().length() > 0 ) {
Log.e( TAG, "Error:\n" + err_str );
}
}
}
catch( Exception e ) {
Log.e( TAG, null, e );
}
finally {
try {
if( osw != null ) osw.close();
if( ebr != null ) ebr.close();
if( is != null ) is.close();
} catch( IOException e ) {
e.printStackTrace();
}
}
}
public synchronized boolean waitUntilOpen() {
try {
for( int i = 0; i < 50; i++ ) {
if( open_done )
return true;
wait( 100 );
}
} catch( InterruptedException e ) {}
return false;
}
public InputStream getInput() {
return waitUntilOpen() ? is : null;
}
public OutputStream getOutput() {
return waitUntilOpen() ? os : null;
}
public synchronized void close() {
may_close = true;
notify();
}
}
@Override
public Item getItem( Uri u ) {
try {
ExecEngine ee = new ExecEngine( null, null, "ls -l -d " + u.getPath(), false, 100 );
ee.start();
StringBuilder sb = null;
for( int i = 0; i < 10; i++ )
sb = ee.getResult();
if( sb == null ) return null;
LsItem ls_item = new LsItem( sb.toString() );
Item item = new Item( ls_item.getName() );
item.size = ls_item.length();
item.date = ls_item.getDate();
item.dir = ls_item.isDirectory();
return item;
} catch( Throwable e ) {
e.printStackTrace();
}
return null;
}
@Override
public InputStream getContent( Uri u ) {
try {
if( u == null ) return null;
String path = u.getPath();
contentEngine = new ContentEngine( path );
contentEngine.start();
InputStream is = contentEngine.getInput();
if( is == null )
contentEngine.close();
return is;
} catch( Throwable e ) {
Log.e( TAG, u.toString(), e );
}
return null;
}
@Override
public OutputStream saveContent( Uri u ) {
try {
if( u == null ) return null;
String path = u.getPath();
dst_f = new File( path );
File root_f = ctx.getDir( "root", Context.MODE_PRIVATE );
if( root_f == null )
return null;
tmp_f = new File( root_f, dst_f.getName() );
return new FileOutputStream( tmp_f );
} catch( Throwable e ) {
Log.e( TAG, u.toString(), e );
}
return null;
}
@Override
public void closeStream( Closeable s ) {
if( s instanceof FileOutputStream ) {
if( tmp_f == null || dst_f == null ) return;
CopyToEngine cte = new CopyToEngine( ctx, new String[] { tmp_f.getAbsolutePath() },
true, dst_f.getParent(), true );
cte.setPermByDest();
cte.start();
return;
}
if( contentEngine != null ) {
contentEngine.close();
contentEngine = null;
}
}
}