Выключите Android-устройство с помощью приложения, подписанного системой

Я разрабатываю приложение для Android, и мы должны отключить его при определенных обстоятельствах.

Я читал во многих местах, что для этого вам нужен корневой телефон. Затем вы можете выполнить команду "reboot" с помощью Java API:

try {
    Process proc = Runtime.getRuntime()
                .exec(new String[]{ "su", "-c", "reboot -p" });
    proc.waitFor();
} catch (Exception ex) {
    ex.printStackTrace();
}

Я действительно пробовал это на устройстве Cyanogenmod 10 (Samsung Galaxy S3), и он работает. Однако мы не хотим использовать внедренное устройство, чтобы отключить его, так как конечный пользователь сможет делать непреднамеренные действия, которые не разрешены нашей компанией.

С другой стороны, наше приложение подписывается сертификатом производителя, в данном случае Cyanogen's. Я прочитал, что, подписав ваше приложение с сертификатом производителя, вы должны иметь возможность выдавать привилегированные команды (как root). Однако, даже если я устанавливаю приложение как системное приложение, подписанное с сертификатом производителя, вышеуказанный код не работает:

  • Если я оставлю часть команды su, появится экран "Запрос суперпользователя", но это то, что мы пытаемся избежать.

  • Если я удалю часть "su" (просто оставив "reboot -p" ), команда молча игнорируется.

В результате мы не можем отключить наше устройство с помощью нашего системного приложения, которое подписано с сертификатом разработчика. Итак, мой вопрос: как я должен это делать?

EDITED

И, кстати, на всякий случай кто-то не уверен в этом: приложение правильно подписано и установлено как системное приложение, потому что мы можем фактически получить доступ к некоторым ограниченным API, таким как PowerManager.goToSleep()

Ответ 1

Если вы хотите перезагрузить устройство (выключение и выключение), попробуйте 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

Если вы хотите, чтобы устройство полностью отключилось, используйте 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);
    }
}

Ответ 2

Это работало отлично для меня:

startActivity(new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"));

вам нужно это разрешение (зависит от того, системное приложение):

 <uses-permission android:name="android.permission.SHUTDOWN"/>

Источник: https://github.com/sas101/shutdown-android-app/wiki

Ответ 3

Хорошо, моя ошибка.

Когда я выполнил некоторые тесты, я не понял, что я удалил из манифеста "android: sharedUserId =" android.uid.system.

После включения sharedUserId следующий код работает без запроса пользователя для подтверждения доступа root:

try {
    Process proc = Runtime.getRuntime()
            .exec(new String[]{ "su", "-c", "reboot -p" });
    proc.waitFor();
} catch (Exception ex) {
    ex.printStackTrace();
}

Я попытался удалить "su" (потому что система может не предоставлять такую ​​команду), но в этом случае она не работает. Удивительно, но файловая система перезагружается в режиме только для чтения, поэтому я должен снова установить ее снова с разрешениями на запись.