Root access in file explorer - android

I'm writing a file explorer which will be capable of modifying system files with root access, but I came across some problems.
What I'm doing now is to grant my app root access, but executing "su" doesn't work.
If I set permissions to the folder in adb shell, the app works fine but I think root browsing doesn't rely on chmods.
Can anyone tell me is there a proper way to make my app work as if it were with root privileges?

Running an android application process (its dalvik VM and native libraries) as root is extremely difficult to achieve, and inadvisable for a number of reasons including not just security but memory waste resulting from having to load private copies of system libraries instead of using the shared read-only copies available when you inherit an unprivileged process from zygote as in a normal application launch.
What the unofficial "su" hack on some rooted phones does is lets you launch a helper process which runs as root while your application process remains unprivileged. It does not change the userid of the application calling it - indeed, there really isn't by design any mechanism for doing that on unix-like operating systems.
Once you have a privileged helper process, you would then need to communicate with it via some means of interprocess communication such as its stdin/stdout or unix domain sockets to have it do file operations on your behalf. The shell present on the phone could probably even be used as the helper application - most of what a file manager needs to do can be implemented with the 'cat' command. Officially, none of this is a stable API, but then an application-accesable "su" hack isn't in official android anyway, so the whole project is deep in "unsupported" territory to begin with.

Root access creates a new Process, so, your app does not have root privileges.
The unique thing you can do with root privileges is execute commands, so, you have to know the commands of android, many of commands is based on Linux, like cp, ls and more.
Use this code for execute commands and get output:
/**
* Execute command and get entire output
* #return Command output
*/
private String executeCommand(String cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec("su");
InputStream in = process.getInputStream();
OutputStream out = process.getOutputStream();
out.write(cmd.getBytes());
out.flush();
out.close();
byte[] buffer = new byte[1024];
int length = buffer.read(buffer);
String result = new String(buffer, 0, length);
process.waitFor();
return result;
}
/**
* Execute command and an array separates each line
* #return Command output separated by lines in a String array
*/
private String[] executeCmdByLines(String cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec("su");
InputStream in = process.getInputStream();
OutputStream out = process.getOutputStream();
out.write(cmd.getBytes());
out.flush();
out.close();
byte[] buffer = new byte[1024];
int length = buffer.read(buffer);
String output = new String(buffer, 0, length);
String[] result = output.split("\n");
process.waitFor();
return result;
}
Usages for a file explorer:
Get list of files:
for (String value : executeCmdByLines("ls /data/data")) {
//Do your stuff here
}
Read text file:
String content = executeCommand("cat /data/someFile.txt");
//Do your stuff here with "content" string
Copy file (cp command not working on some devices):
executeCommand("cp /source/of/file /destination/file");
Delete file (rm command not working on some devices):
executeCommand("rm /path/to/file");

Related

How to get current Thermal values in Android Programatically?

I am trying to get thermal values of my device programatically. I am able to do it in adb but unable to do this programatically. How can I loop over each and every directory in the directory "thermal" to get the particular value. Some part of my code is as following :
Process p=Runtime.getRuntime().exec("cd sys/class/thermal/");
You cannot access /sys/class/thermal/ through an app because the user performing the action (something like u0_aXYZ) does not have enough permission to perform this action compared to a (root) adb shell.
You can do for instance:
adb shell
run-as your-app-package-name
and from now on you can navigate across the device file system and take a look at the folders your app can access.
If you want to proceed with that your only option is to have a rooted device and build an app with root permissions.
Now I found to do this programatically. I used for loop to find the values. Visit the detailed answer here.
for(int i=0;i<29;i++){
float temp;
Process process = Runtime.getRuntime().exec("cat sys/class/thermal/thermal_zone" + i + "/temp");
process.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine();
if (line != null) {
temp = Float.parseFloat(line);
}
reader.close();
process.destroy();
}

My application freezes after a superuser request

I am writing an application which involves getting information on all running processes (name/package name to begin with). I am doing this by invoking "ps" in my code. I requested superuser access from within the application before invoking the "ps" command. However, when I attempt to read the input stream, the application freezes and I do not get any output in the Logcat. Below is the code that I am using:
Process process = Runtime.getRuntime().exec("su");
DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());
outputStream.writeBytes("ps -t -x -P -p -c");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String topInfo = bufferedReader.readLine(); //Where it freezes
while(topInfo != null)
{
Log.i(appInfo, topInfo);
topInfo = bufferedReader.readLine();
}
outputStream.flush();
outputStream.close();
The code works as expected without superuser request, however the result only consists of my application and the "ps" process.
Is there something that I have missed, or something I need to research before I attempt to fix this?I have tried to search this issue on the Internet before asking here, without success. Any help is appreciated.
Thanks in advance.
P.S The application is being run on a rooted device running Android 7.1.1
I have found the cause of the problem. As it turns out, the BufferedReader was not ready to read, therefore it was not getting any input from the input stream. I confirmed this with the following code:
while(bufferedReader.ready())
{
String topInfo;
while ((topInfo = bufferedReader.readLine()) != null)
{
Log.i(appInfo, topInfo);
}
}
The fix to this problem is to wait for the BufferedReader to be ready to to read the process input stream. This can either be done by pausing the thread for some time, or including a loop that will loop through till the BufferedReader is ready to read. I opted for the latter, as shown below:
do
{
//Wait
} while(!bufferedReader.ready());
This gave me the desired results, which was a list of processes running on my device.

Get filepath from google drive in Lollipop (MediaStore.MediaColumns.DATA == null)

When the user clicks the "send file" button in google drive and selects my app. I want to get the filepath of that file and then allow the user to upload it to a different location.
I check these similar SO post for kitkat phones: Get real path from URI, Android KitKat new storage access framework
Android - Convert URI to file path on lollipop
However the solution to that no longer seems to work in Lollipop devices.
The problem seems to be that MediaStore.MediaColumns.DATA returns null when running a query on the ContentResolver.
https://code.google.com/p/android/issues/detail?id=63651
You should use ContentResolver.openFileDescriptor() instead of trying to get a raw filesystem path. The "_data" column is not part of the CATEGORY_OPENABLE contract, so Drive is not required to return it.
I've read this blog post by CommonsWare which suggest I "try using the Uri directly with ContentResolver" which I don't understand. How do I use the URI directly with ContentResolvers?
However, I'm still not clear on how best to approach these types of URIs.
The best solution i've been able to find is to call openFileDescriptor and then copy the filestream into a new file, then passing that new file path to my upload activity.
private static String getDriveFileAbsolutePath(Activity context, Uri uri) {
if (uri == null) return null;
ContentResolver resolver = context.getContentResolver();
FileInputStream input = null;
FileOutputStream output = null;
String outputFilePath = new File(context.getCacheDir(), fileName).getAbsolutePath();
try {
ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
FileDescriptor fd = pfd.getFileDescriptor();
input = new FileInputStream(fd);
output = new FileOutputStream(outputFilePath);
int read = 0;
byte[] bytes = new byte[4096];
while ((read = input.read(bytes)) != -1) {
output.write(bytes, 0, read);
}
return new File(outputFilePath).getAbsolutePath();
} catch (IOException ignored) {
// nothing we can do
} finally {
input.close();
output.close();
}
return "";
}
The only problem here is that I lose the filename of that file. This seems a bit over complicated just to get a filePath from drive. Is there a better way to do this?
thanks.
EDIT:
So i can use a normal query to get the filename. Then I can pass that into my getDriveAbsolutePath() method. Which will get me pretty close to what I want, the only problem now is that I'm missing file extensions. All searches I've done recommend using the filepath to get extensions, which I can't do with openFileDescriptor(). Any help?
String filename = "";
final String[] projection = {
MediaStore.MediaColumns.DISPLAY_NAME
};
ContentResolver cr = context.getApplicationContext().getContentResolver();
Cursor metaCursor = cr.query(uri, projection, null, null, null);
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
filename = metaCursor.getString(0);
}
} finally {
metaCursor.close();
}
}
However, I'm not entirely convinced this is the "right" way to do this?
The only problem here is that I lose the filename of that file. This seems a bit over complicated just to get a filePath from drive. Is there a better way to do this?
You seem to miss an important point here. Files in Linux don't need to have a name. They may exist in memory (e.g. android.os.MemoryFile) or even reside in directory without having a name (such as files, created
with O_TMPFILE flag). What they do need to have is a file descriptor.
Short summary: file descriptors are better than simple files and should always be used instead, unless closing them after yourself is too much of burden. They can be employed for same things as File objects, and much more, if you can use JNI. They are made available by special ContentProvider and can be accessed via openFileDescriptor method of ContentResolver (which receives Uri, associated with target provider).
That said, just saying people, used to File objects, to replace them with descriptors sure sounds weird. Read an elaborate explanation below, if you feel like trying it out. If you don't, just skip to the bottom of the answer for "simple" solution.
EDIT: the answer below have been written before Lollipop became widespread. Nowadays there is a handy class for direct access to Linux system calls, that makes using JNI for working with file descriptors optional.
Quick briefing on descriptors
File descriptors come from Linux open system call and corresponding open() function in C library. You don't need to have access to file to operate on it's descriptor. Most access checks will simply be skipped, but some crucial information, such as access type (read/write/read-and-write etc.) is "hardcoded" into descriptor and can not be changed after it is created. File descriptors are represented by non-negative integer numbers, starting from 0. Those numbers are local to each process and don't have any persistent or system-wide meaning, they merely distinguish handles
to files from each other for given process (0, 1 and 2 traditionally reference stdin, stdout and stderr).
Each descriptor is represented by a reference to entry in descriptor table, stored in OS kernel. There are per-process and system-wide limits to number of entries in that table, so close your descriptors quickly, unless you want your attempts to open things and create new descriptors to suddenly fail.
Operating on descriptors
In Linux there are two kinds of C library functions and system calls: working with names (such as readdir(), stat(), chdir(), chown(), open(), link()) and operating on descriptors: getdents, fstat(), fchdir(), fchown(), fchownat(), openat(), linkat() etc. You can call these functions and system calls easily after a reading a couple of man pages and studying some dark JNI magic. That will raise quality of your software through the roof! (just in case: I am talking about reading and studying, not just blindly using JNI all the time).
In Java there is a class for working with descriptors: java.io.FileDescriptor. It can be used with FileXXXStream classes and thus indirectly with all framework IO classes, including memory-mapped and random access files, channels and channel locks. It is a tricky class. Because of requirement to be compatible with certain proprietary OS, this cross-platform class does not expose underlying integer number. It can not even be closed! Instead you are expected to close the corresponding IO classes, which (again, for compatibility reasons) share the same underlying descriptor with each other:
FileInputStream fileStream1 = new FileInputStream("notes.db");
FileInputStream fileStream2 = new FileInputStream(fileStream1.getFD());
WritableByteChannel aChannel = fileStream1.getChannel();
// pass fileStream1 and aChannel to some methods, written by clueless people
...
// surprise them (or get surprised by them)
fileStream2.close();
There are no supported ways to get integer value out of FileDescriptor, but you can (almost) safely assume, that on older OS versions there is a private integer descriptor field, which can be accessed via reflection.
Shooting yourself in the foot with descriptors
In Android framework there is a specialized class for working with Linux file descriptor: android.os.ParcelFileDescriptor. Unfortunately, it is almost as bad as FileDescriptor. Why? For two reasons:
1) It has a finalize() method. Read it's javadoc to learn, what this means for your performance. And you still has to close it, if you don't want to face sudden IO errors.
2) Because of being finalizable it will be automatically closed by virtual machine once the reference to a class instance goes out of scope. Here is why having finalize() on some framework classes, especially MemoryFile is a mistake on part of framework developers:
public FileOutputStream giveMeAStream() {
ParcelFileDescriptor fd = ParcelFileDescriptor.open("myfile", MODE_READ_ONLY);
return new FileInputStream(fd.getDescriptor());
}
...
FileInputStream aStream = giveMeAStream();
// enjoy having aStream suddenly closed during garbage collection
Fortunately, there is a remedy to such horrors: a magical dup system call:
public FileOutputStream giveMeAStream() {
ParcelFileDescriptor fd = ParcelFileDescriptor.open("myfile", MODE_READ_ONLY);
return new FileInputStream(fd.dup().getDescriptor());
}
...
FileInputStream aStream = giveMeAStream();
// you are perfectly safe now...
// Just kidding! Also close original ParcelFileDescriptor like this:
public FileOutputStream giveMeAStreamProperly() {
// Use try-with-resources block, because closing things in Java is hard.
// You can employ Retrolambda for backward compatibility,
// it can handle those too!
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open("myfile", MODE_READ_ONLY)) {
return new FileInputStream(fd.dup().getDescriptor());
}
}
The dup syscall clones integer file descriptor, which makes corresponding FileDescriptor independent from original one. Note, that passing descriptors across processes does not require manual duplication: received
descriptors are independent from source process. Passing descriptor of MemoryFile (if you obtain it with reflection) does require the call to dup: having a shared memory region destroyed in originating
process will make it inaccessible to everyone. Furthermore, you have to either perform dup in native code or keep a reference to created ParcelFileDescriptor until a receiver is done with your MemoryFile.
Giving and receiving descriptors
There are two ways to give and receive file descriptors: by having a child process inherit creator's descriptors and via interprocess communication.
Letting children of process inherit files, pipes and sockets, open by creator, is a common practice in Linux, but requires forking in native code on Android – Runtime.exec() and ProcessBuilder close all extra
descriptors after creating a child process. Make sure to close unnecessary descriptors too, if you choose
to fork yourself.
The only IPC facilities, currently supporting passing file descriptors on Android are Binder and Linux domain sockets.
Binder allows you to give ParcelFileDescriptor to anything that accepts parcelable objects, including putting them in Bundles, returning from content providers and passing via AIDL calls to services.
Note, that most attempts to pass Bundles with descriptors outside of the process, including calling startActivityForResult will by denied by system, likely because timely closing those descriptors would have been too hard. Much better choices are creating a ContentProvider (which manages descriptor lifecycle for you, and publishes files via ContentResolver) or writing an AIDL interface and closing a descriptor right after it is transferred. Also note, that persisting ParcelFileDescriptor
anywhere does not make much sense: it would only work until process death and corresponding integer will most likely point to something else, once your process is recreated.
Domain sockets are low-level and a bit painful to use for descriptor transfer, especially compared to providers and AIDL. They are, however, a good (and the only documented) option for native processes. If you are
forced to open files and/or move data around with native binaries (which is usually the case for applications, using root privileges), consider not wasting your efforts and CPU resource on intricate communications with
those binaries, instead write an open helper. [shameless advert] By the way, you may use the one I wrote, instead of creating your own. [/shameless advert]
Answer to exact question
I hope, that this answer have given you a good idea, what's wrong with MediaStore.MediaColumns.DATA, and why creating this column is a misnomer on the part of Android development team.
That said, if you are still not convinced, want that filename at all costs, or simply failed to read the overwhelming wall of text above, here – have a ready-to-go JNI function; inspired by Getting Filename from file descriptor in C (EDIT: now has a pure-Java version):
// src/main/jni/fdutil.c
JNIEXPORT jstring Java_com_example_FdUtil_getFdPathInternal(JNIEnv *env, jint descriptor)
{
// The filesystem name may not fit in PATH_MAX, but all workarounds
// (as well as resulting strings) are prone to OutOfMemoryError.
// The proper solution would, probably, include writing a specialized
// CharSequence. Too much pain, too little gain.
char buf[PATH_MAX + 1] = { 0 };
char procFile[25];
sprintf(procFile, "/proc/self/fd/%d", descriptor);
if (readlink(procFile, buf, sizeof(buf)) == -1) {
// the descriptor is no more, became inaccessible etc.
jclass exClass = (*env) -> FindClass(env, "java/io/IOException");
(*env) -> ThrowNew(env, exClass, "readlink() failed");
return NULL;
}
if (buf[PATH_MAX] != 0) {
// the name is over PATH_MAX bytes long, the caller is at fault
// for dealing with such tricky descriptors
jclass exClass = (*env) -> FindClass(env, "java/io/IOException");
(*env) -> ThrowNew(env, exClass, "The path is too long");
return NULL;
}
if (buf[0] != '/') {
// the name is not in filesystem namespace, e.g. a socket,
// pipe or something like that
jclass exClass = (*env) -> FindClass(env, "java/io/IOException");
(*env) -> ThrowNew(env, exClass, "The descriptor does not belong to file with name");
return NULL;
}
// doing stat on file does not give any guarantees, that it
// will remain valid, and on Android it likely to be
// inaccessible to us anyway let's just hope
return (*env) -> NewStringUTF(env, buf);
}
And here is a class, that goes with it:
// com/example/FdUtil.java
public class FdUtil {
static {
System.loadLibrary(System.mapLibraryName("fdutil"));
}
public static String getFdPath(ParcelFileDescriptor fd) throws IOException {
int intFd = fd.getFd();
if (intFd <= 0)
throw new IOException("Invalid fd");
return getFdPathInternal(intFd);
}
private static native String getFdPathInternal(int fd) throws IOException;
}

implement at command on rooted android and get the result

I'm a beginner in stackoverflow so I cant add a comment.
I saw this page:
Read command output inside su process
and I tried this answer and it is ok:
Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
//from here all commands are executed with su permissions
stdin.writeBytes("ls /data\n"); // \n executes the command
InputStream stdout = p.getInputStream();
byte[] buffer = new byte[BUFF_LEN];
int read;
String out = new String();
//read method will wait forever if there is nothing in the stream
//so we need to read it in another way than while((read=stdout.read(buffer))>0)
while(true){
read = stdout.read(buffer);
out += new String(buffer, 0, read);
if(read<BUFF_LEN){
//we have read everything
break;
}
}
//do something with the output
but when I tried at command in the shell the response was the same command.
I put this command:
stdin.writeBytes("echo AT+CQI?\n");
the answer was:
AT+CQI?
I wrote:
stdin.writeBytes("echo ATinkd\n");
the answer was:
ATinkd
That is mean "bla..bla..bla..". that is mean the android system does not recognize this commands as at commands.
I wonder if any body have an advice or solution.
First I think you are just sending the AT command to stdout in the shell which will not do anything other than giving you an echo which you read back. For this approach to work you have to redirect the echo command to the serial port device file. Android phones use various devices for this, /dev/ttyGS0 and /dev/smd0 seems to be common names.
However I would suggest using the program atinput to send AT commands and capture modem responses. It is specifically written to be used from the command line like that. That will relieve you from communicating directly with the modem and the only thing left is the pipe handling reading the response.

How can I transfer a database test fixture to device from my unit test application

I'm writing an Android JUnit test and want to copy/reset a test fixture file (it's an SQLite database file.) If I were within the main application, I know I could just place the file in the assets directory and use getResources().getAssets().open(sourceFile)
However, this API appears to be unavailable from the ActivityInstrumentationTestCase2 class.
Is there an easy way to copy a file over from the testing PC, or should I just keep a fresh copy of a test fixture on the device and copy it over a temporary file?
Thanks!
The resources in your test application and your main application are accessible separately in an instrumentation test case. If you want to access resources that are in the res/raw or assets folder of the test project itself, you can use
getInstrumentation().getContext().getResources()
To access resources in the application being tested (the "target" application), use
getInstrumentation().getTargetContext().getResources()
Note, however, that you can never modify files in the assets folder;
getResources().getAssets().open(sourceFile)
returns an InputStream. There's no way to modify the file. That's because assets are stored compressed inside the APK, and are not really writeable at all.
If what you want to do is modify the path to the files the Activity you're testing uses, you should use ActivityUnitTestCase and setActivityContext() with a RenamingDelegatingContext. This allows you to redirect file and database access in a context to a new directory by specifying a directory prefix. You can even use the more complex constructor to wrap the target context for most operations, but use your test application's context for file operations, so the activity will access files stored in the test application rather than the primary application but still use other resources in the primary application.
What I've done to accomplish this (in a not very elegant way) is to copy the test fixture onto my device (or emulated device.) I named it "cleantestdatabase.db." Then in the test code, I copy it to "testdatabase.db," so that I can modify it with my tests but reset it to a known state. Here's the code:
copyFile("cleantestdatabase.db", "testdatabase.db");
private void copyFile(String source, String dest) throws IOException{
String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath() + getActivity().getString(R.string.default_dir);
File newDir = new File(rootPath);
boolean result = newDir.mkdir();
if(result == false){
Log.e("Error", "result false");
}
InputStream in = new FileInputStream(rootPath + source);
File outFile = new File(rootPath + dest);
if(outFile.exists()) {
outFile.delete();
}
outFile.createNewFile();
OutputStream out = new FileOutputStream(outFile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}

Categories

Resources