Handling Google Prominent Disclosure Guidelines In Android App

Amit Gupta
4 min readMay 19, 2023

--

Hi folks, I have written this article to shed light on the Prominent Disclosure policy which is made mandatory by Google for uploading apps on the play store.

A couple of months ago, Google launched a new policy to handle compliance of Prominent Disclosure dialogs for Android applications.

We connected with the Google team over meet and thanks to Google team to help out to understand what Prominent Disclosure is and how can we accommodate it in our application.

So let me first explain the meaning of Prominent Disclosure, It is an explanation dialog to users on their screen shown before the native android permissions. Before Prominent Disclosure(PD) policy, Android community was familiar with native permission dialogs. Let’s say for storage permissions, once a user clicks on a button where storage permission is required at run time, and if someone denies that native permission dialog, then we show the explanation to users why this permission is important. But after PD, as per Google policy, once a user clicks on a button, we first have to show the explanation to the user and once user accepts it after reading explanation then only native permissions dialog should be opened.

Prominent Disclosure Screen Shot

So as we started working on it in our app and were facing many challenges described below:

1.) Initially every module in the app was using permission in their own way, such as in their activities, fragments , web-view flows, and react-native etc. It was biggest problem for us as we had to change at multiple different places in our app.

Solution: So we made one common utility for the permission which can be used from any module within the app and for every Activity/Fragment/UI component. We have even created a common Bridge for permissions utility Web Flows and our React Native Flows so that if in future Google changes something strange we should not need to change every UI component and all our native/react modules code may intact from the change at one place.

/**
* Returns SnackBar Dto object checking the tag parameter(Please use TAG is used from PermissionSnackBarConstants)
*
*
@param activity target activity
*
@param pSnackBarCallbacks snackbar interface
*
@param permissionGroup TAG for Permission specific Snack bar
*
@param permissions permission for which Snackbar to show
*
@param requestCode Request code for the permission
*
@param pageName PageName for Omniture Tracking
*
@return Returns SnackBar object
*/
public static PermissionSnackBar getSnackBar(Activity activity, PermissionSnackBar.SnackBarCallback pSnackBarCallbacks, String permissionGroup,
String permissions[], int requestCode, boolean isNeverAskAgain, String pageName) {
SnackBarDto.RequestBuilder requestBuilder = new SnackBarDto.RequestBuilder();
requestBuilder.setPermissions(permissions)
.setRequestCode(requestCode);
SnackBarDto snackBarDto = null;
switch (permissionGroup) {

case PermissionConstants.LOCATION_PERMISSION_SNACKBAR:
snackBarDto = getLocationSnackBarObjForPermissionDenied(activity,requestBuilder,isNeverAskAgain);
break;

case PermissionConstants.STORAGE_PERMISSION_SNACKBAR:
snackBarDto = getStorageSnackBarObjForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.SMS_PERMISSION_SNACKBAR:
snackBarDto = getSmsSnackBarObjForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.CONTACTS_PERMISSION_SNACKBAR:
snackBarDto = getContactsSnackBarObjForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.CAMERA_PERMISSION_SNACKBAR:
snackBarDto = getCameraSnackBarObjForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.GENERIC_PERMISSION_SNACKBAR:
snackBarDto = getGenericSnackBarObjForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.AUDIO_PERMISSION_SNACKBAR:
snackBarDto = getRecordAudioSnackBarForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.READ_PHONE_STATE:
snackBarDto = getReadPhoneStateSnackBarForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.CALL_PHONE:
snackBarDto = getCallPhoneSnackBarForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

case PermissionConstants.BLUETOOTH:
case PermissionConstants.BLUETOOTH_ADMIN:
snackBarDto = getBluetoothSnackBarForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
break;

default:
snackBarDto = getGenericSnackBarObjForPermissionDenied(activity, requestBuilder, isNeverAskAgain);
// More Permission Group can be added here by lobs use case
LogUtils.info(TAG, "No Snack bar corresponding to Given Tag");
}
return new PermissionSnackBar(activity, pSnackBarCallbacks, snackBarDto, pageName, permissionGroup, permissions, requestCode);
}

2.) After solving the first problem, we found that our third party libs were also not compliant. Since we are having multiple verticals in our Application and it was very difficult for us to find out which permissions are used by those SDKs and in which flows?

Solution:

We start checking through the merged manifest and checked the whole project for the permission with the help of Android Studio scoped search option. After that we were able to find the SDKs which were using permissions and we sent communications to SDK teams to update their SDK to be compliant.

Some SDKs were updated but still some were missed since there was no Point of contact for those SDKs, then we had to clone their GitHub Projects, which were distributed as open source and published the new modified SDK version. Some private SDKs had to be deleted and alternatives SDKs were integrated.

3.) At some places where app flows was not critical for us and was dependent on third party SDK, we just put check that if there is already permission given by user then we will go inside that flows, otherwise we will move to alternate flows.

4.) For some third party SDK which is particularly required to enter the flow, we just took the permission with PD while entering into the SDK flow instead of permissions being asked inside SDK at some button click.

Conclusion:

After this exercise, we learned these pointers:

1.) We should roll out build on Beta group proactively, and once QA starts testing for sprint tasks so that in review cycle if something happens, we can have time to fix them.

2.) We should write common utilities for the implementation where Privacy Control, Permission Control, Govt. Guidelines and Google Guidelines impact the codebase.

3.) We should use minimal third party dependencies in our codebase, and if required, please choose known Third Party SDKs which keep getting updated.

4.) We should always use bridges for our React native or WebView code which will call common native code where we require permissions and privacy control functionality so that all updates can be handled from one base code.

I Hope this article will be helpful for people who are facing the same problem.

--

--