3

Starting with Android 4.2 , turning on/off airplane mode isn't supported using normal APIs.

It should probably work when WRITE_SECURE_SETTINGS permission is granted, but that's only for system apps (as I've read).

What should be done in order to do it on devices with root?

Should a system app also require root in order to toggle airplane mode?

android developer
  • 114,585
  • 152
  • 739
  • 1,270

1 Answers1

13

To toggle Airplane / Flight mode on and off on an Android rooted device (phone, tablet, note), you can do the following:

private final String COMMAND_FLIGHT_MODE_1 = "settings put global airplane_mode_on";
private final String COMMAND_FLIGHT_MODE_2 = "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state";

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public void setFlightMode(Context context) {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
        // API 17 onwards.
        if (isRooted(context)) {            
            int enabled = isFlightModeEnabled(context) ? 0 : 1;
            // Set Airplane / Flight mode using su commands.
            String command = COMMAND_FLIGHT_MODE_1 + " " + enabled;
            executeCommandWithoutWait(context, "-c", command);
            command = COMMAND_FLIGHT_MODE_2 + " " + enabled;
            executeCommandWithoutWait(context, "-c", command);
        } else {                
            try {
               // No root permission, just show Airplane / Flight mode setting screen.
               Intent intent = new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS);
               intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
               context.startActivity(intent);
            } catch (ActivityNotFoundException e) {
               Log.e(TAG, "Setting screen not found due to: " + e.fillInStackTrace());
            }
        }
    } else {
        // API 16 and earlier.
        boolean enabled = isFlightModeEnabled(context);
        Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, enabled ? 0 : 1);
        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        intent.putExtra("state", !enabled);
        context.sendBroadcast(intent);
    }

To check whether Airplane / Flight mode is already on and off, do the following:

@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private boolean isFlightModeEnabled(Context context) {
    boolean mode = false;
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
        // API 17 onwards 
        mode = Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
    } else {
        // API 16 and earlier.
        mode = Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1;
    }
    return mode;
}

To execute su command, do the following:

private void executeCommandWithoutWait(Context context, String option, String command) {
    boolean success = false;
    String su = "su";
    for (int i=0; i < 3; i++) {
        // "su" command executed successfully.
        if (success) {
            // Stop executing alternative su commands below. 
            break;
        }
        if (i == 1) {
            su = "/system/xbin/su";
        } else if (i == 2) {
            su = "/system/bin/su";
        }       
        try {
            // execute command
            Runtime.getRuntime().exec(new String[]{su, option, command});
        } catch (IOException e) {
            Log.e(TAG, "su command has failed due to: " + e.fillInStackTrace());
        }   
    }
}

Alternatively, if your app:

  1. Was signed with an Android framework's certificate; and
  2. Was installed to the /system/app/ directory; and
  3. Have the relevant tags declared in AndroidManifest.xml file (e.g. WRITE_SECURE_SETTINGS, etc).

then you can just do this:

Settings.Global.putInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, isEnabled ? 0 : 1);

Since anything defined in Settings.Global can be read-write by system apps - even third-party app created as a system app.

ChuongPham
  • 4,761
  • 8
  • 43
  • 53
  • Interesting , thank you. How come using the "grant" for getting a permission won't work ? is it one of the permissions that cannot be granted? also, is this all a workaround which might not work on newer android versions? – android developer Sep 05 '14 at 14:31
  • As of Android 4.2 and later versions, Google have tied down the permission to only be available to system apps, so "grant" won't work. I have already tried this path without any success. The codes I posted worked in most Android 4.3 and 4.4 rooted devices - since I don't have all the Android physical devices running variations of 4.2+ firmwares - I can't claim that it will work on _all_ 4.2+ rooted Android devices. However, to date, none of my users have reported any errors with turning on airplane / flight mode on rooted 4.2+ devices. So, I guess no news is good news! ;) – ChuongPham Sep 05 '14 at 14:38
  • I see. Does it even work on Android L ? How did you get this solution? Also, what is your app? Mine is this one (I don't intend on adding this feature, it's just interesting) : https://play.google.com/store/apps/details?id=com.lb.app_manager – android developer Sep 05 '14 at 16:39
  • So what you've found is more like a breach, no? It might, sooner or later get "fixed", and then you'd need to find something else, right? BTW, I've now installed your app, and I think you should set the targetSdk to the highest possible (as the documentation , Lint and google suggest), plus I think you should change the notification, to include buttons in it, like Widgetsoid has, and allow to choose which toggles to have. Also, I can't find a way to close the app... – android developer Sep 05 '14 at 19:23
  • Since Google has not implemented a "fix" for the last four firmware releases (Android 4.2 to Android L) - I guess it's not. The functionality you mentioned is something I consider for future release of my app. To close the app, you can just use the Back button on your device - since I don't implement an exit option in my app as per recommendation from Hackbod (Android framework engineer) and other senior Android developers who have commented here at SO. – ChuongPham Sep 06 '14 at 06:57
  • I meant closing the app including its notification... About Android, ok. Thank you. – android developer Sep 06 '14 at 09:10
  • you forgot success=true – djdance Oct 20 '16 at 13:31
  • On Android 9 (possibly earlier, but after 6) the root method you mentioned didn't work. However, `su -c settings put global airplane_mode_on 1` followed by `su -c am broadcast -a android.intent.action.AIRPLANE_MODE` works :) – Radu Aug 06 '20 at 22:10