Settings.canDrawOverlays() allays returns ‘false’ on Android O

I was updating one of my clients app, and testing it how it behaves on Android O (API 26). The app requires permissions to draw over system windows (android.permission.SYSTEM_ALERT_WINDOW). On Android 6 (API 23) and up, you are obligated to request ‘special’ permissions while the app is running.

The ‘SYSTEM_ALERT_WINDOW’ permission is a special permission that breaks the rules set by the new permission model available on Android 6 (API 23) and up. It’s request involves calling Settings.canDrawOverlays() and if it returns ‘false‘ starting ‘Settings‘ where the user can choose to grant your app the ability to draw overlays or not.

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
   // On API 23 and later ask the user to grant us permission to draw system overlay
   // windows.
   if (!Settings.canDrawOverlays(this))
   {
      Intent intent = new Intent(
             Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
             Uri.parse("package:" + getPackageName()));
 
      startActivityForResult(intent, REQUEST_PERMISSION_SYSTEM_OVERLAY_RESULT);
   }
}

There is a bug with Settings.canDrawOverlays() (only) on API 26 where it will always return ‘false’ disregarding the actual user decision. The workaround provided here is a bit ugly, but does not involves restarting the app (which will be quite annoying for the user) after the permission is granted.

The code below first checks the result of System.canDrawOverlays() if it returns ‘true’ it continues with the rest of the application flow. If it returns ‘false’ a check if we are running on Android O (API 26) is performed. If that’s the case, we are calling our ‘workaround’ method.

The ‘workaround’ method tries to add an invisible overlay window on the screen, and if that’s OK we assume that we have a permission to draw overlays, else an exception is thrown.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
 
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; if(requestCode == REQUEST_PERMISSION_SYSTEM_OVERLAY_RESULT) { if(Settings.canDrawOverlays(this)) { m_permissionSystemOverlayWindowGranted = true; if(m_permissionReadPhoneStateGranted && m_permissionProcessOutgoingCallsGranted) { startService(new Intent(this, EstatePlusService.class)); m_layoutNoPermissions.setVisibility(View.INVISIBLE); m_progressBar.setVisibility(View.INVISIBLE); m_layoutLogin.setVisibility(View.VISIBLE); } } else if(Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { // NOTE: This is a workaround to fix the bug in Android O where the // Settings.canDrawOverlays() will always return 'false' if(canDrawOverlays(this)) { m_permissionSystemOverlayWindowGranted = true; if(m_permissionReadPhoneStateGranted && m_permissionProcessOutgoingCallsGranted) { startService(new Intent(this, EstatePlusService.class)); m_layoutNoPermissions.setVisibility(View.INVISIBLE); m_progressBar.setVisibility(View.INVISIBLE); m_layoutLogin.setVisibility(View.VISIBLE); } } } } } /** * Workaround for Android O */ public static boolean canDrawOverlays(Context context) { try { WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); if (windowManager == null) { return false; } final View viewToAdd = new View(context); WindowManager.LayoutParams params = new WindowManager.LayoutParams( 0, 0, android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
                                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
            viewToAdd.setLayoutParams(params);
            windowManager.addView(viewToAdd, params);
            windowManager.removeView(viewToAdd);
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
 
        return false;
    }

Install Oracle JDK on OpenSUSE

Due to licensing issues, OpenSUSE comes with OpenJDK. I personally prefer using Oracle’s JDK. It’s worth mentioning that OpenJDK will not work in some cases such as building Android source code and it’s not recommended for Android development.

Here is how to install and setup Oracle JDK on OpenSUSE.

1. Download the JDK from Oracle’s site. I use 64-bit OpenSUSE so i downloaded the ‘Linux x64‘ version rpm. For 32 bit systems download the ‘i586′ version of the package.

2. Install the JDK by opening a terminal, becoming root and switching to the directory where you downloaded the RPM package.

For x64 version execute:

rpm -i jdk-8u5-linux-x64.rpm

For 32-bit version execute:

rpm -i jdk-8u11-linux-i586.rpm

3. Make the OracleJDK default system JDK.

While at the terminal and with root privileges execute the following sequence of commands:

update-alternatives --install /usr/bin/java java /usr/java/jdk1.8.0_05/bin/java 1551
update-alternatives --install /usr/bin/javadoc javadoc /usr/java/jdk1.8.0_05/bin/javadoc 1551
update-alternatives --install /usr/bin/jar jar /usr/java/jdk1.8.0_05/bin/jar 1551
update-alternatives --install /usr/bin/javap javap /usr/java/jdk1.8.0_05/bin/javap 1551
update-alternatives --install /usr/bin/javac javac /usr/java/jdk1.8.0_05/bin/javac 1551
update-alternatives --install /usr/bin/javaws javaws /usr/java/jdk1.8.0_05/bin/javaws 1551
update-alternatives --install /usr/bin/javah javah /usr/java/jdk1.8.0_05/bin/javah 1551
update-alternatives --install /usr/bin/jarsigner jarsigner /usr/java/jdk1.8.0_05/bin/jarsigner 1551

4. Define JAVA_HOME environment variable.

Type ‘exit‘ at the terminal to become your normal everyday user again. Open .bashrc in your favorite command line text editor and the following:

export JAVA_HOME=/usr/java/jdk1.8.0_05

Save the file and exit from the editor. Type:

source .bashrc

5. Verify Java version by typing ‘java -version‘ it should says “java version “1.8.0_05”“. If that’s the case you have OracleJDK correctly installed.