Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
27ba7d64
Commit
27ba7d64
authored
Nov 17, 2014
by
Max Lv
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix a very old bug in redsocks
parent
8fb5d7f8
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
813 additions
and
408 deletions
+813
-408
jni/redsocks/redsocks.c
jni/redsocks/redsocks.c
+1
-1
src/main/java/eu/chainfire/libsuperuser/Application.java
src/main/java/eu/chainfire/libsuperuser/Application.java
+1
-1
src/main/java/eu/chainfire/libsuperuser/Debug.java
src/main/java/eu/chainfire/libsuperuser/Debug.java
+2
-1
src/main/java/eu/chainfire/libsuperuser/Shell.java
src/main/java/eu/chainfire/libsuperuser/Shell.java
+787
-393
src/main/java/eu/chainfire/libsuperuser/ShellNotClosedException.java
...va/eu/chainfire/libsuperuser/ShellNotClosedException.java
+1
-1
src/main/java/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
...eu/chainfire/libsuperuser/ShellOnMainThreadException.java
+1
-1
src/main/java/eu/chainfire/libsuperuser/StreamGobbler.java
src/main/java/eu/chainfire/libsuperuser/StreamGobbler.java
+1
-1
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
+1
-0
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
.../scala/com/github/shadowsocks/ShadowsocksNatService.scala
+18
-9
No files found.
jni/redsocks/redsocks.c
View file @
27ba7d64
...
@@ -780,7 +780,7 @@ static int redsocks_init_instance(redsocks_instance *instance)
...
@@ -780,7 +780,7 @@ static int redsocks_init_instance(redsocks_instance *instance)
int
on
=
1
;
int
on
=
1
;
int
fd
=
-
1
;
int
fd
=
-
1
;
fd
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
);
fd
=
socket
(
AF_INET
,
SOCK_STREAM
,
IPPROTO_TCP
);
if
(
fd
==
-
1
)
{
if
(
fd
==
-
1
)
{
log_errno
(
LOG_ERR
,
"socket"
);
log_errno
(
LOG_ERR
,
"socket"
);
goto
fail
;
goto
fail
;
...
...
src/main/java/eu/chainfire/libsuperuser/Application.java
View file @
27ba7d64
/*
/*
* Copyright (C) 2012-201
3
Jorrit "Chainfire" Jongma
* Copyright (C) 2012-201
4
Jorrit "Chainfire" Jongma
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
...
src/main/java/eu/chainfire/libsuperuser/Debug.java
View file @
27ba7d64
/*
/*
* Copyright (C) 2012-201
3
Jorrit "Chainfire" Jongma
* Copyright (C) 2012-201
4
Jorrit "Chainfire" Jongma
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -18,6 +18,7 @@ package eu.chainfire.libsuperuser;
...
@@ -18,6 +18,7 @@ package eu.chainfire.libsuperuser;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.util.Log
;
import
android.util.Log
;
import
com.github.shadowsocks.BuildConfig
;
import
com.github.shadowsocks.BuildConfig
;
/**
/**
...
...
src/main/java/eu/chainfire/libsuperuser/Shell.java
View file @
27ba7d64
/*
/*
* Copyright (C) 2012-201
3
Jorrit "Chainfire" Jongma
* Copyright (C) 2012-201
4
Jorrit "Chainfire" Jongma
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -38,20 +38,23 @@ import android.os.Looper;
...
@@ -38,20 +38,23 @@ import android.os.Looper;
import
eu.chainfire.libsuperuser.StreamGobbler.OnLineListener
;
import
eu.chainfire.libsuperuser.StreamGobbler.OnLineListener
;
/**
/**
* Class providing functionality to execute commands in a (root) shell
* Class providing functionality to execute commands in a (root) shell
*/
*/
public
class
Shell
{
public
class
Shell
{
/**
/**
* <p>Runs commands using the supplied shell, and returns the output, or null in
* <p>
* case of errors.</p>
* Runs commands using the supplied shell, and returns the output, or null
*
* in case of errors.
* <p>This method is deprecated and only provided for backwards compatibility.
* </p>
* Use {@link #run(String, String[], String[], boolean)} instead, and see that
* <p>
* same method for usage notes.</p>
* This method is deprecated and only provided for backwards compatibility.
* Use {@link #run(String, String[], String[], boolean)} instead, and see
* that same method for usage notes.
* </p>
*
*
* @param shell The shell to use for executing the commands
* @param shell The shell to use for executing the commands
* @param commands The commands to execute
* @param commands The commands to execute
* @param wantSTDERR Return STDERR in the output ?
* @param wantSTDERR Return STDERR in the output ?
* @return Output of the commands, or null in case of an error
* @return Output of the commands, or null in case of an error
*/
*/
@Deprecated
@Deprecated
...
@@ -60,38 +63,48 @@ public class Shell {
...
@@ -60,38 +63,48 @@ public class Shell {
}
}
/**
/**
* <p>Runs commands using the supplied shell, and returns the output, or null in
* <p>
* case of errors.</p>
* Runs commands using the supplied shell, and returns the output, or null
*
* in case of errors.
* <p>Note that due to compatibility with older Android versions,
* </p>
* wantSTDERR is not implemented using redirectErrorStream, but rather appended
* <p>
* to the output. STDOUT and STDERR are thus not guaranteed to be in the correct
* Note that due to compatibility with older Android versions, wantSTDERR is
* order in the output.</p>
* not implemented using redirectErrorStream, but rather appended to the
*
* output. STDOUT and STDERR are thus not guaranteed to be in the correct
* <p>Note as well that this code will intentionally crash when run in debug mode
* order in the output.
* from the main thread of the application. You should always execute shell
* </p>
* commands from a background thread.</p>
* <p>
*
* Note as well that this code will intentionally crash when run in debug
* <p>When in debug mode, the code will also excessively log the commands passed to
* mode from the main thread of the application. You should always execute
* and the output returned from the shell.</p>
* shell commands from a background thread.
*
* </p>
* <p>Though this function uses background threads to gobble STDOUT and STDERR so
* <p>
* a deadlock does not occur if the shell produces massive output, the output is
* When in debug mode, the code will also excessively log the commands
* still stored in a List<String>, and as such doing something like <em>'ls -lR /'</em>
* passed to and the output returned from the shell.
* will probably have you run out of memory.</p>
* </p>
* <p>
* Though this function uses background threads to gobble STDOUT and STDERR
* so a deadlock does not occur if the shell produces massive output, the
* output is still stored in a List<String>, and as such doing
* something like <em>'ls -lR /'</em> will probably have you run out of
* memory.
* </p>
*
*
* @param shell The shell to use for executing the commands
* @param shell The shell to use for executing the commands
* @param commands The commands to execute
* @param commands The commands to execute
* @param environment List of all environment variables (in 'key=value' format) or null for defaults
* @param environment List of all environment variables (in 'key=value'
* @param wantSTDERR Return STDERR in the output ?
* format) or null for defaults
* @param wantSTDERR Return STDERR in the output ?
* @return Output of the commands, or null in case of an error
* @return Output of the commands, or null in case of an error
*/
*/
public
static
List
<
String
>
run
(
String
shell
,
String
[]
commands
,
String
[]
environment
,
boolean
wantSTDERR
)
{
public
static
List
<
String
>
run
(
String
shell
,
String
[]
commands
,
String
[]
environment
,
boolean
wantSTDERR
)
{
String
shellUpper
=
shell
.
toUpperCase
(
Locale
.
ENGLISH
);
String
shellUpper
=
shell
.
toUpperCase
(
Locale
.
ENGLISH
);
if
(
Debug
.
getSanityChecksEnabledEffective
()
&&
Debug
.
onMainThread
())
{
if
(
Debug
.
getSanityChecksEnabledEffective
()
&&
Debug
.
onMainThread
())
{
// check if we're running in the main thread, and if so, crash if we're in debug mode,
// check if we're running in the main thread, and if so, crash if
// to let the developer know attention is needed here.
// we're in debug mode, to let the developer know attention is
// needed here.
Debug
.
log
(
ShellOnMainThreadException
.
EXCEPTION_COMMAND
);
Debug
.
log
(
ShellOnMainThreadException
.
EXCEPTION_COMMAND
);
throw
new
ShellOnMainThreadException
(
ShellOnMainThreadException
.
EXCEPTION_COMMAND
);
throw
new
ShellOnMainThreadException
(
ShellOnMainThreadException
.
EXCEPTION_COMMAND
);
...
@@ -119,11 +132,14 @@ public class Shell {
...
@@ -119,11 +132,14 @@ public class Shell {
}
}
}
}
// setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers
// setup our process, retrieve STDIN stream, and STDOUT/STDERR
// gobblers
Process
process
=
Runtime
.
getRuntime
().
exec
(
shell
,
environment
);
Process
process
=
Runtime
.
getRuntime
().
exec
(
shell
,
environment
);
DataOutputStream
STDIN
=
new
DataOutputStream
(
process
.
getOutputStream
());
DataOutputStream
STDIN
=
new
DataOutputStream
(
process
.
getOutputStream
());
StreamGobbler
STDOUT
=
new
StreamGobbler
(
shellUpper
+
"-"
,
process
.
getInputStream
(),
res
);
StreamGobbler
STDOUT
=
new
StreamGobbler
(
shellUpper
+
"-"
,
process
.
getInputStream
(),
StreamGobbler
STDERR
=
new
StreamGobbler
(
shellUpper
+
"*"
,
process
.
getErrorStream
(),
wantSTDERR
?
res
:
null
);
res
);
StreamGobbler
STDERR
=
new
StreamGobbler
(
shellUpper
+
"*"
,
process
.
getErrorStream
(),
wantSTDERR
?
res
:
null
);
// start gobbling and write our commands to the shell
// start gobbling and write our commands to the shell
STDOUT
.
start
();
STDOUT
.
start
();
...
@@ -133,17 +149,25 @@ public class Shell {
...
@@ -133,17 +149,25 @@ public class Shell {
STDIN
.
write
((
write
+
"\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
write
((
write
+
"\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
flush
();
STDIN
.
flush
();
}
}
STDIN
.
write
(
"exit\n"
.
getBytes
(
"UTF-8"
));
try
{
STDIN
.
flush
();
STDIN
.
write
(
"exit\n"
.
getBytes
(
"UTF-8"
));
STDIN
.
flush
();
}
catch
(
IOException
e
)
{
// happens if the script already contains the exit line - if
// there were a more serious issue, it would already have thrown
// an exception while writing the script to STDIN
}
// wait for our process to finish, while we gobble away in the background
// wait for our process to finish, while we gobble away in the
// background
process
.
waitFor
();
process
.
waitFor
();
// make sure our threads are done gobbling, our streams are closed, and the process is
// make sure our threads are done gobbling, our streams are closed,
// destroyed - while the latter two shouldn't be needed in theory, and may even produce
// and the process is destroyed - while the latter two shouldn't be
// warnings, in "normal" Java they are required for guaranteed cleanup of resources, so
// needed in theory, and may even produce warnings, in "normal" Java
// lets be safe and do this on Android as well
// they are required for guaranteed cleanup of resources, so lets be
try
{
// safe and do this on Android as well
try
{
STDIN
.
close
();
STDIN
.
close
();
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
}
}
...
@@ -154,7 +178,7 @@ public class Shell {
...
@@ -154,7 +178,7 @@ public class Shell {
// in case of su, 255 usually indicates access denied
// in case of su, 255 usually indicates access denied
if
(
SU
.
isSU
(
shell
)
&&
(
process
.
exitValue
()
==
255
))
{
if
(
SU
.
isSU
(
shell
)
&&
(
process
.
exitValue
()
==
255
))
{
res
=
null
;
res
=
null
;
}
}
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
// shell probably not found
// shell probably not found
res
=
null
;
res
=
null
;
...
@@ -168,19 +192,21 @@ public class Shell {
...
@@ -168,19 +192,21 @@ public class Shell {
}
}
protected
static
String
[]
availableTestCommands
=
new
String
[]
{
protected
static
String
[]
availableTestCommands
=
new
String
[]
{
"echo -BOC-"
,
"echo -BOC-"
,
"id"
"id"
};
};
/**
/**
* See if the shell is alive, and if so, check the UID
* See if the shell is alive, and if so, check the UID
*
*
* @param ret Standard output from running availableTestCommands
* @param ret Standard output from running availableTestCommands
* @param checkForRoot true if we are expecting this shell to be running as root
* @param checkForRoot true if we are expecting this shell to be running as
* root
* @return true on success, false on error
* @return true on success, false on error
*/
*/
protected
static
boolean
parseAvailableResult
(
List
<
String
>
ret
,
boolean
checkForRoot
)
{
protected
static
boolean
parseAvailableResult
(
List
<
String
>
ret
,
boolean
checkForRoot
)
{
if
(
ret
==
null
)
return
false
;
if
(
ret
==
null
)
return
false
;
// this is only one of many ways this can be done
// this is only one of many ways this can be done
boolean
echo_seen
=
false
;
boolean
echo_seen
=
false
;
...
@@ -190,8 +216,10 @@ public class Shell {
...
@@ -190,8 +216,10 @@ public class Shell {
// id command is working, let's see if we are actually root
// id command is working, let's see if we are actually root
return
!
checkForRoot
||
line
.
contains
(
"uid=0"
);
return
!
checkForRoot
||
line
.
contains
(
"uid=0"
);
}
else
if
(
line
.
contains
(
"-BOC-"
))
{
}
else
if
(
line
.
contains
(
"-BOC-"
))
{
// if we end up here, at least the su command starts some kind of shell,
// if we end up here, at least the su command starts some kind
// let's hope it has root privileges - no way to know without additional
// of shell,
// let's hope it has root privileges - no way to know without
// additional
// native binaries
// native binaries
echo_seen
=
true
;
echo_seen
=
true
;
}
}
...
@@ -211,7 +239,9 @@ public class Shell {
...
@@ -211,7 +239,9 @@ public class Shell {
* @return Output of the command, or null in case of an error
* @return Output of the command, or null in case of an error
*/
*/
public
static
List
<
String
>
run
(
String
command
)
{
public
static
List
<
String
>
run
(
String
command
)
{
return
Shell
.
run
(
"sh"
,
new
String
[]
{
command
},
null
,
false
);
return
Shell
.
run
(
"sh"
,
new
String
[]
{
command
},
null
,
false
);
}
}
/**
/**
...
@@ -241,21 +271,30 @@ public class Shell {
...
@@ -241,21 +271,30 @@ public class Shell {
* if so which version.
* if so which version.
*/
*/
public
static
class
SU
{
public
static
class
SU
{
private
static
Boolean
isSELinuxEnforcing
=
null
;
private
static
String
[]
suVersion
=
new
String
[]
{
null
,
null
};
/**
/**
* Runs command as root (if available) and return output
* Runs command as root (if available) and return output
*
*
* @param command The command to run
* @param command The command to run
* @return Output of the command, or null if root isn't available or in case of an error
* @return Output of the command, or null if root isn't available or in
* case of an error
*/
*/
public
static
List
<
String
>
run
(
String
command
)
{
public
static
List
<
String
>
run
(
String
command
)
{
return
Shell
.
run
(
"su"
,
new
String
[]
{
command
},
null
,
false
);
return
Shell
.
run
(
"su"
,
new
String
[]
{
command
},
null
,
false
);
}
}
/**
/**
* Runs commands as root (if available) and return output
* Runs commands as root (if available) and return output
*
*
* @param commands The commands to run
* @param commands The commands to run
* @return Output of the commands, or null if root isn't available or in case of an error
* @return Output of the commands, or null if root isn't available or in
* case of an error
*/
*/
public
static
List
<
String
>
run
(
List
<
String
>
commands
)
{
public
static
List
<
String
>
run
(
List
<
String
>
commands
)
{
return
Shell
.
run
(
"su"
,
commands
.
toArray
(
new
String
[
commands
.
size
()]),
null
,
false
);
return
Shell
.
run
(
"su"
,
commands
.
toArray
(
new
String
[
commands
.
size
()]),
null
,
false
);
...
@@ -265,15 +304,17 @@ public class Shell {
...
@@ -265,15 +304,17 @@ public class Shell {
* Runs commands as root (if available) and return output
* Runs commands as root (if available) and return output
*
*
* @param commands The commands to run
* @param commands The commands to run
* @return Output of the commands, or null if root isn't available or in case of an error
* @return Output of the commands, or null if root isn't available or in
* case of an error
*/
*/
public
static
List
<
String
>
run
(
String
[]
commands
)
{
public
static
List
<
String
>
run
(
String
[]
commands
)
{
return
Shell
.
run
(
"su"
,
commands
,
null
,
false
);
return
Shell
.
run
(
"su"
,
commands
,
null
,
false
);
}
}
/**
/**
* Detects whether or not superuser access is available, by checking the output
* Detects whether or not superuser access is available, by checking the
* of the "id" command if available, checking if a shell runs at all otherwise
* output of the "id" command if available, checking if a shell runs at
* all otherwise
*
*
* @return True if superuser access available
* @return True if superuser access available
*/
*/
...
@@ -285,41 +326,65 @@ public class Shell {
...
@@ -285,41 +326,65 @@ public class Shell {
}
}
/**
/**
* <p>Detects the version of the su binary installed (if any), if supported by the binary.
* <p>
* Most binaries support two different version numbers, the public version that is
* Detects the version of the su binary installed (if any), if supported
* displayed to users, and an internal version number that is used for version number
* by the binary. Most binaries support two different version numbers,
* comparisons. Returns null if su not available or retrieving the version isn't supported.</p>
* the public version that is displayed to users, and an internal
*
* version number that is used for version number comparisons. Returns
* <p>Note that su binary version and GUI (APK) version can be completely different.</p>
* null if su not available or retrieving the version isn't supported.
*
* </p>
* @param internal Request human-readable version or application internal version
* <p>
* Note that su binary version and GUI (APK) version can be completely
* different.
* </p>
* <p>
* This function caches its result to improve performance on multiple
* calls
* </p>
*
* @param internal Request human-readable version or application
* internal version
* @return String containing the su version or null
* @return String containing the su version or null
*/
*/
public
static
String
version
(
boolean
internal
)
{
public
static
synchronized
String
version
(
boolean
internal
)
{
List
<
String
>
ret
=
Shell
.
run
(
int
idx
=
internal
?
0
:
1
;
internal
?
"su -V"
:
"su -v"
,
if
(
suVersion
[
idx
]
==
null
)
{
new
String
[]
{
},
String
version
=
null
;
null
,
false
List
<
String
>
ret
=
Shell
.
run
(
);
internal
?
"su -V"
:
"su -v"
,
if
(
ret
==
null
)
return
null
;
new
String
[]
{},
null
,
for
(
String
line
:
ret
)
{
false
if
(!
internal
)
{
);
if
(
line
.
contains
(
"."
))
return
line
;
}
else
{
if
(
ret
!=
null
)
{
try
{
for
(
String
line
:
ret
)
{
if
(
Integer
.
parseInt
(
line
)
>
0
)
return
line
;
if
(!
internal
)
{
}
catch
(
NumberFormatException
e
)
{
if
(
line
.
contains
(
"."
))
{
version
=
line
;
break
;
}
}
else
{
try
{
if
(
Integer
.
parseInt
(
line
)
>
0
)
{
version
=
line
;
break
;
}
}
catch
(
NumberFormatException
e
)
{
}
}
}
}
}
}
suVersion
[
idx
]
=
version
;
}
}
return
null
;
return
suVersion
[
idx
]
;
}
}
/**
/**
* Attempts to deduce if the shell command refers to a su shell
* Attempts to deduce if the shell command refers to a su shell
*
*
* @param shell Shell command to run
* @param shell Shell command to run
* @return Shell command appears to be su
* @return Shell command appears to be su
*/
*/
...
@@ -340,9 +405,9 @@ public class Shell {
...
@@ -340,9 +405,9 @@ public class Shell {
}
}
/**
/**
* Constructs a shell command to start a su shell using the supplied
* Constructs a shell command to start a su shell using the supplied
uid
*
uid and SELinux context. This is can be an expensive operation,
*
and SELinux context. This is can be an expensive operation, consider
* c
onsider c
aching the result.
* caching the result.
*
*
* @param uid Uid to use (0 == root)
* @param uid Uid to use (0 == root)
* @param context (SELinux) context name to use or null
* @param context (SELinux) context name to use or null
...
@@ -352,47 +417,16 @@ public class Shell {
...
@@ -352,47 +417,16 @@ public class Shell {
// su[ --context <context>][ <uid>]
// su[ --context <context>][ <uid>]
String
shell
=
"su"
;
String
shell
=
"su"
;
// First known firmware with SELinux built-in was a 4.2 (17) leak
if
((
context
!=
null
)
&&
isSELinuxEnforcing
())
{
if
((
context
!=
null
)
&&
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
>=
17
))
{
String
display
=
version
(
false
);
Boolean
enforcing
=
null
;
String
internal
=
version
(
true
);
// Detect enforcing through sysfs, not always present
if
(
enforcing
==
null
)
{
File
f
=
new
File
(
"/sys/fs/selinux/enforce"
);
if
(
f
.
exists
())
{
try
{
InputStream
is
=
new
FileInputStream
(
"/sys/fs/selinux/enforce"
);
try
{
enforcing
=
(
is
.
read
()
==
'1'
);
}
finally
{
is
.
close
();
}
}
catch
(
Exception
e
)
{
}
}
}
// 4.4+ builds are enforcing by default, take the gamble
if
(
enforcing
==
null
)
{
enforcing
=
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
>=
19
);
}
// Switching to a context in permissive mode is not generally
// We only know the format for SuperSU v1.90+ right now
// useful aside from audit testing, but we want to avoid
if
((
display
!=
null
)
&&
// switching the context due to the increased chance of issues.
(
internal
!=
null
)
&&
if
(
enforcing
)
{
(
display
.
endsWith
(
"SUPERSU"
))
&&
String
display
=
version
(
false
);
(
Integer
.
valueOf
(
internal
)
>=
190
))
{
String
internal
=
version
(
true
);
shell
=
String
.
format
(
Locale
.
ENGLISH
,
"%s --context %s"
,
shell
,
context
);
// We only know the format for SuperSU v1.90+ right now
if
(
(
display
!=
null
)
&&
(
internal
!=
null
)
&&
(
display
.
endsWith
(
"SUPERSU"
))
&&
(
Integer
.
valueOf
(
internal
)
>=
190
)
)
{
shell
=
String
.
format
(
Locale
.
ENGLISH
,
"%s --context %s"
,
shell
,
context
);
}
}
}
}
}
...
@@ -404,29 +438,84 @@ public class Shell {
...
@@ -404,29 +438,84 @@ public class Shell {
return
shell
;
return
shell
;
}
}
}
/**
* Command result callback, notifies the recipient of the completion of a command
* block, including the (last) exit code, and the full output
*/
public
interface
OnCommandResultListener
{
/**
/**
* <p>Command result callback</p>
* Constructs a shell command to start a su shell connected to mount
*
* master daemon, to perform public mounts on Android 4.3+ (or 4.2+ in
* <p>Depending on how and on which thread the shell was created, this callback
* SELinux enforcing mode)
* may be executed on one of the gobbler threads. In that case, it is important
* the callback returns as quickly as possible, as delays in this callback may
* pause the native process or even result in a deadlock</p>
*
*
* <p>See {@link Shell.Interactive} for threading details</p>
* @return Shell command
*/
public
static
String
shellMountMaster
()
{
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
>=
17
)
{
return
"su --mount-master"
;
}
return
"su"
;
}
/**
* Detect if SELinux is set to enforcing, caches result
*
*
* @param commandCode Value previously supplied to addCommand
* @return true if SELinux set to enforcing, or false in the case of
* @param exitCode Exit code of the last command in the block
* permissive or not present
* @param output All output generated by the command block
*/
*/
public
void
onCommandResult
(
int
commandCode
,
int
exitCode
,
List
<
String
>
output
);
public
static
synchronized
boolean
isSELinuxEnforcing
()
{
if
(
isSELinuxEnforcing
==
null
)
{
Boolean
enforcing
=
null
;
// First known firmware with SELinux built-in was a 4.2 (17)
// leak
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
>=
17
)
{
// Detect enforcing through sysfs, not always present
if
(
enforcing
==
null
)
{
File
f
=
new
File
(
"/sys/fs/selinux/enforce"
);
if
(
f
.
exists
())
{
try
{
InputStream
is
=
new
FileInputStream
(
"/sys/fs/selinux/enforce"
);
try
{
enforcing
=
(
is
.
read
()
==
'1'
);
}
finally
{
is
.
close
();
}
}
catch
(
Exception
e
)
{
}
}
}
// 4.4+ builds are enforcing by default, take the gamble
if
(
enforcing
==
null
)
{
enforcing
=
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
>=
19
);
}
}
if
(
enforcing
==
null
)
{
enforcing
=
false
;
}
isSELinuxEnforcing
=
enforcing
;
}
return
isSELinuxEnforcing
;
}
/**
* <p>
* Clears results cached by isSELinuxEnforcing() and version(boolean
* internal) calls.
* </p>
* <p>
* Most apps should never need to call this, as neither enforcing status
* nor su version is likely to change on a running device - though it is
* not impossible.
* </p>
*/
public
static
synchronized
void
clearCachedResults
()
{
isSELinuxEnforcing
=
null
;
suVersion
[
0
]
=
null
;
suVersion
[
1
]
=
null
;
}
}
private
interface
OnResult
{
// for any onCommandResult callback
// for any onCommandResult callback
public
static
final
int
WATCHDOG_EXIT
=
-
1
;
public
static
final
int
WATCHDOG_EXIT
=
-
1
;
public
static
final
int
SHELL_DIED
=
-
2
;
public
static
final
int
SHELL_DIED
=
-
2
;
...
@@ -437,6 +526,60 @@ public class Shell {
...
@@ -437,6 +526,60 @@ public class Shell {
public
static
final
int
SHELL_RUNNING
=
0
;
public
static
final
int
SHELL_RUNNING
=
0
;
}
}
/**
* Command result callback, notifies the recipient of the completion of a
* command block, including the (last) exit code, and the full output
*/
public
interface
OnCommandResultListener
extends
OnResult
{
/**
* <p>
* Command result callback
* </p>
* <p>
* Depending on how and on which thread the shell was created, this
* callback may be executed on one of the gobbler threads. In that case,
* it is important the callback returns as quickly as possible, as
* delays in this callback may pause the native process or even result
* in a deadlock
* </p>
* <p>
* See {@link Shell.Interactive} for threading details
* </p>
*
* @param commandCode Value previously supplied to addCommand
* @param exitCode Exit code of the last command in the block
* @param output All output generated by the command block
*/
public
void
onCommandResult
(
int
commandCode
,
int
exitCode
,
List
<
String
>
output
);
}
/**
* Command per line callback for parsing the output line by line without
* buffering It also notifies the recipient of the completion of a command
* block, including the (last) exit code.
*/
public
interface
OnCommandLineListener
extends
OnResult
,
OnLineListener
{
/**
* <p>
* Command result callback
* </p>
* <p>
* Depending on how and on which thread the shell was created, this
* callback may be executed on one of the gobbler threads. In that case,
* it is important the callback returns as quickly as possible, as
* delays in this callback may pause the native process or even result
* in a deadlock
* </p>
* <p>
* See {@link Shell.Interactive} for threading details
* </p>
*
* @param commandCode Value previously supplied to addCommand
* @param exitCode Exit code of the last command in the block
*/
public
void
onCommandResult
(
int
commandCode
,
int
exitCode
);
}
/**
/**
* Internal class to store command block properties
* Internal class to store command block properties
*/
*/
...
@@ -446,12 +589,16 @@ public class Shell {
...
@@ -446,12 +589,16 @@ public class Shell {
private
final
String
[]
commands
;
private
final
String
[]
commands
;
private
final
int
code
;
private
final
int
code
;
private
final
OnCommandResultListener
onCommandResultListener
;
private
final
OnCommandResultListener
onCommandResultListener
;
private
final
OnCommandLineListener
onCommandLineListener
;
private
final
String
marker
;
private
final
String
marker
;
public
Command
(
String
[]
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
public
Command
(
String
[]
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
,
OnCommandLineListener
onCommandLineListener
)
{
this
.
commands
=
commands
;
this
.
commands
=
commands
;
this
.
code
=
code
;
this
.
code
=
code
;
this
.
onCommandResultListener
=
onCommandResultListener
;
this
.
onCommandResultListener
=
onCommandResultListener
;
this
.
onCommandLineListener
=
onCommandLineListener
;
this
.
marker
=
UUID
.
randomUUID
().
toString
()
+
String
.
format
(
"-%08x"
,
++
commandCounter
);
this
.
marker
=
UUID
.
randomUUID
().
toString
()
+
String
.
format
(
"-%08x"
,
++
commandCounter
);
}
}
}
}
...
@@ -471,24 +618,38 @@ public class Shell {
...
@@ -471,24 +618,38 @@ public class Shell {
private
int
watchdogTimeout
=
0
;
private
int
watchdogTimeout
=
0
;
/**
/**
* <p>Set a custom handler that will be used to post all callbacks to</p>
* <p>
*
* Set a custom handler that will be used to post all callbacks to
* <p>See {@link Shell.Interactive} for further details on threading and handlers</p>
* </p>
* <p>
* See {@link Shell.Interactive} for further details on threading and
* handlers
* </p>
*
*
* @param handler Handler to use
* @param handler Handler to use
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setHandler
(
Handler
handler
)
{
this
.
handler
=
handler
;
return
this
;
}
public
Builder
setHandler
(
Handler
handler
)
{
this
.
handler
=
handler
;
return
this
;
}
/**
/**
* <p>Automatically create a handler if possible ? Default to true</p>
* <p>
*
* Automatically create a handler if possible ? Default to true
* <p>See {@link Shell.Interactive} for further details on threading and handlers</p>
* </p>
* <p>
* See {@link Shell.Interactive} for further details on threading and
* handlers
* </p>
*
*
* @param autoHandler Auto-create handler ?
* @param autoHandler Auto-create handler ?
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setAutoHandler
(
boolean
autoHandler
)
{
this
.
autoHandler
=
autoHandler
;
return
this
;
}
public
Builder
setAutoHandler
(
boolean
autoHandler
)
{
this
.
autoHandler
=
autoHandler
;
return
this
;
}
/**
/**
* Set shell binary to use. Usually "sh" or "su", do not use a full path
* Set shell binary to use. Usually "sh" or "su", do not use a full path
...
@@ -497,21 +658,28 @@ public class Shell {
...
@@ -497,21 +658,28 @@ public class Shell {
* @param shell Shell to use
* @param shell Shell to use
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setShell
(
String
shell
)
{
this
.
shell
=
shell
;
return
this
;
}
public
Builder
setShell
(
String
shell
)
{
this
.
shell
=
shell
;
return
this
;
}
/**
/**
* Convenience function to set "sh" as used shell
* Convenience function to set "sh" as used shell
*
*
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
useSH
()
{
return
setShell
(
"sh"
);
}
public
Builder
useSH
()
{
return
setShell
(
"sh"
);
}
/**
/**
* Convenience function to set "su" as used shell
* Convenience function to set "su" as used shell
*
*
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
useSU
()
{
return
setShell
(
"su"
);
}
public
Builder
useSU
()
{
return
setShell
(
"su"
);
}
/**
/**
* Set if error output should be appended to command block result output
* Set if error output should be appended to command block result output
...
@@ -519,7 +687,10 @@ public class Shell {
...
@@ -519,7 +687,10 @@ public class Shell {
* @param wantSTDERR Want error output ?
* @param wantSTDERR Want error output ?
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setWantSTDERR
(
boolean
wantSTDERR
)
{
this
.
wantSTDERR
=
wantSTDERR
;
return
this
;
}
public
Builder
setWantSTDERR
(
boolean
wantSTDERR
)
{
this
.
wantSTDERR
=
wantSTDERR
;
return
this
;
}
/**
/**
* Add or update an environment variable
* Add or update an environment variable
...
@@ -528,7 +699,10 @@ public class Shell {
...
@@ -528,7 +699,10 @@ public class Shell {
* @param value Value of the environment variable
* @param value Value of the environment variable
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addEnvironment
(
String
key
,
String
value
)
{
environment
.
put
(
key
,
value
);
return
this
;
}
public
Builder
addEnvironment
(
String
key
,
String
value
)
{
environment
.
put
(
key
,
value
);
return
this
;
}
/**
/**
* Add or update environment variables
* Add or update environment variables
...
@@ -536,7 +710,10 @@ public class Shell {
...
@@ -536,7 +710,10 @@ public class Shell {
* @param addEnvironment Map of environment variables
* @param addEnvironment Map of environment variables
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addEnvironment
(
Map
<
String
,
String
>
addEnvironment
)
{
environment
.
putAll
(
addEnvironment
);
return
this
;
}
public
Builder
addEnvironment
(
Map
<
String
,
String
>
addEnvironment
)
{
environment
.
putAll
(
addEnvironment
);
return
this
;
}
/**
/**
* Add a command to execute
* Add a command to execute
...
@@ -544,19 +721,30 @@ public class Shell {
...
@@ -544,19 +721,30 @@ public class Shell {
* @param command Command to execute
* @param command Command to execute
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addCommand
(
String
command
)
{
return
addCommand
(
command
,
0
,
null
);
}
public
Builder
addCommand
(
String
command
)
{
return
addCommand
(
command
,
0
,
null
);
}
/**
/**
* <p>Add a command to execute, with a callback to be called on completion</p>
* <p>
*
* Add a command to execute, with a callback to be called on completion
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param command Command to execute
* @param command Command to execute
* @param code User-defined value passed back to the callback
* @param code User-defined value passed back to the callback
* @param onCommandResultListener Callback to be called on completion
* @param onCommandResultListener Callback to be called on completion
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addCommand
(
String
command
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
return
addCommand
(
new
String
[]
{
command
},
code
,
onCommandResultListener
);
}
public
Builder
addCommand
(
String
command
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
return
addCommand
(
new
String
[]
{
command
},
code
,
onCommandResultListener
);
}
/**
/**
* Add commands to execute
* Add commands to execute
...
@@ -564,19 +752,31 @@ public class Shell {
...
@@ -564,19 +752,31 @@ public class Shell {
* @param commands Commands to execute
* @param commands Commands to execute
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addCommand
(
List
<
String
>
commands
)
{
return
addCommand
(
commands
,
0
,
null
);
}
public
Builder
addCommand
(
List
<
String
>
commands
)
{
return
addCommand
(
commands
,
0
,
null
);
}
/**
/**
* <p>Add commands to execute, with a callback to be called on completion (of all commands)</p>
* <p>
*
* Add commands to execute, with a callback to be called on completion
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* (of all commands)
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param commands Commands to execute
* @param commands Commands to execute
* @param code User-defined value passed back to the callback
* @param code User-defined value passed back to the callback
* @param onCommandResultListener Callback to be called on completion (of all commands)
* @param onCommandResultListener Callback to be called on completion
* (of all commands)
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addCommand
(
List
<
String
>
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
return
addCommand
(
commands
.
toArray
(
new
String
[
commands
.
size
()]),
code
,
onCommandResultListener
);
}
public
Builder
addCommand
(
List
<
String
>
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
return
addCommand
(
commands
.
toArray
(
new
String
[
commands
.
size
()]),
code
,
onCommandResultListener
);
}
/**
/**
* Add commands to execute
* Add commands to execute
...
@@ -584,58 +784,96 @@ public class Shell {
...
@@ -584,58 +784,96 @@ public class Shell {
* @param commands Commands to execute
* @param commands Commands to execute
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addCommand
(
String
[]
commands
)
{
return
addCommand
(
commands
,
0
,
null
);
}
public
Builder
addCommand
(
String
[]
commands
)
{
return
addCommand
(
commands
,
0
,
null
);
}
/**
/**
* <p>Add commands to execute, with a callback to be called on completion (of all commands)</p>
* <p>
*
* Add commands to execute, with a callback to be called on completion
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* (of all commands)
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param commands Commands to execute
* @param commands Commands to execute
* @param code User-defined value passed back to the callback
* @param code User-defined value passed back to the callback
* @param onCommandResultListener Callback to be called on completion (of all commands)
* @param onCommandResultListener Callback to be called on completion
* (of all commands)
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
addCommand
(
String
[]
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
this
.
commands
.
add
(
new
Command
(
commands
,
code
,
onCommandResultListener
));
return
this
;
}
public
Builder
addCommand
(
String
[]
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
this
.
commands
.
add
(
new
Command
(
commands
,
code
,
onCommandResultListener
,
null
));
return
this
;
}
/**
/**
* <p>Set a callback called for every line output to STDOUT by the shell</p>
* <p>
*
* Set a callback called for every line output to STDOUT by the shell
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param onLineListener Callback to be called for each line
* @param onLineListener Callback to be called for each line
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setOnSTDOUTLineListener
(
OnLineListener
onLineListener
)
{
this
.
onSTDOUTLineListener
=
onLineListener
;
return
this
;
}
public
Builder
setOnSTDOUTLineListener
(
OnLineListener
onLineListener
)
{
this
.
onSTDOUTLineListener
=
onLineListener
;
return
this
;
}
/**
/**
* <p>Set a callback called for every line output to STDERR by the shell</p>
* <p>
*
* Set a callback called for every line output to STDERR by the shell
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param onLineListener Callback to be called for each line
* @param onLineListener Callback to be called for each line
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setOnSTDERRLineListener
(
OnLineListener
onLineListener
)
{
this
.
onSTDERRLineListener
=
onLineListener
;
return
this
;
}
public
Builder
setOnSTDERRLineListener
(
OnLineListener
onLineListener
)
{
this
.
onSTDERRLineListener
=
onLineListener
;
return
this
;
}
/**
/**
* <p>Enable command timeout callback</p>
* <p>
*
* Enable command timeout callback
* <p>This will invoke the onCommandResult() callback with exitCode WATCHDOG_EXIT if a command takes longer than watchdogTimeout
* </p>
* seconds to complete.</p>
* <p>
*
* This will invoke the onCommandResult() callback with exitCode
* <p>If a watchdog timeout occurs, it generally means that the Interactive session is out of sync with the shell process. The
* WATCHDOG_EXIT if a command takes longer than watchdogTimeout seconds
* caller should close the current session and open a new one.</p>
* to complete.
* </p>
* <p>
* If a watchdog timeout occurs, it generally means that the Interactive
* session is out of sync with the shell process. The caller should
* close the current session and open a new one.
* </p>
*
*
* @param watchdogTimeout Timeout, in seconds; 0 to disable
* @param watchdogTimeout Timeout, in seconds; 0 to disable
* @return This Builder object for method chaining
* @return This Builder object for method chaining
*/
*/
public
Builder
setWatchdogTimeout
(
int
watchdogTimeout
)
{
this
.
watchdogTimeout
=
watchdogTimeout
;
return
this
;
}
public
Builder
setWatchdogTimeout
(
int
watchdogTimeout
)
{
this
.
watchdogTimeout
=
watchdogTimeout
;
return
this
;
}
/**
/**
* <p>Enable/disable reduced logcat output</p>
* <p>
*
* Enable/disable reduced logcat output
* <p>Note that this is a global setting</p>
* </p>
* <p>
* Note that this is a global setting
* </p>
*
*
* @param useMinimal true for reduced output, false for full output
* @param useMinimal true for reduced output, false for full output
* @return This Builder object for method chaining
* @return This Builder object for method chaining
...
@@ -648,11 +886,13 @@ public class Shell {
...
@@ -648,11 +886,13 @@ public class Shell {
/**
/**
* Construct a {@link Shell.Interactive} instance, and start the shell
* Construct a {@link Shell.Interactive} instance, and start the shell
*/
*/
public
Interactive
open
()
{
return
new
Interactive
(
this
,
null
);
}
public
Interactive
open
()
{
return
new
Interactive
(
this
,
null
);
}
/**
/**
* Construct a {@link Shell.Interactive} instance, try to start the
shell, and
* Construct a {@link Shell.Interactive} instance, try to start the
* call onCommandResultListener to report success or failure
*
shell, and
call onCommandResultListener to report success or failure
*
*
* @param onCommandResultListener Callback to return shell open status
* @param onCommandResultListener Callback to return shell open status
*/
*/
...
@@ -662,55 +902,70 @@ public class Shell {
...
@@ -662,55 +902,70 @@ public class Shell {
}
}
/**
/**
* <p>An interactive shell - initially created with {@link Shell.Builder} - that
* <p>
* executes blocks of commands you supply in the background, optionally calling
* An interactive shell - initially created with {@link Shell.Builder} -
* callbacks as each block completes.</p>
* that executes blocks of commands you supply in the background, optionally
*
* calling callbacks as each block completes.
* <p>STDERR output can be supplied as well, but due to compatibility with older
* </p>
* Android versions, wantSTDERR is not implemented using redirectErrorStream,
* <p>
* but rather appended to the output. STDOUT and STDERR are thus not guaranteed to
* STDERR output can be supplied as well, but due to compatibility with
* be in the correct order in the output.</p>
* older Android versions, wantSTDERR is not implemented using
*
* redirectErrorStream, but rather appended to the output. STDOUT and STDERR
* <p>Note as well that the close() and waitForIdle() methods will intentionally
* are thus not guaranteed to be in the correct order in the output.
* crash when run in debug mode from the main thread of the application. Any blocking
* </p>
* call should be run from a background thread.</p>
* <p>
*
* Note as well that the close() and waitForIdle() methods will
* <p>When in debug mode, the code will also excessively log the commands passed to
* intentionally crash when run in debug mode from the main thread of the
* and the output returned from the shell.</p>
* application. Any blocking call should be run from a background thread.
*
* </p>
* <p>Though this function uses background threads to gobble STDOUT and STDERR so
* <p>
* a deadlock does not occur if the shell produces massive output, the output is
* When in debug mode, the code will also excessively log the commands
* still stored in a List<String>, and as such doing something like <em>'ls -lR /'</em>
* passed to and the output returned from the shell.
* will probably have you run out of memory when using a
* </p>
* {@link Shell.OnCommandResultListener}. A work-around is to not supply this callback,
* <p>
* but using (only) {@link Shell.Builder#setOnSTDOUTLineListener(OnLineListener)}. This
* Though this function uses background threads to gobble STDOUT and STDERR
* way, an internal buffer will not be created and wasting your memory.</p>
* so a deadlock does not occur if the shell produces massive output, the
*
* output is still stored in a List<String>, and as such doing
* something like <em>'ls -lR /'</em> will probably have you run out of
* memory when using a {@link Shell.OnCommandResultListener}. A work-around
* is to not supply this callback, but using (only)
* {@link Shell.Builder#setOnSTDOUTLineListener(OnLineListener)}. This way,
* an internal buffer will not be created and wasting your memory.
* </p>
* <h3>Callbacks, threads and handlers</h3>
* <h3>Callbacks, threads and handlers</h3>
*
* <p>
* <p>On which thread the callbacks execute is dependent on your initialization. You can
* On which thread the callbacks execute is dependent on your
* supply a custom Handler using {@link Shell.Builder#setHandler(Handler)} if needed.
* initialization. You can supply a custom Handler using
* If you do not supply a custom Handler - unless you set {@link Shell.Builder#setAutoHandler(boolean)}
* {@link Shell.Builder#setHandler(Handler)} if needed. If you do not supply
* to false - a Handler will be auto-created if the thread used for instantiation
* a custom Handler - unless you set
* of the object has a Looper.</p>
* {@link Shell.Builder#setAutoHandler(boolean)} to false - a Handler will
*
* be auto-created if the thread used for instantiation of the object has a
* <p>If no Handler was supplied and it was also not auto-created, all callbacks will
* Looper.
* be called from either the STDOUT or STDERR gobbler threads. These are important
* </p>
* threads that should be blocked as little as possible, as blocking them may in rare
* <p>
* cases pause the native process or even create a deadlock.</p>
* If no Handler was supplied and it was also not auto-created, all
*
* callbacks will be called from either the STDOUT or STDERR gobbler
* <p>The main thread must certainly have a Looper, thus if you call {@link Shell.Builder#open()}
* threads. These are important threads that should be blocked as little as
* from the main thread, a handler will (by default) be auto-created, and all the callbacks
* possible, as blocking them may in rare cases pause the native process or
* will be called on the main thread. While this is often convenient and easy to code with,
* even create a deadlock.
* you should be aware that if your callbacks are 'expensive' to execute, this may negatively
* </p>
* impact UI performance.</p>
* <p>
*
* The main thread must certainly have a Looper, thus if you call
* <p>Background threads usually do <em>not</em> have a Looper, so calling {@link Shell.Builder#open()}
* {@link Shell.Builder#open()} from the main thread, a handler will (by
* from such a background thread will (by default) result in all the callbacks being executed
* default) be auto-created, and all the callbacks will be called on the
* in one of the gobbler threads. You will have to make sure the code you execute in these callbacks
* main thread. While this is often convenient and easy to code with, you
* is thread-safe.</p>
* should be aware that if your callbacks are 'expensive' to execute, this
* may negatively impact UI performance.
* </p>
* <p>
* Background threads usually do <em>not</em> have a Looper, so calling
* {@link Shell.Builder#open()} from such a background thread will (by
* default) result in all the callbacks being executed in one of the gobbler
* threads. You will have to make sure the code you execute in these
* callbacks is thread-safe.
* </p>
*/
*/
public
static
class
Interactive
{
public
static
class
Interactive
{
private
final
Handler
handler
;
private
final
Handler
handler
;
private
final
boolean
autoHandler
;
private
final
boolean
autoHandler
;
private
final
String
shell
;
private
final
String
shell
;
...
@@ -724,7 +979,7 @@ public class Shell {
...
@@ -724,7 +979,7 @@ public class Shell {
private
Process
process
=
null
;
private
Process
process
=
null
;
private
DataOutputStream
STDIN
=
null
;
private
DataOutputStream
STDIN
=
null
;
private
StreamGobbler
STDOUT
=
null
;
private
StreamGobbler
STDOUT
=
null
;
private
StreamGobbler
STDERR
=
null
;
private
StreamGobbler
STDERR
=
null
;
private
ScheduledThreadPoolExecutor
watchdog
=
null
;
private
ScheduledThreadPoolExecutor
watchdog
=
null
;
private
volatile
boolean
running
=
false
;
private
volatile
boolean
running
=
false
;
...
@@ -747,7 +1002,8 @@ public class Shell {
...
@@ -747,7 +1002,8 @@ public class Shell {
*
*
* @param builder Builder class to take values from
* @param builder Builder class to take values from
*/
*/
private
Interactive
(
final
Builder
builder
,
final
OnCommandResultListener
onCommandResultListener
)
{
private
Interactive
(
final
Builder
builder
,
final
OnCommandResultListener
onCommandResultListener
)
{
autoHandler
=
builder
.
autoHandler
;
autoHandler
=
builder
.
autoHandler
;
shell
=
builder
.
shell
;
shell
=
builder
.
shell
;
wantSTDERR
=
builder
.
wantSTDERR
;
wantSTDERR
=
builder
.
wantSTDERR
;
...
@@ -757,7 +1013,8 @@ public class Shell {
...
@@ -757,7 +1013,8 @@ public class Shell {
onSTDERRLineListener
=
builder
.
onSTDERRLineListener
;
onSTDERRLineListener
=
builder
.
onSTDERRLineListener
;
watchdogTimeout
=
builder
.
watchdogTimeout
;
watchdogTimeout
=
builder
.
watchdogTimeout
;
// If a looper is available, we offload the callbacks from the gobbling threads
// If a looper is available, we offload the callbacks from the
// gobbling threads
// to whichever thread created us. Would normally do this in open(),
// to whichever thread created us. Would normally do this in open(),
// but then we could not declare handler as final
// but then we could not declare handler as final
if
((
Looper
.
myLooper
()
!=
null
)
&&
(
builder
.
handler
==
null
)
&&
autoHandler
)
{
if
((
Looper
.
myLooper
()
!=
null
)
&&
(
builder
.
handler
==
null
)
&&
autoHandler
)
{
...
@@ -770,11 +1027,13 @@ public class Shell {
...
@@ -770,11 +1027,13 @@ public class Shell {
if
(
onCommandResultListener
==
null
)
{
if
(
onCommandResultListener
==
null
)
{
return
;
return
;
}
else
if
(
ret
==
false
)
{
}
else
if
(
ret
==
false
)
{
onCommandResultListener
.
onCommandResult
(
0
,
OnCommandResultListener
.
SHELL_EXEC_FAILED
,
null
);
onCommandResultListener
.
onCommandResult
(
0
,
OnCommandResultListener
.
SHELL_EXEC_FAILED
,
null
);
return
;
return
;
}
}
// Allow up to 60 seconds for SuperSU/Superuser dialog, then enable the user-specified
// Allow up to 60 seconds for SuperSU/Superuser dialog, then enable
// the user-specified
// timeout for all subsequent operations
// timeout for all subsequent operations
watchdogTimeout
=
60
;
watchdogTimeout
=
60
;
addCommand
(
Shell
.
availableTestCommands
,
0
,
new
OnCommandResultListener
()
{
addCommand
(
Shell
.
availableTestCommands
,
0
,
new
OnCommandResultListener
()
{
...
@@ -791,11 +1050,11 @@ public class Shell {
...
@@ -791,11 +1050,11 @@ public class Shell {
}
}
@Override
@Override
protected
void
finalize
()
throws
Throwable
{
protected
void
finalize
()
throws
Throwable
{
if
(!
closed
&&
Debug
.
getSanityChecksEnabledEffective
())
{
if
(!
closed
&&
Debug
.
getSanityChecksEnabledEffective
())
{
// waste of resources
// waste of resources
Debug
.
log
(
ShellNotClosedException
.
EXCEPTION_NOT_CLOSED
);
Debug
.
log
(
ShellNotClosedException
.
EXCEPTION_NOT_CLOSED
);
throw
new
ShellNotClosedException
();
throw
new
ShellNotClosedException
();
}
}
super
.
finalize
();
super
.
finalize
();
}
}
...
@@ -805,73 +1064,170 @@ public class Shell {
...
@@ -805,73 +1064,170 @@ public class Shell {
*
*
* @param command Command to execute
* @param command Command to execute
*/
*/
public
void
addCommand
(
String
command
)
{
addCommand
(
command
,
0
,
null
);
}
public
void
addCommand
(
String
command
)
{
addCommand
(
command
,
0
,
(
OnCommandResultListener
)
null
);
}
/**
/**
* <p>Add a command to execute, with a callback to be called on completion</p>
* <p>
*
* Add a command to execute, with a callback to be called on completion
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param command Command to execute
* @param command Command to execute
* @param code User-defined value passed back to the callback
* @param code User-defined value passed back to the callback
* @param onCommandResultListener Callback to be called on completion
* @param onCommandResultListener Callback to be called on completion
*/
*/
public
void
addCommand
(
String
command
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
addCommand
(
new
String
[]
{
command
},
code
,
onCommandResultListener
);
}
public
void
addCommand
(
String
command
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
addCommand
(
new
String
[]
{
command
},
code
,
onCommandResultListener
);
}
/**
* <p>
* Add a command to execute, with a callback. This callback gobbles the
* output line by line without buffering it and also returns the result
* code on completion.
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
* @param command Command to execute
* @param code User-defined value passed back to the callback
* @param onCommandLineListener Callback
*/
public
void
addCommand
(
String
command
,
int
code
,
OnCommandLineListener
onCommandLineListener
)
{
addCommand
(
new
String
[]
{
command
},
code
,
onCommandLineListener
);
}
/**
/**
* Add commands to execute
* Add commands to execute
*
*
* @param commands Commands to execute
* @param commands Commands to execute
*/
*/
public
void
addCommand
(
List
<
String
>
commands
)
{
addCommand
(
commands
,
0
,
null
);
}
public
void
addCommand
(
List
<
String
>
commands
)
{
addCommand
(
commands
,
0
,
(
OnCommandResultListener
)
null
);
}
/**
/**
* <p>Add commands to execute, with a callback to be called on completion (of all commands)</p>
* <p>
* Add commands to execute, with a callback to be called on completion
* (of all commands)
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* @param commands Commands to execute
* @param code User-defined value passed back to the callback
* @param onCommandResultListener Callback to be called on completion
* (of all commands)
*/
public
void
addCommand
(
List
<
String
>
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
addCommand
(
commands
.
toArray
(
new
String
[
commands
.
size
()]),
code
,
onCommandResultListener
);
}
/**
* <p>
* Add commands to execute, with a callback. This callback gobbles the
* output line by line without buffering it and also returns the result
* code on completion.
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param commands Commands to execute
* @param commands Commands to execute
* @param code User-defined value passed back to the callback
* @param code User-defined value passed back to the callback
* @param onCommand
ResultListener Callback to be called on completion (of all commands)
* @param onCommand
LineListener Callback
*/
*/
public
void
addCommand
(
List
<
String
>
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
addCommand
(
commands
.
toArray
(
new
String
[
commands
.
size
()]),
code
,
onCommandResultListener
);
}
public
void
addCommand
(
List
<
String
>
commands
,
int
code
,
OnCommandLineListener
onCommandLineListener
)
{
addCommand
(
commands
.
toArray
(
new
String
[
commands
.
size
()]),
code
,
onCommandLineListener
);
}
/**
/**
* Add commands to execute
* Add commands to execute
*
*
* @param commands Commands to execute
* @param commands Commands to execute
*/
*/
public
void
addCommand
(
String
[]
commands
)
{
addCommand
(
commands
,
0
,
null
);
}
public
void
addCommand
(
String
[]
commands
)
{
addCommand
(
commands
,
0
,
(
OnCommandResultListener
)
null
);
}
/**
/**
* <p>Add commands to execute, with a callback to be called on completion (of all commands)</p>
* <p>
* Add commands to execute, with a callback to be called on completion
* (of all commands)
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* <p>The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details</p>
* @param commands Commands to execute
* @param code User-defined value passed back to the callback
* @param onCommandResultListener Callback to be called on completion
* (of all commands)
*/
public
synchronized
void
addCommand
(
String
[]
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
this
.
commands
.
add
(
new
Command
(
commands
,
code
,
onCommandResultListener
,
null
));
runNextCommand
();
}
/**
* <p>
* Add commands to execute, with a callback. This callback gobbles the
* output line by line without buffering it and also returns the result
* code on completion.
* </p>
* <p>
* The thread on which the callback executes is dependent on various
* factors, see {@link Shell.Interactive} for further details
* </p>
*
*
* @param commands Commands to execute
* @param commands Commands to execute
* @param code User-defined value passed back to the callback
* @param code User-defined value passed back to the callback
* @param onCommand
ResultListener Callback to be called on completion (of all commands)
* @param onCommand
LineListener Callback
*/
*/
public
synchronized
void
addCommand
(
String
[]
commands
,
int
code
,
OnCommandResultListener
onCommandResultListener
)
{
public
synchronized
void
addCommand
(
String
[]
commands
,
int
code
,
this
.
commands
.
add
(
new
Command
(
commands
,
code
,
onCommandResultListener
));
OnCommandLineListener
onCommandLineListener
)
{
this
.
commands
.
add
(
new
Command
(
commands
,
code
,
null
,
onCommandLineListener
));
runNextCommand
();
runNextCommand
();
}
}
/**
/**
* Run the next command if any and if ready, signals idle state if no commands left
* Run the next command if any and if ready, signals idle state if no
* commands left
*/
*/
private
void
runNextCommand
()
{
private
void
runNextCommand
()
{
runNextCommand
(
true
);
runNextCommand
(
true
);
}
}
/**
/**
* Called from a ScheduledThreadPoolExecutor timer thread every second when there is an outstanding command
* Called from a ScheduledThreadPoolExecutor timer thread every second
* when there is an outstanding command
*/
*/
private
synchronized
void
handleWatchdog
()
{
private
synchronized
void
handleWatchdog
()
{
final
int
exitCode
;
final
int
exitCode
;
if
(
watchdog
==
null
)
return
;
if
(
watchdog
==
null
)
if
(
watchdogTimeout
==
0
)
return
;
return
;
if
(
watchdogTimeout
==
0
)
return
;
if
(!
isRunning
())
{
if
(!
isRunning
())
{
exitCode
=
OnCommandResultListener
.
SHELL_DIED
;
exitCode
=
OnCommandResultListener
.
SHELL_DIED
;
...
@@ -927,13 +1283,14 @@ public class Shell {
...
@@ -927,13 +1283,14 @@ public class Shell {
/**
/**
* Run the next command if any and if ready
* Run the next command if any and if ready
*
*
* @param notifyIdle signals idle state if no commands left ?
* @param notifyIdle signals idle state if no commands left ?
*/
*/
private
void
runNextCommand
(
boolean
notifyIdle
)
{
private
void
runNextCommand
(
boolean
notifyIdle
)
{
// must always be called from a synchronized method
// must always be called from a synchronized method
boolean
running
=
isRunning
();
boolean
running
=
isRunning
();
if
(!
running
)
idle
=
true
;
if
(!
running
)
idle
=
true
;
if
(
running
&&
idle
&&
(
commands
.
size
()
>
0
))
{
if
(
running
&&
idle
&&
(
commands
.
size
()
>
0
))
{
Command
command
=
commands
.
get
(
0
);
Command
command
=
commands
.
get
(
0
);
...
@@ -942,22 +1299,25 @@ public class Shell {
...
@@ -942,22 +1299,25 @@ public class Shell {
buffer
=
null
;
buffer
=
null
;
lastExitCode
=
0
;
lastExitCode
=
0
;
lastMarkerSTDOUT
=
null
;
lastMarkerSTDOUT
=
null
;
lastMarkerSTDERR
=
null
;
lastMarkerSTDERR
=
null
;
if
(
command
.
commands
.
length
>
0
)
{
if
(
command
.
commands
.
length
>
0
)
{
try
{
try
{
if
(
command
.
onCommandResultListener
!=
null
)
{
if
(
command
.
onCommandResultListener
!=
null
)
{
// no reason to store the output if we don't have an OnCommandResultListener
// no reason to store the output if we don't have an
// user should catch the output with an OnLineListener in this case
// OnCommandResultListener
buffer
=
Collections
.
synchronizedList
(
new
ArrayList
<
String
>());
// user should catch the output with an
// OnLineListener in this case
buffer
=
Collections
.
synchronizedList
(
new
ArrayList
<
String
>());
}
}
idle
=
false
;
idle
=
false
;
this
.
command
=
command
;
this
.
command
=
command
;
startWatchdog
();
startWatchdog
();
for
(
String
write
:
command
.
commands
)
{
for
(
String
write
:
command
.
commands
)
{
Debug
.
logCommand
(
String
.
format
(
"[%s+] %s"
,
shell
.
toUpperCase
(
Locale
.
ENGLISH
),
write
));
Debug
.
logCommand
(
String
.
format
(
"[%s+] %s"
,
STDIN
.
write
((
write
+
"\n"
).
getBytes
(
"UTF-8"
));
shell
.
toUpperCase
(
Locale
.
ENGLISH
),
write
));
STDIN
.
write
((
write
+
"\n"
).
getBytes
(
"UTF-8"
));
}
}
STDIN
.
write
((
"echo "
+
command
.
marker
+
" $?\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
write
((
"echo "
+
command
.
marker
+
" $?\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
write
((
"echo "
+
command
.
marker
+
" >&2\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
write
((
"echo "
+
command
.
marker
+
" >&2\n"
).
getBytes
(
"UTF-8"
));
...
@@ -966,7 +1326,7 @@ public class Shell {
...
@@ -966,7 +1326,7 @@ public class Shell {
}
}
}
else
{
}
else
{
runNextCommand
(
false
);
runNextCommand
(
false
);
}
}
}
else
if
(!
running
)
{
}
else
if
(!
running
)
{
// our shell died for unknown reasons - abort all submissions
// our shell died for unknown reasons - abort all submissions
while
(
commands
.
size
()
>
0
)
{
while
(
commands
.
size
()
>
0
)
{
...
@@ -975,7 +1335,7 @@ public class Shell {
...
@@ -975,7 +1335,7 @@ public class Shell {
}
}
if
(
idle
&&
notifyIdle
)
{
if
(
idle
&&
notifyIdle
)
{
synchronized
(
idleSync
)
{
synchronized
(
idleSync
)
{
idleSync
.
notifyAll
();
idleSync
.
notifyAll
();
}
}
}
}
...
@@ -985,17 +1345,15 @@ public class Shell {
...
@@ -985,17 +1345,15 @@ public class Shell {
* Processes a STDOUT/STDERR line containing an end/exitCode marker
* Processes a STDOUT/STDERR line containing an end/exitCode marker
*/
*/
private
synchronized
void
processMarker
()
{
private
synchronized
void
processMarker
()
{
if
(
command
.
marker
.
equals
(
lastMarkerSTDOUT
)
&&
(
command
.
marker
.
equals
(
lastMarkerSTDERR
)))
{
if
(
command
.
marker
.
equals
(
lastMarkerSTDOUT
)
if
(
buffer
!=
null
)
{
&&
(
command
.
marker
.
equals
(
lastMarkerSTDERR
)))
{
postCallback
(
command
,
lastExitCode
,
buffer
);
postCallback
(
command
,
lastExitCode
,
buffer
);
}
stopWatchdog
();
stopWatchdog
();
command
=
null
;
command
=
null
;
buffer
=
null
;
buffer
=
null
;
idle
=
true
;
idle
=
true
;
runNextCommand
();
runNextCommand
();
}
}
}
}
/**
/**
...
@@ -1011,7 +1369,7 @@ public class Shell {
...
@@ -1011,7 +1369,7 @@ public class Shell {
final
OnLineListener
fListener
=
listener
;
final
OnLineListener
fListener
=
listener
;
startCallback
();
startCallback
();
handler
.
post
(
new
Runnable
()
{
handler
.
post
(
new
Runnable
()
{
@Override
@Override
public
void
run
()
{
public
void
run
()
{
try
{
try
{
...
@@ -1024,7 +1382,7 @@ public class Shell {
...
@@ -1024,7 +1382,7 @@ public class Shell {
}
else
{
}
else
{
listener
.
onLine
(
line
);
listener
.
onLine
(
line
);
}
}
}
}
}
}
/**
/**
...
@@ -1033,7 +1391,7 @@ public class Shell {
...
@@ -1033,7 +1391,7 @@ public class Shell {
* @param line Line to add
* @param line Line to add
*/
*/
private
synchronized
void
addBuffer
(
String
line
)
{
private
synchronized
void
addBuffer
(
String
line
)
{
if
(
buffer
!=
null
)
{
if
(
buffer
!=
null
)
{
buffer
.
add
(
line
);
buffer
.
add
(
line
);
}
}
}
}
...
@@ -1050,12 +1408,17 @@ public class Shell {
...
@@ -1050,12 +1408,17 @@ public class Shell {
/**
/**
* Schedule a callback to run on the appropriate thread
* Schedule a callback to run on the appropriate thread
*/
*/
private
void
postCallback
(
final
Command
fCommand
,
final
int
fExitCode
,
final
List
<
String
>
fOutput
)
{
private
void
postCallback
(
final
Command
fCommand
,
final
int
fExitCode
,
if
(
fCommand
.
onCommandResultListener
==
null
)
{
final
List
<
String
>
fOutput
)
{
if
(
fCommand
.
onCommandResultListener
==
null
&&
fCommand
.
onCommandLineListener
==
null
)
{
return
;
return
;
}
}
if
(
handler
==
null
)
{
if
(
handler
==
null
)
{
fCommand
.
onCommandResultListener
.
onCommandResult
(
fCommand
.
code
,
fExitCode
,
fOutput
);
if
((
fCommand
.
onCommandResultListener
!=
null
)
&&
(
fOutput
!=
null
))
fCommand
.
onCommandResultListener
.
onCommandResult
(
fCommand
.
code
,
fExitCode
,
fOutput
);
if
(
fCommand
.
onCommandLineListener
!=
null
)
fCommand
.
onCommandLineListener
.
onCommandResult
(
fCommand
.
code
,
fExitCode
);
return
;
return
;
}
}
startCallback
();
startCallback
();
...
@@ -1063,7 +1426,12 @@ public class Shell {
...
@@ -1063,7 +1426,12 @@ public class Shell {
@Override
@Override
public
void
run
()
{
public
void
run
()
{
try
{
try
{
fCommand
.
onCommandResultListener
.
onCommandResult
(
fCommand
.
code
,
fExitCode
,
fOutput
);
if
((
fCommand
.
onCommandResultListener
!=
null
)
&&
(
fOutput
!=
null
))
fCommand
.
onCommandResultListener
.
onCommandResult
(
fCommand
.
code
,
fExitCode
,
fOutput
);
if
(
fCommand
.
onCommandLineListener
!=
null
)
fCommand
.
onCommandLineListener
.
onCommandResult
(
fCommand
.
code
,
fExitCode
);
}
finally
{
}
finally
{
endCallback
();
endCallback
();
}
}
...
@@ -1072,7 +1440,8 @@ public class Shell {
...
@@ -1072,7 +1440,8 @@ public class Shell {
}
}
/**
/**
* Decrease callback counter, signals callback complete state when dropped to 0
* Decrease callback counter, signals callback complete state when
* dropped to 0
*/
*/
private
void
endCallback
()
{
private
void
endCallback
()
{
synchronized
(
callbackSync
)
{
synchronized
(
callbackSync
)
{
...
@@ -1084,8 +1453,8 @@ public class Shell {
...
@@ -1084,8 +1453,8 @@ public class Shell {
}
}
/**
/**
* Internal call that launches the shell, starts gobbling, and starts
executing commands.
* Internal call that launches the shell, starts gobbling, and starts
* See {@link Shell.Interactive}
*
executing commands.
See {@link Shell.Interactive}
*
*
* @return Opened successfully ?
* @return Opened successfully ?
*/
*/
...
@@ -1093,7 +1462,8 @@ public class Shell {
...
@@ -1093,7 +1462,8 @@ public class Shell {
Debug
.
log
(
String
.
format
(
"[%s%%] START"
,
shell
.
toUpperCase
(
Locale
.
ENGLISH
)));
Debug
.
log
(
String
.
format
(
"[%s%%] START"
,
shell
.
toUpperCase
(
Locale
.
ENGLISH
)));
try
{
try
{
// setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers
// setup our process, retrieve STDIN stream, and STDOUT/STDERR
// gobblers
if
(
environment
.
size
()
==
0
)
{
if
(
environment
.
size
()
==
0
)
{
process
=
Runtime
.
getRuntime
().
exec
(
shell
);
process
=
Runtime
.
getRuntime
().
exec
(
shell
);
}
else
{
}
else
{
...
@@ -1104,50 +1474,55 @@ public class Shell {
...
@@ -1104,50 +1474,55 @@ public class Shell {
String
[]
env
=
new
String
[
newEnvironment
.
size
()];
String
[]
env
=
new
String
[
newEnvironment
.
size
()];
for
(
Map
.
Entry
<
String
,
String
>
entry
:
newEnvironment
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
String
>
entry
:
newEnvironment
.
entrySet
())
{
env
[
i
]
=
entry
.
getKey
()
+
"="
+
entry
.
getValue
();
env
[
i
]
=
entry
.
getKey
()
+
"="
+
entry
.
getValue
();
i
++;
i
++;
}
}
process
=
Runtime
.
getRuntime
().
exec
(
shell
,
env
);
process
=
Runtime
.
getRuntime
().
exec
(
shell
,
env
);
}
}
STDIN
=
new
DataOutputStream
(
process
.
getOutputStream
());
STDIN
=
new
DataOutputStream
(
process
.
getOutputStream
());
STDOUT
=
new
StreamGobbler
(
shell
.
toUpperCase
(
Locale
.
ENGLISH
)
+
"-"
,
process
.
getInputStream
(),
new
OnLineListener
()
{
STDOUT
=
new
StreamGobbler
(
shell
.
toUpperCase
(
Locale
.
ENGLISH
)
+
"-"
,
@Override
process
.
getInputStream
(),
new
OnLineListener
()
{
public
void
onLine
(
String
line
)
{
@Override
synchronized
(
Interactive
.
this
)
{
public
void
onLine
(
String
line
)
{
if
(
command
==
null
)
{
synchronized
(
Interactive
.
this
)
{
return
;
if
(
command
==
null
)
{
}
return
;
if
(
line
.
startsWith
(
command
.
marker
))
{
}
try
{
if
(
line
.
startsWith
(
command
.
marker
))
{
lastExitCode
=
Integer
.
valueOf
(
line
.
substring
(
command
.
marker
.
length
()
+
1
),
10
);
try
{
}
catch
(
Exception
e
)
{
lastExitCode
=
Integer
.
valueOf
(
line
.
substring
(
command
.
marker
.
length
()
+
1
),
10
);
}
catch
(
Exception
e
)
{
}
lastMarkerSTDOUT
=
command
.
marker
;
processMarker
();
}
else
{
addBuffer
(
line
);
processLine
(
line
,
onSTDOUTLineListener
);
processLine
(
line
,
command
.
onCommandLineListener
);
}
}
}
lastMarkerSTDOUT
=
command
.
marker
;
processMarker
();
}
else
{
addBuffer
(
line
);
processLine
(
line
,
onSTDOUTLineListener
);
}
}
}
});
STDERR
=
new
StreamGobbler
(
shell
.
toUpperCase
(
Locale
.
ENGLISH
)
+
"*"
,
process
.
getErrorStream
(),
new
OnLineListener
()
{
@Override
public
void
onLine
(
String
line
)
{
synchronized
(
Interactive
.
this
)
{
if
(
command
==
null
)
{
return
;
}
}
if
(
line
.
startsWith
(
command
.
marker
))
{
});
lastMarkerSTDERR
=
command
.
marker
;
STDERR
=
new
StreamGobbler
(
shell
.
toUpperCase
(
Locale
.
ENGLISH
)
+
"*"
,
processMarker
();
process
.
getErrorStream
(),
new
OnLineListener
()
{
}
else
{
@Override
if
(
wantSTDERR
)
addBuffer
(
line
);
public
void
onLine
(
String
line
)
{
processLine
(
line
,
onSTDERRLineListener
);
synchronized
(
Interactive
.
this
)
{
if
(
command
==
null
)
{
return
;
}
if
(
line
.
startsWith
(
command
.
marker
))
{
lastMarkerSTDERR
=
command
.
marker
;
processMarker
();
}
else
{
if
(
wantSTDERR
)
addBuffer
(
line
);
processLine
(
line
,
onSTDERRLineListener
);
}
}
}
}
}
});
}
});
// start gobbling and write our commands to the shell
// start gobbling and write our commands to the shell
STDOUT
.
start
();
STDOUT
.
start
();
...
@@ -1166,43 +1541,49 @@ public class Shell {
...
@@ -1166,43 +1541,49 @@ public class Shell {
}
}
/**
/**
* Close shell and clean up all resources. Call this when you are done with the shell.
* Close shell and clean up all resources. Call this when you are done
* If the shell is not idle (all commands completed) you should not call this method
* with the shell. If the shell is not idle (all commands completed) you
* from the main UI thread because it may block for a long time. This method will
* should not call this method from the main UI thread because it may
* intentionally crash your app (if in debug mode) if you try to do this anyway.
* block for a long time. This method will intentionally crash your app
* (if in debug mode) if you try to do this anyway.
*/
*/
public
void
close
()
{
public
void
close
()
{
boolean
_idle
=
isIdle
();
// idle must be checked synchronized
boolean
_idle
=
isIdle
();
// idle must be checked synchronized
synchronized
(
this
)
{
synchronized
(
this
)
{
if
(!
running
)
return
;
if
(!
running
)
return
;
running
=
false
;
running
=
false
;
closed
=
true
;
closed
=
true
;
}
}
// This method should not be called from the main thread unless the shell is idle
// This method should not be called from the main thread unless the
// and can be cleaned up with (minimal) waiting. Only throw in debug mode.
// shell is idle and can be cleaned up with (minimal) waiting. Only
// throw in debug mode.
if
(!
_idle
&&
Debug
.
getSanityChecksEnabledEffective
()
&&
Debug
.
onMainThread
())
{
if
(!
_idle
&&
Debug
.
getSanityChecksEnabledEffective
()
&&
Debug
.
onMainThread
())
{
Debug
.
log
(
ShellOnMainThreadException
.
EXCEPTION_NOT_IDLE
);
Debug
.
log
(
ShellOnMainThreadException
.
EXCEPTION_NOT_IDLE
);
throw
new
ShellOnMainThreadException
(
ShellOnMainThreadException
.
EXCEPTION_NOT_IDLE
);
throw
new
ShellOnMainThreadException
(
ShellOnMainThreadException
.
EXCEPTION_NOT_IDLE
);
}
}
if
(!
_idle
)
waitForIdle
();
if
(!
_idle
)
waitForIdle
();
try
{
try
{
STDIN
.
write
((
"exit\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
write
((
"exit\n"
).
getBytes
(
"UTF-8"
));
STDIN
.
flush
();
STDIN
.
flush
();
// wait for our process to finish, while we gobble away in the background
// wait for our process to finish, while we gobble away in the
// background
process
.
waitFor
();
process
.
waitFor
();
// make sure our threads are done gobbling, our streams are closed, and the process is
// make sure our threads are done gobbling, our streams are
// destroyed - while the latter two shouldn't be needed in theory, and may even produce
// closed, and the process is destroyed - while the latter two
// warnings, in "normal" Java they are required for guaranteed cleanup of resources, so
// shouldn't be needed in theory, and may even produce warnings,
// lets be safe and do this on Android as well
// in "normal" Java they are required for guaranteed cleanup of
try
{
// resources, so lets be safe and do this on Android as well
try
{
STDIN
.
close
();
STDIN
.
close
();
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
}
}
STDOUT
.
join
();
STDOUT
.
join
();
STDERR
.
join
();
STDERR
.
join
();
...
@@ -1218,21 +1599,21 @@ public class Shell {
...
@@ -1218,21 +1599,21 @@ public class Shell {
}
}
/**
/**
* Try to clean up as much as possible from a shell that's gotten itself
wedged.
* Try to clean up as much as possible from a shell that's gotten itself
*
Hopefully the StreamGobblers will croak on their own when the other side of
*
wedged. Hopefully the StreamGobblers will croak on their own when the
* the pipe is closed.
*
other side of
the pipe is closed.
*/
*/
public
synchronized
void
kill
()
{
public
synchronized
void
kill
()
{
running
=
false
;
running
=
false
;
closed
=
true
;
closed
=
true
;
try
{
try
{
STDIN
.
close
();
STDIN
.
close
();
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
}
}
try
{
try
{
process
.
destroy
();
process
.
destroy
();
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
}
}
}
}
...
@@ -1242,12 +1623,15 @@ public class Shell {
...
@@ -1242,12 +1623,15 @@ public class Shell {
* @return Shell running ?
* @return Shell running ?
*/
*/
public
boolean
isRunning
()
{
public
boolean
isRunning
()
{
if
(
process
==
null
)
{
return
false
;
}
try
{
try
{
// if this throws, we're still running
// if this throws, we're still running
process
.
exitValue
();
process
.
exitValue
();
return
false
;
return
false
;
}
catch
(
IllegalThreadStateException
e
)
{
}
catch
(
IllegalThreadStateException
e
)
{
}
}
return
true
;
return
true
;
}
}
...
@@ -1259,31 +1643,42 @@ public class Shell {
...
@@ -1259,31 +1643,42 @@ public class Shell {
public
synchronized
boolean
isIdle
()
{
public
synchronized
boolean
isIdle
()
{
if
(!
isRunning
())
{
if
(!
isRunning
())
{
idle
=
true
;
idle
=
true
;
synchronized
(
idleSync
)
{
synchronized
(
idleSync
)
{
idleSync
.
notifyAll
();
idleSync
.
notifyAll
();
}
}
}
}
return
idle
;
return
idle
;
}
}
/**
/**
* <p>Wait for idle state. As this is a blocking call, you should not call it from the main UI thread.
* <p>
* If you do so and debug mode is enabled, this method will intentionally crash your app.</p>
* Wait for idle state. As this is a blocking call, you should not call
*
* it from the main UI thread. If you do so and debug mode is enabled,
* <p>If not interrupted, this method will not return until all commands have finished executing.
* this method will intentionally crash your app.
* Note that this does not necessarily mean that all the callbacks have fired yet.</p>
* </p>
*
* <p>
* <p>If no Handler is used, all callbacks will have been executed when this method returns. If
* If not interrupted, this method will not return until all commands
* a Handler is used, and this method is called from a different thread than associated with the
* have finished executing. Note that this does not necessarily mean
* Handler's Looper, all callbacks will have been executed when this method returns as well.
* that all the callbacks have fired yet.
* If however a Handler is used but this method is called from the same thread as associated
* </p>
* with the Handler's Looper, there is no way to know.</p>
* <p>
*
* If no Handler is used, all callbacks will have been executed when
* <p>In practice this means that in most simple cases all callbacks will have completed when this
* this method returns. If a Handler is used, and this method is called
* method returns, but if you actually depend on this behavior, you should make certain this is
* from a different thread than associated with the Handler's Looper,
* indeed the case.</p>
* all callbacks will have been executed when this method returns as
*
* well. If however a Handler is used but this method is called from the
* <p>See {@link Shell.Interactive} for further details on threading and handlers</p>
* same thread as associated with the Handler's Looper, there is no way
* to know.
* </p>
* <p>
* In practice this means that in most simple cases all callbacks will
* have completed when this method returns, but if you actually depend
* on this behavior, you should make certain this is indeed the case.
* </p>
* <p>
* See {@link Shell.Interactive} for further details on threading and
* handlers
* </p>
*
*
* @return True if wait complete, false if wait interrupted
* @return True if wait complete, false if wait interrupted
*/
*/
...
@@ -1297,28 +1692,27 @@ public class Shell {
...
@@ -1297,28 +1692,27 @@ public class Shell {
synchronized
(
idleSync
)
{
synchronized
(
idleSync
)
{
while
(!
idle
)
{
while
(!
idle
)
{
try
{
try
{
idleSync
.
wait
();
idleSync
.
wait
();
}
catch
(
InterruptedException
e
)
{
}
catch
(
InterruptedException
e
)
{
return
false
;
return
false
;
}
}
}
}
}
}
if
(
if
((
handler
!=
null
)
&&
(
handler
!=
null
)
&&
(
handler
.
getLooper
()
!=
null
)
&&
(
handler
.
getLooper
()
!=
null
)
&&
(
handler
.
getLooper
()
!=
Looper
.
myLooper
()))
{
(
handler
.
getLooper
()
!=
Looper
.
myLooper
())
// If the callbacks are posted to a different thread than
)
{
// this one, we can wait until all callbacks have called
// If the callbacks are posted to a different thread than this one, we can wait until
// before returning. If we don't use a Handler at all, the
// all callbacks have called before returning. If we don't use a Handler at all,
// callbacks are already called before we get here. If we do
// the callbacks are already called before we get here. If we do use a Handler but
// use a Handler but we use the same Looper, waiting here
// we use the same Looper, waiting here would actually block the callbacks from being
// would actually block the callbacks from being called
// called
synchronized
(
callbackSync
)
{
synchronized
(
callbackSync
)
{
while
(
callbacks
>
0
)
{
while
(
callbacks
>
0
)
{
try
{
try
{
callbackSync
.
wait
();
callbackSync
.
wait
();
}
catch
(
InterruptedException
e
)
{
}
catch
(
InterruptedException
e
)
{
return
false
;
return
false
;
}
}
...
@@ -1336,7 +1730,7 @@ public class Shell {
...
@@ -1336,7 +1730,7 @@ public class Shell {
* @return Handler used ?
* @return Handler used ?
*/
*/
public
boolean
hasHandler
()
{
public
boolean
hasHandler
()
{
return
(
handler
!=
null
);
return
(
handler
!=
null
);
}
}
}
}
}
}
src/main/java/eu/chainfire/libsuperuser/ShellNotClosedException.java
View file @
27ba7d64
/*
/*
* Copyright (C) 2012-201
3
Jorrit "Chainfire" Jongma
* Copyright (C) 2012-201
4
Jorrit "Chainfire" Jongma
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
...
src/main/java/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
View file @
27ba7d64
/*
/*
* Copyright (C) 2012-201
3
Jorrit "Chainfire" Jongma
* Copyright (C) 2012-201
4
Jorrit "Chainfire" Jongma
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
...
src/main/java/eu/chainfire/libsuperuser/StreamGobbler.java
View file @
27ba7d64
/*
/*
* Copyright (C) 2012-201
3
Jorrit "Chainfire" Jongma
* Copyright (C) 2012-201
4
Jorrit "Chainfire" Jongma
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
...
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
View file @
27ba7d64
...
@@ -341,6 +341,7 @@ class Shadowsocks
...
@@ -341,6 +341,7 @@ class Shadowsocks
ab
.
clear
()
ab
.
clear
()
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
)
ab
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
)
ab
.
append
(
"rm /data/data/com.github.shadowsocks/redsocks.conf"
)
ab
.
append
(
"rm /data/data/com.github.shadowsocks/redsocks.conf"
)
ab
.
append
(
"rm /data/data/com.github.shadowsocks/redsocks.pid"
)
ab
.
append
(
Utils
.
getIptables
+
" -t nat -F OUTPUT"
)
ab
.
append
(
Utils
.
getIptables
+
" -t nat -F OUTPUT"
)
Console
.
runRootCommand
(
ab
.
toArray
)
Console
.
runRootCommand
(
ab
.
toArray
)
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
View file @
27ba7d64
...
@@ -137,18 +137,27 @@ class ShadowsocksNatService extends Service with BaseService {
...
@@ -137,18 +137,27 @@ class ShadowsocksNatService extends Service with BaseService {
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
.
mkString
(
" "
))
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
.
mkString
(
" "
))
Core
.
sstunnel
(
cmd
.
toArray
)
Core
.
sstunnel
(
cmd
.
toArray
)
}
else
{
}
else
{
val
conf
=
{
val
cmdBuf
=
new
ArrayBuffer
[
String
]
ConfigUtils
.
PDNSD
.
format
(
"127.0.0.1"
)
cmdBuf
+=
(
"ss-tunnel"
}
,
"-b"
,
"127.0.0.1"
,
"-l"
,
"8163"
,
"-L"
,
"8.8.8.8:53"
,
"-s"
,
config
.
proxy
,
"-p"
,
config
.
remotePort
.
toString
,
"-k"
,
config
.
sitekey
,
"-m"
,
config
.
encMethod
,
"-f"
,
Path
.
BASE
+
"ss-tunnel.pid"
)
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmdBuf
.
mkString
(
" "
))
Core
.
sstunnel
(
cmdBuf
.
toArray
)
val
conf
=
ConfigUtils
.
PDNSD_BYPASS
.
format
(
"127.0.0.1"
,
getString
(
R
.
string
.
exclude
),
8163
)
ConfigUtils
.
printToFile
(
new
File
(
Path
.
BASE
+
"pdnsd.conf"
))(
p
=>
{
ConfigUtils
.
printToFile
(
new
File
(
Path
.
BASE
+
"pdnsd.conf"
))(
p
=>
{
p
.
println
(
conf
)
p
.
println
(
conf
)
})
})
val
cmd
=
Path
.
BASE
+
"pdnsd -c "
+
Path
.
BASE
+
"pdnsd.conf"
val
args
=
"pdnsd -c "
+
Path
.
BASE
+
"pdnsd.conf"
val
cmd
=
Path
.
BASE
+
args
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
)
if
(
BuildConfig
.
DEBUG
)
Log
.
d
(
TAG
,
cmd
)
Co
nsole
.
runRootCommand
(
cmd
)
Co
re
.
pdnsd
(
cmd
.
split
(
" "
)
)
}
}
}
}
...
@@ -172,7 +181,7 @@ class ShadowsocksNatService extends Service with BaseService {
...
@@ -172,7 +181,7 @@ class ShadowsocksNatService extends Service with BaseService {
p
.
println
(
conf
)
p
.
println
(
conf
)
})
})
val
cmd
=
Path
.
BASE
+
args
val
cmd
=
Path
.
BASE
+
args
Console
.
run
Root
Command
(
cmd
)
Console
.
runCommand
(
cmd
)
}
}
/** Called when the activity is first created. */
/** Called when the activity is first created. */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment