Analyze of permissions

General overview

In Android Permission are staticaly defined in Manifest , and they will be linked and bundled to App in Install time , notice from Android 6 and later  (SDK>=23) Permissions are categorized by type of permission as Normal and Dangerous , for Normal permission , they are granted automatically by system while dangerous require more action from users for example writing on storage is considered as dangerous while Location access are Normal, dangerous are by default disabled when required even if they are defined in manifest by App , and they should be Re-Enabled by user via Device Setting , in this case there are some API such as a callback developer can use in order to get notified when it’s disabled and perform necessary action to invite user to enable that feature.


Overlay window in Android L:

One of the most important changes Google made in Android M (SDK 23), is the strategy to manage permissions , there are more restricted rules Google adopted to make your device more secure , on the other side thanks Google doesn't let developers have some rest in work! many companies have to review thiers apps code to adopt those rules!!

in developer view the permissions setup in manifest are not granted automatically once user confirm installation

in install time!! As an example let me talk about one of the most used feature many apps used in the past and now need to be reviewed or say good bye to it definitley!this feature is the WindowManager which allow creation of overlay window and system popup ...etc which for example allow View to be placed on top of screen over all other windows, this kind of window have been used by many companies including Facebook, Tango ,....etc to place a small window in form of notification on top of screen to inform users of comming message , connection , notification....

it's generally used for messaging applications.Using this feature generally doesn't require a special handling except adding this simple permission to Manifest,

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

but From Android M things are changed , the permission is considered as dangerous by Google new guideline and it's not granted in Run time , you see it when the first time you run app , regardeless permission is added or not , developers should explicitely require user to allow it!

In this case you first check if permission is already granted or not , because as I mentioned this permission is only granted or rejected at the first time you open app :

to check overlay permission call static method canDrawOverlays(context) from Settings class , this class as implemented in settings.java (from SDK 23) has been redefined and you can find many permissions action such as ACTION_MANAGE_OVERLAY_PERMISSION , ACTION_MANAGE_WRITE_SETTINGS …. inside it


suppose we have an Activity or service, that at an appropriate time display a small popup window on top of screen overlaying current active activity.This Activity is responsible to check if Overlay permission required in manifest is granted or not! if so we start a service to manage it and finish Activity, if not we just finish Activity :

Be aware this checking process is supposed to be done on OS >=Android M so we can use this function 


//request code through which we will be later informed by system if permission is granted or not!
static int OVERLAY_PERMISSION_REQUEST_CODE = 0x555;

// function to check if overlay permission is granted
public void checkOverlyPermission() {

        if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.M) {

            if (!Settings.canDrawOverlays(this)) {

                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,

                        Uri.parse("package:" + getPackageName()));

                startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE);//ask system setting for permission

            }

            else {

                startOverlayService();

            }

        }

          else {

            startOverlayService();

        }

    }


 void startOverlayService(){

        Intent intent = new Intent(getBaseContext(),OverlayService.class);//

        intent.setAction("Start OverlayService");

        startService(intent);

        finish();

    }

Then to Handle result of user action for permission:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE) {

            if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.M) {

                if (!Settings.canDrawOverlays(this)) {

                      Toast.makeText(MainActivity.this,"permission not granted",Toast.LENGTH_SHORT).show();

                      confirmOrRetry();

                }

                else {

                    startOverlayService();

                }

            }

        }

    }


if user's still rejecting the permission ,we should be able to retry so in a very simple scenario add two button 'Retry' and 'Close' as below

void confirmOrRetry(){

        setContentView(R.layout.activity_main);

        View vRetry = findViewById(R.id.id_btn_retry);

        View vClose = findViewById(R.id.id_btn_close);

        vRetry.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                checkOverlyPermission();


            }

        });

 vClose.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                MainActivity.this.finish();

            }

        });

    }


This new strategy and behaviour is only applied to an application targetting SDK 23 and later that are supposed to run under Android L , Hopefuly the Android team still let older applications to works as before , if they use a lower SDK version they keep running as before and permissions are granted at install time, for example if you compile the project which we implemented the codes above for SDK 22 we don't even need those consideration !and it works as before.



In order to use a feature which require a specific permission you should check that permission by following function from ContextCompat class:

public static int checkSelfPermission (Context context, String permission)

for example:

int permissionCheck = ContextCompat.checkSelfPermission(context,Manifest.permission.WRITE_CALENDAR);

if(permissionCheck == PERMISSION_DENIED){

//invite user to enable via setting or request the permission

} else if(permissionCheck == PERMISSION_GRANTED){

//other action

}

In order to request a permission in RunTime you can use:

 public static void requestPermissions (Activity activity, String[] permissions, int requestCode)  

for example the following snippet from Android Documentation shows a check/request :


 // Here, thisActivity is the current activity

    if (ContextCompat.checkSelfPermission(thisActivity,

    Manifest.permission.READ_CONTACTS)

    != PackageManager.PERMISSION_GRANTED) {

    

    // Should we show an explanation?

    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,

    Manifest.permission.READ_CONTACTS)) {

    // Show an expanation to the user *asynchronously* -- don't block

    // this thread waiting for the user's response! After the user

    // sees the explanation, try again to request the permission.

    

    } else {

    // No explanation needed, we can request the permission.

    ActivityCompat.requestPermissions(thisActivity,

    new String[]{Manifest.permission.READ_CONTACTS},

    MY_PERMISSIONS_REQUEST_READ_CONTACTS);

    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an

    // app-defined int constant. The callback method gets the

    // result of the request.

    }

    }


And for handling of result :


@Override

    public void onRequestPermissionsResult(int requestCode,

    String permissions[], int[] grantResults) {

    switch (requestCode) {

    case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {

    // If request is cancelled, the result arrays are empty.

    if (grantResults.length > 0

    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

    

    // permission was granted, yay! Do the

    // contacts-related task you need to do.

    

    } else {

    

    // permission denied, boo! Disable the

    // functionality that depends on this permission.

    }

    return;

    }

    

    // other 'case' lines to check for other

    // permissions this app might request

    }

    }



To retrieve the list of permissions required by APP you can :


StringBuffer appNameAndPermissions=new StringBuffer();

PackageManager pm = getPackageManager();

List packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);

for (ApplicationInfo applicationInfo : packages) {

Log.d("test", "App: " + applicationInfo.name + " Package: " + applicationInfo.packageName);

try {

PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName, PackageManager.GET_PERMISSIONS);

              appNameAndPermissions.append(packageInfo.packageName+"*:\n");

 //Get Permissions

String[] requestedPermissions = packageInfo.requestedPermissions;

 if(requestedPermissions != null) {

 for (int i = 0; i < requestedPermissions.length; i++) {

 Log.d("test", requestedPermissions[i]);

appNameAndPermissions.append(requestedPermissions[i]+"\n");

}

 appNameAndPermissions.append("\n");

}

} catch (NameNotFoundException e) {

 e.printStackTrace();

}}

}

 © Xosrov 2016