Storing android adb shell screencap as variable - android

Making an app at the moment for my personal use (rooted) and it requires getting certain pixels colors from the screen. I was trying to accomplish this through the Runtime.
Process p = Runtime.getRuntime().exec("screencap");
p.waitFor();
InputStream is = p.getInputStream()
BitmapFactory.decodeStream(is);
and I get factory returned null.
but if I dump the process to my sd card through adb -d shell screencap /sdcard/ss.dump and access it from my app
BitmapFactory.decodeFile("/sdcard/ss.dump");
all goes well.
So it there anyway to dump the stream straight into BitmapFactory within my app?
Thanks SO and please excuse the generally laziness/shortcuts of the example code.

This might help if not too far off your intended path. (I think you are using node / javascript). I spawned the ADB.EXE command producing a stream (and being 'jailed' on Windows the program must transform the stream to account for linefeed ending differences. So with that, I have working the following:
exports.capture = function(filename) {
// you'll need to map your requirement (decodeStream) instead
// of streaming to a file
var strm = fs.createWriteStream(path);
var cv = new Convert();
cv.pipe(strm);
var capture = spawn(cmd, args);
capture.stdout.on('data', function(data) {
cv.write(data);
});
capture.stdout.on('exit', function(data) {
cv.end();
});
}
To explain the process, spawn is running the ADB command, on windows, CR-LF are inserted (being a PNG stream), and stream is chunked / piped through a fs-transformation. Others on the web have described the process as adb.exe shell screencap -p | sed 's/\r$//' > output.file. And it does work. To be clear the conversion is CR-CR-LF => LF for us window jailed birds. And if you don't want to implement a SED and not use javascript regular expressions converting binary->strings->binary, you may follow my path. There is probably a simpler way, I just don't know what it is...
So, Convert() is an object with methods that converts the byte stream on the fly.
See the codewind blog link is: http://codewinds.com/blog/2013-08-20-nodejs-transform-streams.html to build your convert.

When using screencap from inside an app you must use su, i.e. root. When you do this via adb it runs under a different user id, which has more permissions than a normal Android app.
There are several examples how to use screencap, e.g. here.

Related

How to get a full android app UID programmatically?

Edit 10 Apr 2020
I may have misnamed what we are looking for. It may actually be the linux user name of the installed app, rather than its UID. So how should we get that programmatically?
Original question below
When we use adb shell ps even on a non rooted android device, it returns process info where the UID comes in the form u0_xxxx where x represents some arbitrary digits in hex (except for system/root processes).
For example
u0_a464 31481 894 5015336 69200 0 0 S com.example.app_uid_checker
In this example app_uid_checker is my app in user space. When trying to obtain the UID programmatically, though, I get 10464 (the decimal representation of a464), and without the u0 prefix.
I tried
package manager's getApplicationInfo()
activity manager's getAllRunningProcess()
android.os.Process.myUid()
(following suggestions in this post on SO. They all return 10464. How can I get the "full" UID? (for want of a better term, I'm not sure what the u0_a464 version of the UID should be called, as compared to the 10464 version)).
Even if we can programmatically use adb shell ps I think it may not be a good way, as adb needs developer mode to be enabled.
You need to use the geteuid(2) and getpwuid(3) to retrieve the data, as the JVM does not expose it.
extern "C" JNIEXPORT jstring JNICALL Java_com_example_GetUser_getUser(JNIEnv* env) {
uid_t uid = geteuid();
struct passwd *user;
if (uid == -1)
return NULL;
user = getpwuid(uid);
return env->NewStringUTF(user->pw_name);
}
Full working project: https://gitlab.com/hackintosh5/getuser

ADB command from macOS application with special characters

I want to pull a file from my android device through an adb command from my macOS application.
Everything works perfect with the code below, except when the name of the file I want to pull contains special characters like german umlauts (äöüÄÖÜ).
I get this error:
adb: error: failed to stat remote object '/storage/emulated/0/Download/Böse': No such file or directory.
But when I use the command adb pull /storage/emulated/0/Download/Böse ~/Desktop from within the Terminal.app, the file will be pulled to my computer.
The strange thing here is that if I copy the substring /storage/emulated/0/Download/Böse from the Xcode console output, the command is also not working within the Terminal.app until I delete the ö and replace it with an ö from my keyboard input.
I tried replacing the ö with the unicode representation \u{00f6}, but this has no effect (but the console output still shows an ö but the 'wrong' encoded one.
// Configure task.
let task = Process()
task.launchPath = "~/Library/Android/sdk/platform-tools/adb"
task.arguments = ["pull", "/storage/emulated/0/Download/Böse", "~/Desktop"]
// Configure pipe.
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
// Run task.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()
// adb: error: failed to stat remote object '/storage/emulated/0/Download/Böse': No such file or directory
print(output)
I found the following in the documentation, how the Process handles the arguments that I provide:
The NSTask object converts both path and the strings in arguments to appropriate C-style strings (using fileSystemRepresentation) before passing them to the task via argv[] . The strings in arguments do not undergo shell expansion, so you do not need to do special quoting, and shell variables, such as $PWD, are not resolved.
It seems like I am not the only one with this problem, and I found this workaround:
How to work around NSTask calling -[NSString fileSystemRepresentation] for arguments, but I was not able to make it work with Swift.
As a workaround I am now writing my adb command to a file and execute it from a bash command in my application.
let source = "/storage/emulated/0/Download/Böse"
let destination = "~/Desktop"
guard let uniqueURL = URL(string: destination + "/" + ProcessInfo.processInfo.globallyUniqueString) else { return }
// Write command to file
let scriptContent = "#!/bin/bash\n~/Library/Android/sdk/platform-tools/adb pull -a \"" + source + "\" \"" + destination + "\""
try? scriptContent.write(to: uniqueURL, atomically: false, encoding: .utf8)
// Configure task.
let task = Process()
task.environment = ["LC_ALL": "de_DE.UTF-8", "LANG": "de_DE.UTF-8"]
task.launchPath = "/bin/bash"
task.arguments = [uniqueURL.path]
// Configure pipe.
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
try? task.run()
// Run task.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()
print(output)
Even though this is working for now, it is not a satisfactory solution as it is not very elegant and not efficient too, so any improvements or better answers are welcome.

What is the purpose of using double underscore ( __ ) before the start and after the end of an file name in c?

I'm studying the android kernel as a beginner. I can read the messages thrown from the macro ERROR() inside the function main() at system/core/init/init.c using dmesg command through adb. I observed that after calling the function open_devnull_stdio() inside main(), dmesg no longer displays the messages thrown by ERROR().
To find the reason, I started digging into the declaration of open_devnull_stdio() inside system/core/init/util.c and I found this line I can't understand
static const char *name = "/dev/__null__";
Actually there was no file named __null__ inside /dev/ in the device, but there was a file named null and I was able to grab it using adb pull and it was a 0 byte (empty) file.
So why is a file name wrapped with double underscore (__) ?
Here is the link for the util.c
There is no special purpose of using double underscore before the start, after the end or both in C. From the point of view of C the file name is just a string, the operating system is free to interpret in whatever way it chooses. From the point of view of Linux, the same applies. Underscores in file names are just characters. They are not treated differently from the letters b and t.
If I guessed right and I'm reading the same file as you (it might be a good idea to link to the source code you're reading) then it should be pretty obvious what the code does on the lines after the one you mentioned. The next lines are:
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
fd = open(name, O_RDWR);
unlink(name);
Which creates the null device which is then opened and immediately deleted again.
I suspect this is done so that programs can run without access to the root filesystem and still be able to open the equivalent of /dev/null.
I don't know the answer but I have an idea:
The following page shows an "strace" output where /dev/__null__ is used:
https://gist.github.com/tetsu-koba/1522515
Under Linux device files have a 33-bit (?) number which identifies the device. (At least under old Linux versions) you could delete some file in /dev and you could restore it or even create it in another directory (!) when you know the 33-bit number! (So you can delete the device /dev/sda2 and create the device (not file!) /home/myuser/sda2 instead.)
The trace in the link above shows the following three lines:
mknod("/dev/__null__", S_IFCHR|0600, makedev(1, 3)) = 0
open("/dev/__null__", O_RDWR|O_LARGEFILE) = 3
unlink("/dev/__null__") = 0
These lines will create the device file /dev/__null__ (with the 33-bit number identifying /dev/null). Then it opens that file and then it removes the file again.
Maybe this is done because the tool shall be able to run both on Linux installations where the device file "/dev/null" is present (in this case the file should not be overwritten) and on installations where that file is missing (in this case a replacement file must be created using the known 33-bit number).
As other people have pointed out this just tells it's the "null device", not a regular file called "null". null is supposed to act like an information sink, not like a normal file where you dump your data to. Hope this helps.

Passing string in stdin

I want to create an AVD (Android Virtual Device) through command line in python. For that, I need to pass a string n to the stdin. I have tried the following
emulator_create = str(subprocess.check_output([android,'create', 'avd', '-n', emulator_name, '-t', target_id, '-b', abi],stdin=PIPE))
emulator_create.communicate("n")
but it raises the following error
raise CalledProcessError(retcode, cmd, output=output)
subprocess.CalledProcessError: Command '['/home/fahim/Android/Sdk/tools/android', 'create', 'avd', '-n', 'samsung_1', '-t', '5', '-b', 'android-tv/x86']' returned non-zero exit status 1
Process finished with exit code 1
What can I do?
There's something not working with your example. subprocess.check_output() returns the output from the child process you want to execute, not a handle to this process. In other words you get a string object (or maybe a bytes object) which you cannot use to manipulate the child process.
Probably what happens is that your script, using subprocess.check_output(), will execute the child process and wait until it is finished before continuing. But since you are never able to communicate with it, it will finish with a non-zero return value which will raise the subprocess.CalledProcessError
Now, using grep as an example of a command that waits on the standard input to execute something (since I don't have an Android Virtual Device creator installed) you could do this:
#!/usr/bin/env python2.7
import subprocess
external_command = ['/bin/grep', 'StackOverflow']
input_to_send = '''Almost every body uses Facebook
You can also say that about Google
But you can find an answer on StackOverflow
Even if you're an old programmer
'''
child_process = subprocess.Popen(args=external_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
universal_newlines=True)
stdout_from_child, stderr_from_child = child_process.communicate(input_to_send)
print "Output from child process:", stdout_from_child
child_process.wait()
It will print "Output from child process: But you can find an answer on StackOverflow", which is the output from grep.
In this example, I have
Used the class subprocess.Popen to create an handle to the child process
Setting arguments stdin and stdout with the value subprocess.PIPE to enables us to communicate later on with this process.
Used its .communicate() method to send a string to its standard input. In the same step, I retrieved its standard output and standard error output.
Printed the standard output retrieved in the last step (just so to show that it is actually working)
Waited that this child process is finished
In Python 3.5, it's even simpler:
#!/usr/bin/env python3.5
import subprocess
external_command = ['/bin/grep', 'StackOverflow']
input_to_send = '''Almost every body uses Facebook
You can also say that about Google
But you can find an answer on StackOverflow
Even if you're an old programmer
'''
completed_process_result = subprocess.run(args=external_command,
input=input_to_send,
stdout=subprocess.PIPE,
universal_newlines=True)
print("Output from child process:", completed_process_result.stdout)
In this example, I have:
Used the module function subprocess.run() to execute the command.
The input argument is the string we send to the standard input of the child process
The return value is used later on to retreive the output of the child process
Now you have to adapt this code to your situation.

Launch an SL4A script from an SL4A script

I would like to start a background SL4A script (on a remote device) from within a different SL4A script. I can launch a script from a terminal by running something like this:
$ am start -a \
com.googlecode.android_scripting.action.LAUNCH_BACKGROUND_SCRIPT -n \
com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher -e \
com.googlecode.android_scripting.extra.SCRIPT_PATH /sdcard/sl4a/scripts/main.py
I can't translate this into a startActivity call in Python.
The answer to a different question on opening a Twitter client works nicely, but I don't know how to extend that code. For example, how would you add a script path, and where would you put the line com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher?
This function will launch any SL4A script from inside another one.
The first argument should be a path to the script you want to launch. The script can be in any language you have an interpreter installed for.
The second argument is optional and should be a bool. It defaults to False. It controls whether the terminal will be visible, so you can see output and errors. It does not effect whether the script has a UI or not.
from android import Android
droid = Android()
def launch_script(path, visible=False):
visibilty = 'FORE' if visible else 'BACK'
activity = 'com.googlecode.android_scripting.action.LAUNCH_{0}GROUND_SCRIPT'.format(visibilty)
extras = {'com.googlecode.android_scripting.extra.SCRIPT_PATH': path}
packagename = 'com.googlecode.android_scripting'
classname = 'com.googlecode.android_scripting.activity.ScriptingLayerServiceLauncher'
intent = droid.makeIntent(activity, None, None, extras, None, packagename, classname).result
droid.startActivityIntent(intent)
There's a gist for this code here.
After many, many failed attempts, I now have this working in Ruby - I had an easier time generating the JSON extras this way than in Python.
Important!
In the command-line version, you call on "com.googlecode.android_scripting/.activity.ScriptingLayerServiceLauncher"
From within a script, this is called as "com.googlecode.android_scripting.activity.ScriptingLayerServiceLauncher", without the slash. Leaving in the slash crashes sl4a.
[code]
require 'android'
require 'json/pure'
d=Android.new
script = '/sdcard/sl4a/scripts/YOUR_SCRIPT'
data = {"com.googlecode.android_scripting.extra.SCRIPT_PATH"=>script}
extras = JSON.generate(data)
d.startActivity('com.googlecode.android_scripting.action.LAUNCH_BACKGROUND_SCRIPT','','',data,true,'com.googlecode.android_scripting','com.googlecode.android_scripting.activity.ScriptingLayerServiceLauncher')
[/code]
I hope this helps!

Categories

Resources