I'm creating a simple Android app that should set the CPU Scaling Governor.
To achieve this I made this function:
public static void setCurrentGovernor(String governor){
Process process;
try{
String cpufreq_path= "/sys/devices/system/cpu/cpu0/cpufreq";
String cmd = "echo `"+governor+"` > "+cpufreq_path+"/scaling_governor ";
process = Runtime.getRuntime().exec(new String[] {"su","-c",cmd});
} catch(IOException e){
e.printStackTrace();
}
}
My problem is that this function won't work, plus superuser always prompts me for permissions, is there a way to give it root permission only at first app boot?
This function is called inside an onitemselectedlistener of a spinner, right after this function there's another one which fetches data from cpufreq files to update the view, but if i do cat scaling_governor i get the old governor, so it is not a faulty update function.
This more solid code solved my problem:
Link
Related
I would appreciate some help with some test automation on Android devices. We use Appium and RemoteWebDriver code to access the Android emulator, open up our application, tap and move around the application UI, and this all seems to work well.
However, as part of my testing, I would like to use Appium to initiate a telephone call on the device, keep the call open for a minute or so, and then hang up. Is there away to do this through the RemoteWebDriver object?
If not, what is the recommended way to make calls on the emulator? I have seen some discussion of using direct telnet calls to the emulator, but hope there is a better way!
You may set these desired capabilities :
capabilities.setCapability("androidPackage", "com.android.dialer");
capabilities.setCapability("appActivity", "DialtactsActivity");
and use this snippet to make call via Appium :
remoteWebDriver.findElement(By.id("com.android.dialer:id/search_view")).sendKeys("NAME_OF_PERSON");
remoteWebDriver.findElements(By.id("com.android.dialer:id/dialer_search_item_view")).get(0).click();
This would make call to the first search item
try {
Thread.sleep(60000); //
} catch (InterruptedException e) {
e.printStackTrace();
}
remoteWebDriver.findElement(By.id("com.android.dialer:id/endButton")).click();
This would disconnect the call after 60 seconds.
You can use a start a phone call using ADB:
public static int makePhoneCall(AppiumDriver driver, Srting deviceId, String phoneNum, int callDuration) throws IOException, InterruptedException {
callDuration *= 1000;
cmd = "adb -s " + deviceId + " shell am start -a android.intent.action.CALL -d tel:" + phoneNum; //open a Dialer and placing a call right away
Process exec = Runtime.getRuntime().exec(cmd); //starting a call and..
Thread.sleep(callDuration);//..waiting for callDuration seconds before hangup
driver.sendKeyEvent(6);// hang up phonecall
return exec.exitValue();
}
It turns out that this is possible, though perhaps a little more painfully than I expected. I had to do two things: specify the correct app to open, and work out the xpath references to the buttons on the dial pad. The activity is com.android.contacts.activities.DialtactsActivity and the xpaths to some of the buttons are:
Number text field: /linear/linear/editText
Number 1 button: /linear/table/row[1]/imageButton[1]
Number 5 button: /linear/table/row[2]/imageButton[2]
Dial button: /linear/frame/imageButton
If anyone has a better way to do this, I'd be very pleased to see it! Martin
press home button
driver.sendKeyEvent(3);
Press call button.
dr.sendKeyEvent(5);
locate dial pad
driver.findElementById("com.android.dialer:id/dialpad_button").click();
type number by send key or by sendKeyEvents.
driver.findElement(By.className("android.widget.EditText")).sendKeys(phoneNumber);
Press call button
driver.findElementById("com.android.dialer:id/dial_button").click();
put some wait and press End call button.
dr.findElementById("com.android.dialer:id/endButton").click();
I am developing an Android application and we need to power off the device under certain circumstances.
I have read in many places that you need a rooted phone in order to do so. Then, you can issue the "reboot" command by using Java's API:
try {
Process proc = Runtime.getRuntime()
.exec(new String[]{ "su", "-c", "reboot -p" });
proc.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
I have actually tried this in a Cyanogenmod 10 device (Samsung Galaxy S3), and it works. However, we do not want a rooted device in order to power it off, since the end user will then be able to do unintended things which are not allowed by our company.
On the other hand, our application is signed by the manufacturer's certificate, in this case Cyanogen's. I have read that by signing your application with the manufacturer's certificate, you should be able to issue privileged commands (as if root). However, even if I install my app as a system app signed with the manufacturer's certificate, the above code does not work:
If I leave the "su" part of the command, the "Superuser Request" screen is displayed, but that's something we are trying to avoid.
If I remove the "su" part (just leaving "reboot -p"), the command is silently ignored.
As a result, we are not being able to poweroff our device with our system app, which is signed with the manifacturer's certificate. So my question is, how am I supposed to do that?
EDITED
And, by the way, just in case someone is not sure about it: the application is properly signed and installed as a system application, because we can actually access some restricted APIs, such as PowerManager.goToSleep()
If you want the device to reboot (power off and on), then try PowerManager.reboot()
PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
powerManager.reboot(null);
android.os.PowerManager:
/**
* Reboot the device. Will not return if the reboot is successful.
* <p>
* Requires the {#link android.Manifest.permission#REBOOT} permission.
* </p>
*
* #param reason code to pass to the kernel (e.g., "recovery") to
* request special boot modes, or null.
*/
public void reboot(String reason) {
try {
mService.reboot(false, reason, true);
} catch (RemoteException e) {
}
}
UPDATE
If you want the device to completely turn off, use PowerManagerService.shutdown():
IPowerManager powerManager = IPowerManager.Stub.asInterface(
ServiceManager.getService(Context.POWER_SERVICE));
try {
powerManager.shutdown(false, false);
} catch (RemoteException e) {
}
com.android.server.power.PowerManagerService:
/**
* Shuts down the device.
*
* #param confirm If true, shows a shutdown confirmation dialog.
* #param wait If true, this call waits for the shutdown to complete and does not return.
*/
#Override // Binder call
public void shutdown(boolean confirm, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(true, confirm, null, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
This was working fine for me:
startActivity(new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"));
you need this permission ( depends on being system-app ):
<uses-permission android:name="android.permission.SHUTDOWN"/>
source:
https://github.com/sas101/shutdown-android-app/wiki
OK, my mistake.
As I performed some tests, I did not realize that I had removed "android:sharedUserId="android.uid.system" from the manifest.
Once the sharedUserId is included, the following code works without prompting the user to confirm root access:
try {
Process proc = Runtime.getRuntime()
.exec(new String[]{ "su", "-c", "reboot -p" });
proc.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
I tried to remove "su" (because the system may not provide such a command), but in that case it does not work. Surprisingly, the file system is remounted in read-only mode, so I must remount it again with write permissions.
this is for kotlin
(requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager)
.reboot("reason")
I'm writing an android app that sets the max frequency, governor etc.. when the screen turns off. To do it i have a service running that receives screen on/off broadcast intents. When the screen off event fires, I read from the shared preferences and set whatever is set by the user with this function
public static void writeFile(String file, String content) {
try {
Process suProcess = Runtime.getRuntime().exec("su");
DataOutputStream out = new DataOutputStream(suProcess.getOutputStream());
out.writeBytes("echo '" + content + "' > '" + file + "'");
out.flush();
out.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
Then when the screen turns back on, i revert the system to its previous state by rewriting the files. Everything is working nicely, working as intended.
The problem is i'm noticing some lag when turning the screen on/off. Also when i turn the screen on, i see the toast message from SuperUser "App was granted su access", and it pops up once for every command. Is there a way to hide that toast message? I haven't found any way to hide a toast message from another activity. I know they can be disabled in the superuser app, but that's not ideal. I read you can write your own superuser binary but that sounds alot more complicated than simple java programming... also sounds like it could lead to security problems.
Basically i'm asking what's the best way to do this, so that it's as non-invasive to the user as possible?
I haven't used su, but I think if you don't close the stream every time it should only display toast once.
To clarify, I use this code to get superuser permission for my app so I can access root and whatnot:
public String runProcess(String[] functs) {
DataOutputStream dos;
DataInputStream dis;
StringBuffer contents = new StringBuffer("");
String tempInput;
Process process;
try {
process = Runtime.getRuntime().exec(functs);
dos = new DataOutputStream(process.getOutputStream());
dis = new DataInputStream(process.getInputStream());
for (String command : functs) {
dos.writeBytes(command + "\n");
dos.flush();
while ((tempInput = dis.readLine()) != null)
contents.append(tempInput + "\n");
}
dos.writeBytes("exit\n");
dos.flush();
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
return contents.toString();
}
And although it works just fine, whenever I call runProcess(new String[] { "su", "-c", "some other command" }); it always asks for superuser permission. I see a lot of root apps on the market who just have to gain superuser permission once at each startup of the app, but I don't think I'd need to ask the user for superuser permission every single time the app calls an function that requires SU. So my question would be, how would I prompt the user to give me SU permission once at the startup of an app without having to continually ask for it for every SU-related action?
EDIT: I know I could run my method/the Runtime.getRuntime().exec() method without typing "su" in it every time but that only works with non-su related actions (i.e. exec("ps") or exec("ls"). Any ideas?
You can use my Library which does this.
https://code.google.com/p/roottools/
Also, if you don't want to use the library the source is available so you can just rip out my code and use it in your application.
Here is a link to the source:
https://code.google.com/p/roottools/source/browse/#svn%2Ftrunk%2FStable%2FRootTools-sdk3-generic%2Fsrc%2Fcom%2Fstericson%2FRootTools
If you are just looking for the permission from a "superUser" app which is already running in your device, you just need the following code in your main java file.
try {
process p= Runtime.getRuntime().exec(su);
}
catch (IOException e) {
e.printStackTrace();
}
Yes, no need to mention that the device has to be already rooted!!!
Every time you open a console asking for root access, i.e. start it with su, the corresponding super user app will either prompt you or allow/deny it, if you checked something like "Don't ask me again" on the previous prompt.
If you only want to have ask (the super user app) once, you will have to keep your root console open, by not calling dos.writeBytes("exit\n");.
Then keep this session in a background thread and use it when necessary.
So either make sure the user checks "Don't ask me again" on the first prompt or keep the session open.
First thing my app does is checking for "su" since it's necessary for the app to work. Even though it sometimes work, often after typing "killall packageName" in the terminal. I've done a simple test application and I can't get it to work every time.
Code where it happens:
String[] args = new String[] { "su" };
Log.v(TAG, "run(" + Arrays.toString(args) + ")");
FutureTask<Process> task = new FutureTask<Process>(new Callable<Process>() {
#Override
public Process call() throws Exception {
return Runtime.getRuntime().exec(args);
}
});
try {
Executors.newSingleThreadExecutor().execute(task);
return task.get(10, TimeUnit.SECONDS);
} catch (Throwable t) {
task.cancel(true);
throw new IOException("failed to start process within 10 seconds", t);
}
Complete project: https://github.com/chrulri/android_testexec
Since this app does nothing more than running exec() in the first place, I cannot close any previously opened file descriptors as mentioned in another stackoverflow question: https://stackoverflow.com/a/11317150/1145705
PS: I run Android 4.0.3 / 4.0.4 on different devices.
3c71 was right about open file descriptors. In my case, it was the AdMob SDK which caused the problems since it was sometimes (re-)loading the Ads from the web at the sime time I tried to call exec(..) leaving me hanging in a deadlock.
My solution is to fork a "su" process ONCE and reuse it for all commands and load the Ads AFTER forking that process.
To use Runtime.exec safely you should wait for the process to finish and consume the output and error streams, preferably concurrently (to prevent blocking):
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html