I'm trying to run adb commands programmatically from inside an Android priviledged app (apk file uploaded to priv-app folder, privapp-permissions-platform.xml file uploaded to /etc/permissions/.
private fun executeAdbCommand() {
val commandStart = "am start -n com.abc.bcd/com.abc.bcd.MainActivity"
val process = Runtime.getRuntime().exec(commandStart)
val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
val commandBattery = "dumpsys battery set level 20"
val processBattery = Runtime.getRuntime().exec(commandBattery)
val bufferedReaderBattery = BufferedReader(InputStreamReader(processBattery.inputStream))
val commandBack = "input keyevent ${KeyEvent.KEYCODE_BACK}"
val processBack = Runtime.getRuntime().exec(commandBack)
val bufferedReaderThree = BufferedReader(InputStreamReader(processBack.inputStream))
}
The first two commands produce no result. The third one is actually pressing the Back Button.
When I run the first two commands from a PowerShell windows they are both executed correctly:
adb shell am start -n com.abc.bcd/com.abc.bcd.MainActivity is starting the mentioned activity,
adb shell dumpsys battery set level 20 is setting the emulator's battery to 20%.
I am targeting Android 10 (API level 29) - is this the expected behaviour of the system? What is the particular reason the first two commands are getting discarded (no error log is generated) and the third one is getting executed? Is there any way of getting the first two commands running (might this be a missing permission or something similar)?
Related
Question about changing the parameters of the transition to doze mode
I have a non-rooted Android 12 device
There are a number of parameters for changing the transition time in doze mode:
inactive_to
motion_inactive_to
light_after_inactive_to
If you change these parameters through the PC using ADB, then the parameters are set. For instance:
adb shell device_config put device_idle inactive_to 2592000000
The problem is that after a reboot, the settings are reset.
Tried to change them directly in the program
// val command = arrayOf("/system/bin/device_config", "put", "device_idle", "motion_inactive_to", "2592000000")
val command = arrayOf("/system/bin/settings", "put", "global", "device_idle_constants", "light_after_inactive_to", "2592000000")
Log.d("ADB", "Start ${command.joinToString(separator = " ")}")
val process = Runtime.getRuntime().exec(command)
val bufReader = BufferedReader(InputStreamReader(process.errorStream))
val log = StringBuilder()
var line: String?
line = bufReader.readLine()
while (line != null) {
log.append(line + "\n")
line = bufReader.readLine()
}
Log.d("ADB", "Command: $log")
But the first command is answered:
“cmd: Can't find service: "device_config"”
And the second command gives an error:
Permission Denial: getCurrentUser() from pid=28435, uid=10245 requires android.permission.INTERACT_ACROSS_USERS
After searching for information about the INTERACT_ACROSS_USERS permission, I understand that it is necessary for it to make the application system. And for this I need to root the device.
Is there any other way to change the parameters of doze mode or disable it altogether?
It's confusing that you can run a command with adb on a non-rooted device, but you can't directly in the program.
Is there any other way to change the parameters of doze mode or disable it altogether?
No. If there were, it would have been pointless to add Doze mode, as every developer would opt out of it.
It's confusing that you can run a command with adb on a non-rooted device, but you can't directly in the program.
Different users/processes having different privileges has been a common concept in operating systems for at least 30 years.
I am working on a small proof of concept on a rooted phone, which relys on being able to read dumpsys output.
If I call dumpsys on my (rooted) phone running Android 11 like this, using adb:
adb shell dumpsys telephony.registry | grep "mCi="
I get a pretty long printout. The grep filters for lines containing cell tower IDs, but that shouldn't e important here (it's just an example). Now I'm trying to execute the same command inside a very simple app, and log its output, like this:
private fun test() {
try {
val process = Runtime.getRuntime().exec("su dumpsys telephony.registry | grep \"mCi=\"")
val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
val string = bufferedReader.readText()
Timber.d("output: $string")
bufferedReader.close() // do I need this?
} catch (e: IOException) {
// handle Exception
}
}
I get no output at all (string length is 0). If I replace my process command with something simple like this: Runtime.getRuntime().exec("echo 'abcde'") the output is logged as intended (output: 'abcde').
I also tried shortening the possible output, in case that was the problem by appending --max-count=1, to have grep only put out the first found line. Again, it works using adb, does not work in code.
what am I doing wrong?
(I am using Timber to print my logs, if anyone doesn't know what that line is in the xample.)
The first thing you should do is to log the stderr stream that is available for your process as well. This will give you information about what is wrong with your command.
Your command is not correctly processed as it is seen as one command. The reason is explained in this answer.
The solution is to use a String[] as an argument of exec and explicitly execute the command with the shell. I wrote some code that executes your command, but it is in Java on an unrooted device. Still, it generates output and grep works.
String[] arrayCommand = {"sh", "-c","dumpsys telephony.registry | grep \"permission\""};
Runtime r = Runtime.getRuntime();
Process process = r.exec(arrayCommand);
String stdoutString = convertInputStreamToString(process.getInputStream());
String stderrString = convertInputStreamToString(process.getErrorStream());
So if I pop open a command prompt and take a peek at my device's /proc/meminfo, like so:
adb shell cat /proc/meminfo
I get back what you'd expect - a nice long list of data on the device's RAM usage + capacity. But when I try to read that same location from an adobe air android app, using this basic code:
var meminfo:File = new File().resolvePath('/proc/meminfo');
meminfo.addEventListener(Event.COMPLETE, function(e:Event):void {
trace(File(e.target).data);
});
meminfo.load();
... I get nothing, just an empty ByteArray.
So why can I see the contents of /proc/meminfo from the adb shell, but not from the app? Same goes for other stuff in the /proc/ directory - cpuinfo, for instance. I have no problem loading an xml file from the /etc/ directory, though.
It turns out that if you copy memtest (or cputest, etc.) out to another location, somewhere a little friendlier to AIR - say, File.documentsDirectory - then everything goes smoothly.
Your code might look a little like this:
var meminfo_original:File = new File().resolvePath('/proc/meminfo');
var meminfo_copy:File = File.documentsDirectory.resolvePath('meminfo.txt');
meminfo_original.copyTo(meminfo_copy, true);
meminfo_copy.addEventListener(Event.COMPLETE, function(e:Event):void {
trace(File(e.target).data.toString());
});
meminfo_copy.load();
I'm trying to enable ADB (USB debugging) only when my application is running and disabling it when my application is not. I have full access to the phone and it is rooted, su available, etc., but I cannot find a way to do the toggling.
What I've tried so far:
Process proc = Runtime.getRuntime().exec(new String [] { "su", "-c", "setprop", "persist.service.adb.enable", "0"});
proc.waitFor();
Process proc2 = Runtime.getRuntime().exec(new String [] { "su", "-c", "stop", "adbd"});
proc2.waitFor();
This however causes the phone to enter a reboot loop instantaneously.
The code that works is easy:
Settings.Secure.putInt(getContentResolver(),Settings.Secure.ADB_ENABLED, 0); // 0 to disable, 1 to enable
Edit/Update:
Thanks to #MohanT for the following update:
Settings.Global.putInt(getContentResolver(),Settings.Global.ADB_ENABLED, 0); // 0 to disable, 1 to enable
However, the app needs to be a system app to be able to use this. To get out of having to sign it, build a custom rom, etc.. I did the following to get it to be a system app.
First, I installed it regularly using eclipse, then adb shell:
> su
# mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system
# cat /data/app/filename.apk > /system/app/filename.apk
# mount -o remount,ro -t yaffs2 /dev/block/mtdblock3 /system
# reboot
You can achieve by following next steps:
Check whether ADB is currently enabled. To do that you can use a relevant global constant:
Settings.Global.ADB_ENABLED for API 17 and above.
Settings.Secure.ADB_ENABLED prior to API 17.
Use implicit Intent with Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS action to open developers options, where user can enable or disable ADB.
Code example:
Java
public static final int API = Build.VERSION.SDK_INT;
public static final int ENABLED=1, DISABLED=0;
public static boolean adbEnabled(Context context){
if(API>16)
return ENABLED == Settings.Global.getInt(context.getContentResolver(), Settings.Global.ADB_ENABLED,DISABLED);
else
return ENABLED == Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ADB_ENABLED,DISABLED);
}
public void checkAdb(Context context){
//if ADB disabled
if(!adbEnabled(context)){
//open developer options settings
Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
context.startActivity(intent);
}
}
Kotlin
val API = Build.VERSION.SDK_INT
val ENABLED = 1
val DISABLED = 0
fun adbEnabled(context: Context): Boolean {
return if (API > 16)
ENABLED == Settings.Global.getInt(context.contentResolver, Settings.Global.ADB_ENABLED, DISABLED)
else
ENABLED == Settings.Secure.getInt(context.contentResolver, Settings.Secure.ADB_ENABLED, DISABLED)
}
fun checkAdb(context: Context) {
//if ADB disabled
if (!adbEnabled(context)) {
//open developer options settings
val intent = Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
context.startActivity(intent)
}
}
Just for reference:
Prior to API 3 (probably no longer relevant) Settings.System.ADB_ENABLED was used.
Chris's answer is correct except the ADB_ENABLED field in Secure class has been depreciated and the string has been moved to Global class. So you can use below command -
Settings.Global.putInt(getContentResolver(),Settings.Global.ADB_ENABLED, 0); // 0 to disable, 1 to enable
From my experiences using setprop doesn't always work reliable, and might even conflict with the Settings > Applications > Development > USB Debugging option. Besides that it might be irritating for instance if USB debugging is enabled but adb doesn't work etc. Therefore using Settings.Secure.ADB_ENABLED is the prefered way... plus you always have the notification in the status panel.
If you don't want to go through the hassle to install your app on system partition when only trying to access Settings.Secure.ADB_ENABLED then you can instead rely on another app that is doing this work already:
This app is called ADB Toggle. You can find it in Google Play:
https://play.google.com/store/apps/details?id=com.ramdroid.adbtoggle
The library to access USB debug settings via ADB Toggle is available here:
https://github.com/ramdroid/AdbToggleAccessLib
I'm trying to enable ADB (USB debugging) only when my application is running and disabling it when my application is not. I have full access to the phone and it is rooted, su available, etc., but I cannot find a way to do the toggling.
What I've tried so far:
Process proc = Runtime.getRuntime().exec(new String [] { "su", "-c", "setprop", "persist.service.adb.enable", "0"});
proc.waitFor();
Process proc2 = Runtime.getRuntime().exec(new String [] { "su", "-c", "stop", "adbd"});
proc2.waitFor();
This however causes the phone to enter a reboot loop instantaneously.
The code that works is easy:
Settings.Secure.putInt(getContentResolver(),Settings.Secure.ADB_ENABLED, 0); // 0 to disable, 1 to enable
Edit/Update:
Thanks to #MohanT for the following update:
Settings.Global.putInt(getContentResolver(),Settings.Global.ADB_ENABLED, 0); // 0 to disable, 1 to enable
However, the app needs to be a system app to be able to use this. To get out of having to sign it, build a custom rom, etc.. I did the following to get it to be a system app.
First, I installed it regularly using eclipse, then adb shell:
> su
# mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system
# cat /data/app/filename.apk > /system/app/filename.apk
# mount -o remount,ro -t yaffs2 /dev/block/mtdblock3 /system
# reboot
You can achieve by following next steps:
Check whether ADB is currently enabled. To do that you can use a relevant global constant:
Settings.Global.ADB_ENABLED for API 17 and above.
Settings.Secure.ADB_ENABLED prior to API 17.
Use implicit Intent with Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS action to open developers options, where user can enable or disable ADB.
Code example:
Java
public static final int API = Build.VERSION.SDK_INT;
public static final int ENABLED=1, DISABLED=0;
public static boolean adbEnabled(Context context){
if(API>16)
return ENABLED == Settings.Global.getInt(context.getContentResolver(), Settings.Global.ADB_ENABLED,DISABLED);
else
return ENABLED == Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ADB_ENABLED,DISABLED);
}
public void checkAdb(Context context){
//if ADB disabled
if(!adbEnabled(context)){
//open developer options settings
Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
context.startActivity(intent);
}
}
Kotlin
val API = Build.VERSION.SDK_INT
val ENABLED = 1
val DISABLED = 0
fun adbEnabled(context: Context): Boolean {
return if (API > 16)
ENABLED == Settings.Global.getInt(context.contentResolver, Settings.Global.ADB_ENABLED, DISABLED)
else
ENABLED == Settings.Secure.getInt(context.contentResolver, Settings.Secure.ADB_ENABLED, DISABLED)
}
fun checkAdb(context: Context) {
//if ADB disabled
if (!adbEnabled(context)) {
//open developer options settings
val intent = Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
context.startActivity(intent)
}
}
Just for reference:
Prior to API 3 (probably no longer relevant) Settings.System.ADB_ENABLED was used.
Chris's answer is correct except the ADB_ENABLED field in Secure class has been depreciated and the string has been moved to Global class. So you can use below command -
Settings.Global.putInt(getContentResolver(),Settings.Global.ADB_ENABLED, 0); // 0 to disable, 1 to enable
From my experiences using setprop doesn't always work reliable, and might even conflict with the Settings > Applications > Development > USB Debugging option. Besides that it might be irritating for instance if USB debugging is enabled but adb doesn't work etc. Therefore using Settings.Secure.ADB_ENABLED is the prefered way... plus you always have the notification in the status panel.
If you don't want to go through the hassle to install your app on system partition when only trying to access Settings.Secure.ADB_ENABLED then you can instead rely on another app that is doing this work already:
This app is called ADB Toggle. You can find it in Google Play:
https://play.google.com/store/apps/details?id=com.ramdroid.adbtoggle
The library to access USB debug settings via ADB Toggle is available here:
https://github.com/ramdroid/AdbToggleAccessLib