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

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

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

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. Я прочитал, что, подписав ваше приложение сертификатом производителя, вы сможете выполнять привилегированные команды (как root). Однако даже если я устанавливаю свое приложение как системное приложение, подписанное сертификатом производителя, приведенный выше код не работает:

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

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

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

РЕДАКТИРОВАНИЕ

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

5 ответов

Решение

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

ОБНОВИТЬ

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

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

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

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

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

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

это для котлина

      (requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager)
                        .reboot("reason")

ОК, моя ошибка

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

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

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

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

Это старая тема, но она все еще актуальна. Я тестировал это на Android R.

Для приложения, подписанного платформой, вы можете использовать частный SDK посредством отражения.

Во-первых, ваши явные потребности

      <uses-permission android:name="android.permission.REBOOT" tools:ignore="ProtectedPermissions" />

Затем вы можете завершить работу, используя

      private void shutdown() {
    final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    try {
        Method shutdownMethod = PowerManager.class.getMethod("shutdown", boolean.class, String.class, boolean.class);
        boolean confirm = false;     // If true, shows a shutdown confirmation dialog.
        String reason = "shutdown";
        boolean wait = true;         // If true, this call waits for the shutdown to complete and does not return.
        shutdownMethod.invoke(powerManager, confirm, reason, wait);
    } catch (Exception e) {
        Log.e(TAG, "Cannot shutdown", e);
    }
}
Другие вопросы по тегам