Other Changes of Note

There are lots of other changes in Android 11, far more than can be presented in this book. This chapter covers a variety of additional changes that you may want to pay attention to.

Stuff That Might Break You

The scariest batch of changes in any Android release are the ones that may break existing app behavior. Things like package visibility might qualify for that.

Here are a few other smaller changes that may cause problems for reasonably-ordinary apps.

Dismissable Ongoing Notifications

For your foreground services, you may be used to raising “ongoing” notifications. These normally are not dismissable by the user.

However, in Android 11, they are.

On the plus side, this does not appear to affect your process importance. You are still registered as having a foreground service, even if the user gets rid of your notification.

However, if you were used to that notification always being there to give the user control over that background work… now the user might remove that notification, intentionally or accidentally. Make sure that your app can still function reasonably, from a UX standpoint, if the user dismisses your notification.

Phone Number Permissions

Some Android SDK methods let you attempt to get the phone number of the device, such as getLine1Number() on TelephonyManager. In practice, these are not very reliable, but you are welcome to try to use them.

For years, in order to call those methods, you needed the READ_PHONE_STATE permission. This is a dangerous permission, and you needed to use runtime permissions to further request it on Android 6.0+.

In Android 11, once your targetSdkVersion reaches 30, you need to request a different permission for those methods: READ_PHONE_NUMBERS. This too is a dangerous permission.

As a result, you need to decide which of those permissions you need based on API level, and include the correct permission in the array that you pass to requestPermissions().

If the only reason you were requesting READ_PHONE_STATE was to use these methods, you may find that you no longer need it on Android 11+ devices. If so, you could elect to add android:maxSdkVersion="29" to your <uses-permission> element for READ_PHONE_STATE to drop it off for API Level 30 and higher devices:

<uses-permission android:name="READ_PHONE_STATE" android:maxSdkVersion="29" />

Conversely, if you are using READ_PHONE_STATE for other things, you will need to request both READ_PHONE_STATE and READ_PHONE_NUMBERS on API Level 30 devices, but only READ_PHONE_STATE on older devices.

Overlay Tweak

Hopefully, you are not using the SYSTEM_ALERT_WINDOW permission in your app. If you are, then you probably have noticed that Google is continuing to “tighten the screws” with each passing release. While it is possible that they will never outright ban SYSTEM_ALERT_WINDOW, they certainly seem intent upon making it more aggravating for developers and users.

In Android 11, the change is to ACTION_MANAGE_OVERLAY_PERMISSION.

SYSTEM_ALERT_WINDOW is one of those special permissions that does not go through the standard dangerous runtime permission system. Rather, the user needs to go into the “Special app access” section of the Apps screen in Settings, and from there go into “Display over other apps”. There, the user can tap on an app that is requesting SYSTEM_ALERT_WINDOW and elect to grant or reject that permission for that app.

ACTION_MANAGE_OVERLAY_PERMISSION is an Intent action that allows you to send the user to this place in Settings. In Android 6.0 through 10, there were two ways to use this Intent:

In Android 11, that latter option is no longer available. You can provide the Uri, but it will be ignored by the Settings app. The user is always taken to the “Display over other apps” screen, where the user will need to click on your app, then grant the permission.

No Third-Party Image Capture Support

ACTION_IMAGE_CAPTURE is a popular means for an app to take a picture, by asking a camera app to do the “heavy lifting”. This means that the app can skip all of the headache of setting up a camera itself (e.g., via the CameraX library) and does not need the CAMERA permission. There are also ACTION_IMAGE_CAPTURE_SECURE and ACTION_VIDEO_CAPTURE Intent actions that perform similar operations.

The problem is that pre-installed camera apps often do not test these actions much, so they tend to have bugs. In lieu of dumping these actions and doing the camera work directly in the app, many apps simply guide the user to install a third-party camera app, one that is known to have a good implementation of things like ACTION_IMAGE_CAPTURE. Then, when the app invokes the ACTION_IMAGE_CAPTURE Intent, the user could choose the third-party camera app in the chooser.

That is no longer an option on Android 11, once your targetSdkVersion reaches 30.

Those three Intent actions will only start a pre-installed camera app. User-installed camera apps are ignored. Even if the user has disabled all pre-installed camera apps, user-installed camera apps are still ignored — Android throws an ActivityNotFoundException instead.

At minimum, if you are using these Intent actions, you will need to handle the ActivityNotFoundException scenario. You really needed that anyway, as work policies or similar constraints might have caused that Intent to fail anyway.

Even if you try enabling package visibility for your desired Intent action, you will find that third-party apps are ignored.

An explicit Intent works for the startActivity()/startActivityForResult() call, if you happen to know of a camera app to try (e.g., net.sourceforge.opencamera for Open Camera). This also works with queryIntentActivities() on PackageManager, if you also enable package visibility, so you can determine whether the Intent would succeed or not before trying to start the activity.

The CamChooser sample module in the book’s sample project demonstrates a related approach: adding candidate camera apps to a chooser:

package com.commonsware.android.r.camchooser

import android.content.Context
import android.content.Intent


private val CAMERA_CANDIDATES = listOf(
  "net.sourceforge.opencamera"
)

fun enhanceCameraIntent(
  context: Context,
  baseIntent: Intent,
  title: String
): Intent {
  val pm = context.packageManager

  val cameraIntents =
    CAMERA_CANDIDATES.map { Intent(baseIntent).setPackage(it) }
      .filter { pm.queryIntentActivities(it, 0).isNotEmpty() }
      .toTypedArray()

  return if (cameraIntents.isEmpty()) {
    baseIntent
  } else {
    Intent
      .createChooser(baseIntent, title)
      .putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents)
  }
}

The enhanceCameraIntent() function will sift through the application IDs from CAMERA_CANDIDATES and see if any appear to exist and support some Intent action (e.g., ACTION_IMAGE_CAPTURE). If there are some, they are attached to Intent.createChooser() via EXTRA_INITIAL_INTENTS. You can then use the returned Intent with startActivityForResult(). The result is:

So, you still need to worry about ActivityNotFoundException, but that was always the case with these Intent actions. The user might be running in a restricted profile and lack access to any camera apps, for example.

This sample only shows one candidate camera app, that being Open Camera. Enterprising developers might create a broader list of candidate apps that could be detected and used. The overall CamChooser sample app implements a testbed, to allow you to see whether these media capture Intent actions appear to work properly for your selected camera app. The checks are rudimentary, mostly confirming that we did not crash and did get the expected result (e.g., Bitmap returned to us, photo/video stored in the EXTRA_OUTPUT location).

Maps V1 Removed

If your app is very old, it is possible that you are still trying to limp along with the original Google Maps on Android implementation, sometimes referred to as “Maps v1”. If you are using classes like com.google.android.maps.MapView and have a <uses-library android:name="com.google.android.maps" /> manifest entry, you are using Maps v1.

And you need to stop. Maps v1 has been deprecated for quite some time, it stopped working in Android 10, and in Android 11, it is simply gone.

If you have <uses-library android:name="com.google.android.maps" android:required="false" />, and you are checking at runtime for the existence of com.google.android.maps.MapView (e.g., Class.forName("com.google.android.maps.MapView")), your app should not find that class, and you should go through whatever sort of “graceful degradation” code path that you have set up for such devices.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.