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")
Related
I'm working with OTA update for the app. It works like this: I click "check update", it checks it, downloads if exists and save apk on the device. Then I can install it, but of course I have a confirmation dialog. I need to do it silently and restart the app.
I want to make it auto-install when downloading finished. So I just click and if there's some update the app restarts in new version. I can't figure out how to do it. The device is rooted.
The following code works only on roodted devices!
private int installApk(File file) {
if (!file.exists()) throw new IllegalArgumentException();
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]{"su", "-c", "pm install -r " + file
.getAbsolutePath()});
int exitCode = process.waitFor();
if (exitCode != 0) throw new RuntimeException();
} catch (IOException | InterruptedException exception) {
Log.w(getClass().getSimpleName(), exception);
}
return (process == null) ? Integer.MIN_VALUE : process.exitValue();
}
You cannot have this for security reasons. No user app can install silently on non rooted phone.
Only thing you can do is tell user you just downloaded the update and ask if a/he wants to install it. If so, then fire installation intent and have your user proceed with installation.
How to do Android device factory data reset from application level?
I tried to run command with su, but it's not working. I have tried two ways:
1)
public void onClick(DialogInterface dialog, int which) {
try {
Process proc = Runtime.getRuntime().exec(new String[] { "su", "–wipe_data" });
proc.waitFor();
} catch (Exception ex) {}
}
2) when clicking a button, I'm calling device setting application, but I also did't get factory data reset.
startActivityForResult(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS), 0);
How to call settings->storage&reset->factory data reset directly from my app?
You should use the Intent.ACTION_MASTER_CLEAR to factory reset from your application. You might need to have appropriate permissions for your app (system/signed) to do this.
In case you don't have the required permissions / it's not possible for your app to get to the appropriate permission level, as an alternate, using am broadcast -a android.intent.action.MASTER_CLEAR from su shell might work.
I am writing an App that is designed to run on one specific device model (an Android set-top device that runs Amlogic based firmware). I have both root capability and my App is signed with the firmware certificate.
My App is the main focus of the device, and it would be helpful to be able to initiate a complete power-off.
I do not have the shutdown command. I do have the reboot command.
reboot -p does not help. It simply freezes the device while remaining powered on.
The PowerManager is one step better, but it sets the device into sleep mode, instead of a complete shutdown:
PowerManager pm = (PowerManager)getSystemService(Service.POWER_SERVICE);
pm.goToSleep(SystemClock.uptimeMillis());
I am open to all suggestions - hacky or otherwise. The version of Android is expected to remain at 4.2.2.
Intents
This command will cause the device to reboot. Intent.ACTION_SHUTDOWN does not appear to do anything. Is this Intent perhaps only to report a shutdown, and not to initiate one?
Intent i = new Intent(Intent.ACTION_REBOOT);
i.putExtra("nowait", 1);
i.putExtra("interval", 1);
i.putExtra("window", 0);
sendBroadcast(i);
The most luck I had with this was to request a shutdown by Intent:
Intent i = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN");
i.putExtra("android.intent.extra.KEY_CONFIRM", true);
startActivity(i);
Shutdown Thread
That is a bit closer. Definitely interesting. Can you find an example of using it?
So far I have come up with this:
Class<?> sdClass = Class.forName("com.android.server.power.ShutdownThread");
Constructor<?> con = sdClass.getDeclaredConstructors()[0];
con.setAccessible(true);
for (Method m : sdClass.getDeclaredMethods()) {
if (m.getName().matches("shutdown")) {
m.setAccessible(true);
m.invoke(sdClass, PlayerActivity.this, false);
} else if (m.getName().matches("rebootOrShutdown")) {
m.setAccessible(true);
m.invoke(sdClass, PlayerActivity.this, false);
} else if (m.getName().matches("beginShutdownSequence")) {
m.setAccessible(true);
m.invoke(sdClass, PlayerActivity.this, false);
}
}
shutdown and beginShutdownSequence create NullPointerExceptions (do you see why?) and rebootOrShutdown creates an InvocationTargetException due to an UnsatisfiedLinkError... It cannot find a native method:
java.lang.UnsatisfiedLinkError: Native method not found:
com.android.server.power.PowerManagerService.nativeShutdown:()V at
com.android.server.power.PowerManagerService.nativeShutdown(Native
Method) at
com.android.server.power.PowerManagerService.lowLevelShutdown(PowerManagerService.java:2163)
at
com.android.server.power.ShutdownThread.rebootOrShutdown(ShutdownThread.java:543)
at
com.android.server.power.ShutdownThread.run(ShutdownThread.java:393)
lowLevelShutdown is the function that all the functions eventually reach, when configured to shutdown (and not reboot). So figuring out how to avoid this link error may be key.
In my case, I do not think it is possible to shut the device down how I would like to.
The closest that I managed to get to my target was using:
Intent i = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN");
i.putExtra("android.intent.extra.KEY_CONFIRM", true);
startActivity(i);
That brings up a dialog to turn the device off. This is the perfect solution, but in my case, using it causes the device to crash. It may be that my device is somewhat special, and other devices will not have these restrictions.
In any case, I hope that my testing will help others in their quest.
It work for me on rooted device.
If your device is rooted then you can use below approach
Shutdown:
try {
Process proc = Runtime.getRuntime()
.exec(new String[]{ "su", "-c", "reboot -p" });
proc.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
Restart:
Same code, just use "reboot" instead of "reboot -p".
Runtime.getRuntime().exec(new String[]{ "su", "-c", "reboot -p" });
it works, just with rooted devices!!
To use this code, you need Super User! Works on 4.0 and above!
Intent i = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN");
i.putExtra("android.intent.extra.KEY_CONFIRM", false);
i.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
and put this permission on manifest:
<uses-permission android:name="android.permission.SHUTDOWN" />
An update: for newer Android version, in my case is Android 8.1, they changed the action name. See below:
Intent i = new Intent("com.android.internal.intent.action.REQUEST_SHUTDOWN");
i.putExtra("android.intent.extra.KEY_CONFIRM", false);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
Good luck!
In newer android versions you aren't allowed to shut down the device from the nonSystem app.
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.
I was curious if there is any library to work with the capacitive buttons of Samsung phones??
I mean to light them up when an event occurs, or blink them, stuffs like that...
Thanks,
rohitkg
There is nothing in the Android SDK for this, as there is no assumption that such buttons exist, have backlights, etc. You are welcome to contact device manufacturers to see if they have a documented and supported means of doing this for their specific devices.
Here's a code snippet I grabbed from samsung-moment-notifications.
Process process = null;
DataOutputStream os = null;
try {
// get root
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
// write the command
os.writeBytes("echo 100 > /sys/class/leds/button-backlight/brightness\n");
os.writeBytes("exit\n");
// clear the buffer
os.flush();
Toast.makeText(NotificationLights.this, "Lights are on", Toast.LENGTH_SHORT).show();
// wait for complete
process.waitFor();
// won't catch an error with root, but it has to have an exception catcher to execute
} catch (Exception e) {
Toast.makeText(NotificationLights.this, "Couldn't get SU, are you rooted?", Toast.LENGTH_SHORT).show();
return;
}
1- You must have a rooted device.
2- You must know the location of the script which turns the lights on/off for each device.
/sys/class/leds/button-backlight/brightness is specific to the Samsung Moment.
If you were to try it on another device it wouldn't work.