Android Runtime Permissions
Android runtime permissions list depends on the type of interception:
- Interception due to android permissions (Typically used by telecom apps)
- Interception due to Push+IVR/SIP solution (For non-dialer apps)
Interception due to android permissions
Typically used by telecom apps. Requires default Phone/Assistant role.
If your application is built for android API level 23 and above, it is necessary to ask the user for permissions in order for DMA to start intercepting. This is because DMA uses "dangerous" permissions like: android.permission.READ_PHONE_STATE and android.permission.PROCESS_OUTGOING_CALLS.
Since android API level 28 there are few more android permissions required in order to DMA SDK be able to finish a call: android.permission.ANSWER_PHONE_CALLS and android.permission.READ_CALL_LOG.
There is a runtime permission that was introduced since android API 33 and it is required to be requested by your application: android.permission.POST_NOTIFICATIONS.
Due to the BAL restriction on Android 15, the "Draw over other app" permission is required to launch the profile.
Another approach is to post the notification with intent to open the profile - this requires POST_NOTIFICATION permission. To override the title and the text on the default notification, define next string resources on the app level (in app's strings.xml):
<string name="dma_intercept_notification_title">Your title</string>
<string name="dma_intercept_notification_text">Your text</string>
The application has to ask for the permission group Phone to make DMA able to receive events. To do this, in one of the setup activities (or some start activity) ask for the permissions as in the example code below:
final List<String> permissionsList = new ArrayList<>();
permissionsList.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
permissionsList.add(Manifest.permission.CALL_PHONE);
permissionsList.add(Manifest.permission.READ_PHONE_STATE);
if (Build.VERSION.SDK_INT >= 28) {
permissionsList.add(Manifest.permission.ANSWER_PHONE_CALLS);
permissionsList.add(Manifest.permission.READ_CALL_LOG);
}
if (Build.VERSION.SDK_INT >= 33) {
permissionsList.add(Manifest.permission.POST_NOTIFICATIONS);
}
ActivityCompat.requestPermissions(
this.activity,
permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
val permissionsList = mutableListOf(
Manifest.permission.PROCESS_OUTGOING_CALLS,
Manifest.permission.CALL_PHONE,
Manifest.permission.READ_PHONE_STATE
)
if (Build.VERSION.SDK_INT >= 28) {
permissionsList.add(Manifest.permission.ANSWER_PHONE_CALLS)
permissionsList.add(Manifest.permission.READ_CALL_LOG)
}
if (Build.VERSION.SDK_INT >= 33) {
permissionsList.add(Manifest.permission.POST_NOTIFICATIONS)
}
ActivityCompat.requestPermissions(
this.activity,
permissionsList.toTypedArray(),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)
And override the "onRequestPermissionsResult" method:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode){
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:{
requestOverlayPermission();
}
}
}
As the DMA library is not involved in the usual workflow of the application, the activity inside the application should ask for this permission. It is recommended to show the rationale dialog, explaining why the application needs this permission before showing the standard android requestPermission dialog.
Interception due to Push+IVR/SIP solution
For non-dialer apps. No sensitive call permissions required.
For those applications, that is not doing interceptions from APK, but uses Push+IVR/SIP integration flow for this, it is enough to request following permissions:
android.permission.CALL_PHONE // Allows the user to initiate a call (e.g., via the "Continue Call" button in the menu)
android.permission.READ_PHONE_STATE // Used to detect call state transitions or SIM-related behavior
New runtime permission that was introduced since android API 33 and it is required to show a notification that opens the menu when triggered via IVR/SIP:
android.permission.POST_NOTIFICATIONS // Required to show a notification that opens the menu when triggered via IVR/SIP
To display the profile/menu from the background when the IVR/SIP-triggered push is received, the SDK uses one of the following mechanisms:
-
Draw Over Other Apps
- Requires: SYSTEM_ALERT_WINDOW (also known as "Draw over other apps")
- Allows the SDK to automatically display the profile as an overlay
-
Notification Fallback
- Requires: POST_NOTIFICATIONS
- SDK posts a notification with an intent to launch the profile when tapped
Ask for the permissions as in the example code below:
final List<String> permissionsList = new ArrayList<>();
permissionsList.add(Manifest.permission.CALL_PHONE);
permissionsList.add(Manifest.permission.READ_PHONE_STATE);
if (Build.VERSION.SDK_INT >= 33)
permissionsList.add(Manifest.permission.POST_NOTIFICATIONS);
ActivityCompat.requestPermissions(
this.activity,
permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
val permissionsList = mutableListOf(
Manifest.permission.CALL_PHONE,
Manifest.permission.READ_PHONE_STATE
)
if (Build.VERSION.SDK_INT >= 33) {
permissionsList.add(Manifest.permission.POST_NOTIFICATIONS)
}
ActivityCompat.requestPermissions(
this.activity,
permissionsList.toTypedArray(),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)
And override the "onRequestPermissionsResult" method:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode){
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:{
requestOverlayPermission();
}
}
}
As the DMA library is not involved in the usual workflow of the application, the activity inside the application should ask for this permission. It is recommended to show the rationale dialog, explaining why the application needs this permission before showing the standard android requestPermission dialog.
CallRedirectionService
The CallRedirectionService empowers applications to manage outgoing calls by enabling actions such as redirection, cancellation, or initiation before the call starts connecting. This powerful functionality is now integrated into the DMA SDK.
Designed for devices running Android 10 and above, the CallRedirectionService provides flexible options for handling outgoing calls.
Flexible Implementation Options
You can implement the service in two ways:
- Hybrid Approach: Combine the CallRedirectionService with the legacy interception method based on
NEW_OUTGOING_CALLevent. - Standalone Use: Use only the CallRedirectionService for managing outgoing calls.
In a hybrid setup, the service prioritizes CallRedirectionService if the user has granted the required permissions. If permissions are not available, the legacy NEW_OUTGOING_CALL mechanism will serve as a fallback.
This integration provides robust and versatile control over outgoing call behavior, ensuring seamless compatibility with modern Android versions.
We suggest the use of a hybrid approach in case of Telecom's applications in order to cover most of the customers.
Request the ROLE_CALL_REDIRECTION
To use the CallRedirectionService API and intercept outgoing calls (on Android 10+), your app must be granted the ROLE_CALL_REDIRECTION role by the user.
You should check whether your app already holds the role using:
If the role is granted, continue the normal flow of the application. If not, Use the RoleManager API to prompt the user to grant the role. See the code below:
private final static int ROLE_REDIRECTION_CODE = 91;
private void requestRedirectionRole() {
if (!hasRedirectionRole()) {
RoleManager roleManager = getSystemService(RoleManager.class);
Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION);
startActivityForResult(intent, ROLE_REDIRECTION_CODE);
} else {
//go to request screening role or android permissions
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ROLE_REDIRECTION_CODE) {
if (Activity.RESULT_OK == resultCode) {
//request next android permissions
} else {
//show rationale with button to redirect user to Default phone app settings to user set it manually
}
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public static void openDefaultAppsSettings(Context ctx) {
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
ctx.startActivity(intent);
}
companion object {
private const val ROLE_REDIRECTION_CODE = 91
@RequiresApi(Build.VERSION_CODES.N)
fun openDefaultAppsSettings(ctx: Context) {
val intent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
ctx.startActivity(intent)
}
}
private fun requestRedirectionRole() {
if (!hasRedirectionRole()) {
val roleManager = getSystemService(RoleManager::class.java)
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION)
startActivityForResult(intent, ROLE_REDIRECTION_CODE)
} else {
//go to request screening role or android permissions
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ROLE_REDIRECTION_CODE) {
if (resultCode == Activity.RESULT_OK) {
//request next android permissions
} else {
//show rationale with button to redirect user to Default phone app settings to user set it manually
}
}
}
This will launch a system UI asking the user to allow your app to intercept outgoing calls via redirection.

System popup to grant the REDIRECTION ROLE to the app.
Best Practices for Requesting ROLE_CALL_REDIRECTION
Since the permissions request process is done by the application side, we recommend:
-
Request the Role First
- Prompt the user to grant the ROLE_CALL_REDIRECTION before requesting other Android permissions (like CALL_PHONE).
- This ensures the user understands the role's importance independently of standard permission prompts.
-
Show a Rationale Dialog First
- Before launching the system role request UI, show a brief in-app dialog explaining:
- What the role is for
- Why the app needs it (e.g. "to manage outgoing calls and provide enhanced call options")
- This improves user acceptance and transparency.
- Before launching the system role request UI, show a brief in-app dialog explaining:
-
Request the Role for All Users
- The role prompt should be shown regardless of whether the user has granted call-related Android permissions.
- This enables support for hybrid integration models (e.g., Redirection + Push or existing users of the app which updated to the new version).
-
Don't Skip Other Permissions
- Even if the user grants the role, you must still request runtime permissions (e.g., CALL_PHONE, POST_NOTIFICATIONS) as needed by your SDK's mode.
- The role does not replace the permission system.
Xiaomi-Specific Behavior: Notification as Fallback for UI Launch
On Xiaomi (MIUI) devices, the system may block the SDK from opening the profile screen from the background, even if SYSTEM_ALERT_WINDOW (overlay permission) is granted.
Due to Xiaomi's strict background activity restrictions, the SDK automatically posts a notification during call interception as a fallback mechanism.
How the Notification Works:
When interception is triggered and the UI cannot be launched directly, the SDK posts a notification containing:
- Title: App name (by default)
- Subtitle: Instructional message (e.g., "Tap to open menu")
The user can tap the notification to open the profile screen manually.
Customizing the Notification Text
You can customize the title and subtitle of this fallback notification by defining two string resources in your app. Project Structure:
MyAndroidProject/
├─ app/
│ ├─ src/
│ │ ├─ main/
│ │ │ ├─ java/
│ │ │ │ └─ ...
│ │ │ ├─ res/
│ │ │ │ └─ values/
│ │ │ │ └─ strings.xml
│ │ │ └─ AndroidManifest.xml
...
Add the following to strings.xml:
<string name="call_redirecation_force_drop_notification_title">New title text</string>
<string name="call_redirecation_force_drop_notification_text">New subtitle text</string>
Replace:
- "New title text" with your preferred notification title
- "New subtitle text" with user-friendly instructions (e.g., "Tap to view DMA menu")
Example:
<string name="call_redirecation_force_drop_notification_title">Call Assistant</string>
<string name="call_redirecation_force_drop_notification_text">Tap to view DMA menu</string>
CallScreeningService
The CallScreeningService allows applications to control incoming calls by enabling actions such as redirecting, blocking, or handling them before they connect. This powerful functionality is now integrated into the DMA SDK.
Designed for devices running Android 10 and above, the CallScreeningService provides flexible options for handling incoming calls.
Flexible Implementation Options
You can implement the service in two ways:
- Hybrid Approach: Combine the CallScreeningService with the legacy interception method based on
READ_PHONE_STATEevents. - Standalone Use: Use only the CallScreeningService for managing incoming calls.
In comparison with the legacy incoming interception mechanism based on READ_PHONE_STATE events, CallScreeningService is triggered earlier.
We suggest the use of a hybrid approach in case of Telecom's applications in order to cover most of the customers.
Request the ROLE_CALL_SCREENING
To use CallScreeningService (for incoming call screening), your app must be explicitly granted the ROLE_CALL_SCREENING by the user. This is a system-level role introduced in Android 10 (API 29).
The process includes checking for the role, prompting the user.
At app startup (either before or after requesting other permissions), check if your app already holds the role:
If the Role is granted, continue the normal flow of the application. If not, proceed to the step of requesting the role with the code below:
private final static int ROLE_SCREENING_CODE = 91;
private void requestScreeningRole() {
if (!hasScreeningRole()) {
RoleManager roleManager = getSystemService(RoleManager.class);
Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING);
startActivityForResult(intent, ROLE_SCREENING_CODE);
} else {
//request android permissions
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ROLE_SCREENING_CODE) {
if (Activity.RESULT_OK == resultCode) {
//request next android permissions
} else {
//show rationale with a button to redirect user to Default phone app settings to user set it manually. When the user returns to the app, go to request the next android permissions.
}
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public static void openDefaultAppsSettings(Context ctx) {
Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
ctx.startActivity(intent);
}
companion object {
private const val ROLE_SCREENING_CODE = 91
@RequiresApi(Build.VERSION_CODES.N)
fun openDefaultAppsSettings(ctx: Context) {
val intent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
ctx.startActivity(intent)
}
}
private fun requestScreeningRole() {
if (!hasScreeningRole()) {
val roleManager = getSystemService(RoleManager::class.java)
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING)
startActivityForResult(intent, ROLE_SCREENING_CODE)
} else {
//request android permissions
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ROLE_SCREENING_CODE) {
if (resultCode == Activity.RESULT_OK) {
//request next android permissions
} else {
//show rationale with a button to redirect user to Default phone app settings to user set it manually. When the user returns to the app, go to request the next android permissions.
}
}
}
For setup instructions and best practices, see:
- Best Practices for Requesting ROLE_CALL_REDIRECTION
- Xiaomi-Specific Behavior: Notification as Fallback for UI Launch
Replace appropriate for CallScreeningService flow notification title and text resources:
<string name="call_screening_force_drop_notification_title">New title text</string>
<string name="call_screening_force_drop_notification_text">New subtitle text</string>
Comparison: CallScreening and CallRedirection services
CallScreeningService can prevent (reject) incoming calls, but it cannot prevent outgoing calls.
Why?
- For incoming calls: CallScreeningService has the CallResponse.Builder#setDisallowCall() method, which allows rejecting (blocking) an incoming call before the user sees it.
- For outgoing calls: CallScreeningService can only observe outgoing calls but cannot block or modify them.
Official Documentation Reference
According to the Android Developer Docs:
"CallScreeningService allows a package to perform call screening for incoming calls, including rejecting calls. However, it cannot block outgoing calls."
If you need to block outgoing calls, you must use CallRedirectionService, which has the ability to either:
- Redirect an outgoing call to a different number.
- Cancel the outgoing call entirely.
Summary
| Action | CallScreeningService | CallRedirectionService |
|---|---|---|
| Detect incoming calls | Yes | No |
| Block (reject) incoming calls | Yes | No |
| Detect outgoing calls | Yes | Yes |
| Block outgoing calls | No | Yes |
| Redirect outgoing calls | No | Yes |
Draw over other apps (SYSTEM_ALERT_WINDOW)
In order to implement Push+IVR/SIP mechanism in a project, please strongly consider requesting "Draw over other apps" permission for your app, as there is a restriction since android 10, which forbids launching profiles when the app is closed.
Update: Due to the BAL restriction, starting Android 15, the permission is required to launch the profile. Another approach is to post the notification with intent to open the profile - this requires POST_NOTIFICATION permission.
In case if the permission is not granted it is only possible to post a notification and by clicking on it a profile will be opened.
It is required to declare permission in "AndroidManifest.xml":
Example of requesting Overlay:
/**
* check is permission granted
*/
public boolean isOverlayAllowed() {
boolean isAllowed;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
isAllowed = Settings.canDrawOverlays(this);
} else {
isAllowed = true;
}
return isAllowed;
}
/**
* request permission here
*/
public void requestDrawOverlayPermission() {
if (Build.VERSION.SDK_INT >= 23) {
Intent intent = new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName())
);
startActivityForResult(intent, OVERLAY_REQUEST_CODE);
}
}
/**
* handle callback of request in this method
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == OVERLAY_REQUEST_CODE)
if (isOverlayAllowed())
goToNextPermission();
}
private const val OVERLAY_REQUEST_CODE = 12
/**
* check is permission granted
*/
fun isOverlayAllowed(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
Settings.canDrawOverlays(this)
else
true
}
/**
* request permission here
*/
fun requestDrawOverlayPermission() {
if (Build.VERSION.SDK_INT >= 23) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
startActivityForResult(intent, OVERLAY_REQUEST_CODE)
}
}
/**
* handle callback of request in this method
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == OVERLAY_REQUEST_CODE)
if (isOverlayAllowed)
requestAutoStartPermission()
}
Autostart permission
Some Android devices which are made by such manufacturers like "Xiaomi", "Huawei", "Oppo", "Vivo", "Letv", etc. have some politics about Power Management that breaks DMA core feature call interception.
Our suggestion is to ask users to grant "Autostart" permission.
The permission might be turned on manually on the application settings page (see figure on the right) where the user needs to find the desirable app and enable the permission manually.

The permission cannot be checked programmatically whether the permissions are granted. So, it means that that permission must be requested only once. Information about if that permission has ever been requested before can be stored in app memory (SharedPreferences e.g.). Save it right before the request.
The DMA library provides "AutoStartHelper" class to handle it in the case that requesting this permission is necessary.
Example of requesting AutoStart:
private static final int AUTOSTART_REQUEST_CODE = 13;
private void requestAutoStartPermission() {
if (AutoStartHelper.isAutoStartDetected(this)
&& !AutoStartHelper.hasEverRequested(this)) {
AutoStartHelper.requestAutoStartWithOnResult(this, AUTOSTART_REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (AUTOSTART_REQUEST_CODE == requestCode) {
requestXiaomiPermission();
}
}
companion object {
private const val AUTOSTART_REQUEST_CODE = 13
}
private fun requestAutoStartPermission() {
if (AutoStartHelper.isAutoStartDetected(this)
&& !AutoStartHelper.hasEverRequested(this)) {
AutoStartHelper.requestAutoStartWithOnResult(this, AUTOSTART_REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (AUTOSTART_REQUEST_CODE == requestCode) {
requestXiaomiPermission()
}
}
About "AutoStartHelper" class:
- AutoStartHelper.isAutoStartDetected(context) - method helps to detect whether a device is required to request AutoStart permission.
- AutoStartHelper.simpleRequestAutoStart(context) and AutoStartHelper.requestAutoStartWithOnResult(activity, requestCode) - methods open app's settings page with the intention to ask the user to grant AutoStart permission. Save a flag about success opening such settings page (relates to next method).
- AutoStartHelper.hasEverRequested(context) - checks whether the AutoStart settings page has ever opened with the intention to ask the user to grant permission (by previous method).
Xiaomi permissions
There are restrictions from Xiaomi devices with MIUI OS wrapper and Android API above 25. On such devices it is forbidden to launch a page from the background (from Service e.g.).
If your app runs on Xiaomi devices you have to ask the user to grant "Display pop-up windows while running in the background" (on old versions of the MIUI it is called "Run in the background").
In order to proper app's work (call interception and showing a profile) in case of an incoming call, please ask a user to grant "Show on Lock screen" permission. Without this permission Xiaomi blocks the appearance of a profile after the device awakens from sleep mode.
Requesting Xiaomi permissions is possible only by opening the specific settings page of the app with the intention to ask users to grant them.
Unfortunately there is no way to check programmatically whether these permissions are granted.
So, we recommend showing users a message with clear description which permissions are necessary to grant for proper work of the app, before opening the Xiaomi settings page.
These permissions are related to MIUI (MIUI is an Android-based operating system for Mi and Redmi phones). And you can find it on the "App info/Setting" page of the app, on the "Other permissions" page.

Example of requesting Xiaomi permissions:
"XiaomiUtils" class provides a set of helper functions related to these xiaomi restrictions. To start using that class please invoke XiaomiUtils.newInstance(context). An instance initializes for only xiaomi devices with MIUI OS and android API level 25 and above. In any other cases an instance might be NULL.
To open Xiaomi "Other permissions" setting page programmatically please invoke XiaomiUtils.queryXiaomiOtherPermissions(context, boolean showOpenToast) function on the very first app launch or in function which triggers when app updates (onPackageReplaced(), e.g.).
Xiaomi "Other permissions" block should be called once. To prevent opening on every app launch use XiaomiUtils.hasEverRequestedManually(context) function to check whether there were attempts to launch the specified settings page before.
Or use a custom way - save information about if it was ever called (for example in Shared Preferences) and check before next requesting whether this value is present, it should not be called again.
- "showOpenToast" - arg provides the possibility to show a toast message with some additional information for the user. To set your own message please override the string resource with name "xiaomi_other_perms_toast" (R.string.xiaomi_other_perms_toast).
DMA provides a mechanism of automatic detection whether the Xiaomi permission isn't granted. During the call, when the call doesn't get interrupted because of the Xiaomi restriction. In this case the user might be displayed a notification (see the figures). To enable that feature please call the function: XiaomiUtils.allowDisplayRestrictionDetectedNotification(context).
On Xiaomi with Android 10 posting of the notification requires adding "USE_FULL_SCREEN_INTENT" permission to manifest:
To change the text and icon on the notification please override resources with next names:
-
In strings:
- "notification_backround_restriction_rationale_title"
- "notification_backround_restriction_rationale"
-
In drawables:
- "xiaomi_restriction_notification_small_icon"
- "xiaomi_restriction_notification_large_icon" - (must be png format)
There are a few other helpful functions included in "XiaomiUtils" class. Description is provided.
Please feel free to check the DialMyApp application in order to see one of the possible ways to request all permissions. DialMyApp - Apps on Google Play
Example of requesting all permissions
There are a few types of permissions which are necessary to request from the user:
-
Android "dangerous" permissions - requesting in runtime (since API 23) by displaying the dialog to users with buttons "allow" and "deny". Might be checked.
-
"Draw over other apps" permission - also known as "Overlay" permission - requesting by opening a separated page of app settings (provided by android system) with the intention to ask the user to enable it manually. Required since API 29.
Please notice that since Android 11 it's possible to open a settings page only with a list of apps where the user needs to find the desirable app and enable the permission manually. Before Android 11, setting for the specific app opened automatically.
-
"Autostart" permission - requesting by opening a separated page of app settings (provided by android system) with a list of apps where the user needs to find the desirable app and enable the permission manually. Required to be requested on such devices as "Xiaomi", "Huawei", "Oppo", "Vivo", "Letv", etc.
Cannot be checked programmatically whether the permission is granted. So, it means that that permission must be requested only once. Information about if that permission has ever been requested before can be stored in app memory (SharedPreferences e.g.). Save it right before the request.
-
"Display pop-up window while running in the background" - relates to "Xiaomi" devices only. Requesting by opening "Other permission" the apps settings page - user has to grant it manually.
Cannot be checked programmatically whether it is granted. It doesn't return a callback in case of using "startActivityForResult".
Requesting permissions sequence
- Start from requesting Android "dangerous" permissions in runtime on the main page;
class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 11;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] permissions = {PROCESS_OUTGOING_CALLS, CALL_PHONE, READ_PHONE_STATE,...}; // permissions list depend on type of interception
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (REQUEST_CODE == requestCode)
if (arePermissionsGranted())
requestOverlayPermission();
}
...
}
class MainActivity : AppCompatActivity() {
companion object {
private const val REQUEST_CODE = 11
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val permissions = arrayOf(PROCESS_OUTGOING_CALLS, CALL_PHONE, READ_PHONE_STATE) // permissions list depend on type of interception
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (REQUEST_CODE == requestCode)
if (arePermissionsGranted())
requestOverlayPermission()
}
...
}
- Continue requesting permission sequence on the request android permissions callback "onRequestPermissionsResult()" -> query "Overlay" permission.
private static final int OVERLAY_REQUEST_CODE = 12;
private void requestOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !isOverlayGranted()) {
Intent intent = new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName"));
startActivityForResult(intent, OVERLAY_REQUEST_CODE);
}
}
private boolean isOverlayGranted() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
return Settings.canDrawOverlays(this);
else
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (OVERLAY_REQUEST_CODE == requestCode) {
requestAutoStartPermission();
}
}
companion object {
private const val OVERLAY_REQUEST_CODE = 12
}
private fun requestOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !isOverlayGranted()) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName"))
startActivityForResult(intent, OVERLAY_REQUEST_CODE)
}
}
private fun isOverlayGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
Settings.canDrawOverlays(this)
else
true
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (OVERLAY_REQUEST_CODE == requestCode) {
requestAutoStartPermission()
}
}
- Next step is -> request the "Autostart" permission on the "Overlay" callback ("onActivityResult()"). DMA provides an "AutoStartHelper" class to handle that request.
private static final int AUTOSTART_REQUEST_CODE = 13;
private void requestAutoStartPermission() {
if (AutoStartHelper.isAutoStartDetected(this)
&& !AutoStartHelper.hasEverRequested(this)) {
AutoStartHelper.requestAutoStartWithOnResult(this,AUTOSTART_REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (AUTOSTART_REQUEST_CODE == requestCode) {
requestXiaomiPermission();
}
}
companion object {
private const val AUTOSTART_REQUEST_CODE = 13
}
private fun requestAutoStartPermission() {
if (AutoStartHelper.isAutoStartDetected(this)
&& !AutoStartHelper.hasEverRequested(this)) {
AutoStartHelper.requestAutoStartWithOnResult(this, AUTOSTART_REQUEST_CODE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (AUTOSTART_REQUEST_CODE == requestCode) {
requestXiaomiPermission()
}
}
- On the "AutoStart" callback -> check and request "Display pop-up window while running in the background" permission. Please note that this request doesn't return a callback in onActivityResult() method -> so, handle this request always as the latest in the sequence of requesting permissions.
Rationale dialog
Recommendation: there is a way to improve awareness of users why the app needs some permission - display a pop-up with the description message ("Our application is required to grant "Overlay" permission to use all functionality" e.g.). You can achieve this by displaying "Yes/No pop-up" before each permission request. For example:
private void requestAutoStartPermission() {
AlertDialog dialog = new AlertDialog.Builder(this)
.setMessage("Dear user, our app is required to grant AutoStart permission. " +
"\nClick OK to proceed...")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (AutoStartHelper.isAutoStartDetected(this)
&& !AutoStartHelper.hasEverRequested(this)) {
AutoStartHelper.requestAutoStartWithOnResult(this, AUTOSTART_REQUEST_CODE);
}
}
})
.setCancelable(false)
.create();
dialog.show();
}
private fun requestAutoStartPermission() {
val dialog = AlertDialog.Builder(this)
.setMessage("Dear user, our app is required to grant AutoStart permission. " +
"\nClick OK to proceed...")
.setPositiveButton("OK") { _, _ ->
if (AutoStartHelper.isAutoStartDetected(this)
&& !AutoStartHelper.hasEverRequested(this)) {
AutoStartHelper.requestAutoStartWithOnResult(this, AUTOSTART_REQUEST_CODE)
}
}
.setCancelable(false)
.create()
dialog.show()
}
Frequently asked questions
-
How to prevent requesting additional permissions every app launch?
- "AutoStart", "Xiaomi" -> After at least once launch of the app settings page with intention to request permission manually, the library saves a flag into the cache. Use "hasEverRequested" - to check it.
- "Overlay" -> android provides a method Settings.canDrawOverlays(context) (Required Android M) to define whether its permission is granted.
-
How to check whether the "AutoStart" or "Display pop-up window while running in the background" permission is granted?
- At the moment there is no way to check it. We are working on a solution. Please note that each of those permissions must be requested at least once (if device is required it).