Other Changes of Note
There are lots of other changes in Android 10, 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 scoped storage certainly qualify for that.
Here are a few other smaller changes that may cause problems for reasonably-ordinary apps.
Background Activity Starts Banned
Starting an activity from the background is banned on Android 10. This change affects all apps, not just those with a targetSdkVersion
of 29
or higher.
Definition of “Background Start”
Your app will be considered to be starting an activity from the background if it calls startActivity()
(or equivalent methods) when it does not have a foreground activity.
The system can start one of your activities from the background — after all, that is how you get to the foreground in the first place. And select system-triggered events that execute a PendingIntent
can start an activity from the background, notably a PendingIntent
tied to a Notification
.
However, a foreground service is not “foreground enough” to be considered starting an activity from the foreground.
What Happens
If you try to start an activity from the background… nothing visible happens. A system app will file a warning message in Logcat:
W/ActivityTaskManager: Background activity start [callingPackage: com.commonsware.android.q.attention; ...]
But, otherwise, that’s it.
In the Q beta releases, a Toast
would appear, but this was removed in the final shipping version.
The Full-Screen Notification Alternative
What Google wants you to do is to switch to a Notification
that uses the “full-screen Intent
” feature. A high-importance, high-priority Notification
that uses this feature will get a heads-up presentation, where if the user taps on the bubble, the PendingIntent
associated with the full-screen feature will be executed. That same PendingIntent
is executed if the Notification
is raised while the screen is off, so the user can be taken to your activity immediately upon getting through the keyguard.
The idea is that this “full-screen” option still allows rapid access to your activity, while not interfering with the user while the user is using their device.
The PayAttention
sample module in the book’s sample project serves as a playground for doing both background activity starts and background full-screen notifications.
The UI consists of two really big buttons, one to start an activity, and the other to show a notification. When the user clicks one of those buttons, the MainActivity
uses WorkManager
to do some work in 10 seconds, then calls finish()
to destroy the activity and ensure that we are in the background:
package com.commonsware.android.q.attention
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit
class MainActivity : AppCompatActivity() {
private val workManager by lazy { WorkManager.getInstance() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar.title = getString(R.string.app_name)
activity.setOnClickListener {
workManager.enqueue(OneTimeWorkRequestBuilder<StartActivityWorker>()
.setInitialDelay(10, TimeUnit.SECONDS)
.build())
finish()
}
notification.setOnClickListener {
workManager.enqueue(OneTimeWorkRequestBuilder<ShowNotificationWorker>()
.setInitialDelay(10, TimeUnit.SECONDS)
.build())
finish()
}
}
}
StartActivityWorker
just starts an activity, though it needs to use FLAG_ACTIVITY_NEW_TASK
since we are starting the activity using the Application
context:
class StartActivityWorker(
private val appContext: Context,
workerParams: WorkerParameters
) : Worker(appContext, workerParams) {
override fun doWork(): Result {
appContext.startActivity(
Intent(
appContext,
MainActivity::class.java
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
return Result.success()
}
}
ShowNotificationWorker
, by contrast, sets up a Notification
that uses setFullScreenIntent()
to make it a full-screen notification:
class ShowNotificationWorker(
private val appContext: Context,
workerParams: WorkerParameters
) : Worker(appContext, workerParams) {
override fun doWork(): Result {
val pi = PendingIntent.getActivity(
appContext,
0,
Intent(appContext, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
val builder = NotificationCompat.Builder(appContext, CHANNEL_WHATEVER)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("Um, hi!")
.setContentText("remove me")
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setFullScreenIntent(pi, true)
val mgr = appContext.getSystemService(NotificationManager::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& mgr.getNotificationChannel(CHANNEL_WHATEVER) == null
) {
mgr.createNotificationChannel(
NotificationChannel(
CHANNEL_WHATEVER,
"Whatever",
NotificationManager.IMPORTANCE_MAX
)
)
}
mgr.notify(NOTIF_ID, builder.build())
return Result.success()
}
Note that the Notification
needs to be PRIORITY_HIGH
and the channel needs to be IMPORTANCE_HIGH
for this to work.
The module has two product flavors:
-
legacy
hastargetSdkVersion
set to 28 -
q
hastargetSdkVersion
set to29
Partly, this is so you can see the behavior of both existing apps and Android 10-ready apps on Android 10.
Partly, though, it is to point out another requirement of using full-screen notifications on Android 10. If your app has targetSdkVersion 29
, you need to request the USE_FULL_SCREEN_INTENT
permission. This is a normal
permission, so all you need is the <uses-permission>
element for it in your manifest. This module handles that through a manifest in the q
source set, so the <uses-permission>
element only gets merged in for q
builds:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.commonsware.android.q.attention"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
</manifest>
If you skip this permission, your notification will still be displayed, but the full-screen feature will not be enabled.
APIs Newly Requiring ACCESS_FINE_LOCATION
A long list of APIs have been added to those that require your app to hold the ACCESS_FINE_LOCATION
permission. While none of these methods give you GPS coordinates, they do provide information that can be used to derive the user’s location.
ACCESS_FINE_LOCATION
is a dangerous
permission, so you will need to both have the <uses-permission>
element for it and request it at runtime.
Alternatively, see if you can find some way to avoid needing to call those methods.
Continued Fight Against “Non-SDK Interfaces”
Starting with Android 9.0, Google began blocking your ability to access classes and members marked with the @hide
pseudo-annotation, plus private and package-private members. Simply put, if it is not documented in the Android SDK, you may not be able to refer to it at compile time by using Java reflection or similar techniques.
Android 10 adds yet more methods that are restricted.
The previous “dark greylist”/“light greylist” distinction has been replaced by a new system. Restricted methods may have an indication of whether they are allowed for certain targetSdkVersion
values or not. Look for @UnsupportedAppUsage
annotations in the AOSP source code, as those represent the banned methods. If they have a maxTargetSdk
value, that indicates the highest targetSdkVersion
for which those methods are supported — otherwise, they are banned. If maxTargetSdk
is 0
, that method is banned for all targetSdkVersion
values.
Developers should examine their code bases for any signs of using reflection to access hidden members: Class.forName()
, getConstructor()
, getField()
, and so on. This is particularly important for library authors, as issues with a library get amplified by the number of library users.
Remember that StrictMode
offers callbacks to find out when violations occur, and treats these violations as part of the VM policy. So, you could arrange to have StrictMode
report these violations to you, where you then log the stack trace somewhere. For example, you might modify your test suites to enable StrictMode
to collect this information, then write the output somewhere that your tests can pick up and incorporate into their results.
If you would like to be more proactive about dealing with this, the warning system for non-SDK usage is integrated with StrictMode
. detectNonSdkApiUsage()
is an option for StrictMode.VmPolicy.Builder
, so you can tie it into your overall StrictMode
reporting approach (e.g., crash app in debug builds, but in release builds just use the listener option on Android 9+ to integrate with your crash reporting engine).
SYSTEM_ALERT_WINDOW
Restrictions
SYSTEM_ALERT_WINDOW
is a special permission that allows an app to draw over other apps. First made prominent by Facebook’s “chat heads” feature, a dizzying array of apps now request SYSTEM_ALERT_WINDOW
. However, the ability to draw over other apps raises serious security issues.
At minimum, Android 10 is set to block SYSTEM_ALERT_WINDOW
on low-end Android Go devices, citing performance concerns.
Beyond that, Android Police claims that there are more significant limitations applied across the board:
- Apps that were installed via the Play Store and received
SYSTEM_ALERT_WINDOW
automatically lose it upon a device reboot - Apps that were “side-loaded” (i.e., installed from somewhere other than the Play Store) lose the permission 30 seconds after installation
If your app relies upon SYSTEM_ALERT_WINDOW
, you will need to investigate these items further and perhaps come up with some other solution for your problem. That might involve bubbles, at least starting in 2020.
ContactsContract
Field Deprecations
A handful of data elements that you can request from ContactsContract
are “obsolete”. Reading between the lines of the note in the JavaDocs about this, it appears that Google is proactively flushing this data.
And, if that is correct, it would appear that this data is not reliable on any relevant version of Android, not just Android 10.
In particular, they mention that these four values are affected:
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
ContactsContract.DataUsageStatColumns.TIMES_USED
These have privacy implications beyond merely knowing who the contact is, as they give insight into the contact’s behavior.
If you use ContactsContract
, please review the note and determine if you are affected.
Background Clipboard Access Banned
Apps no longer have access to the clipboard, unless:
- They are the default input method editor, or
- They are “the app that currently has focus”
In a typical single-window environment, this would mean that your activity is in the foreground. In split-screen or multi-window environments, this phrasing suggests that even if your activity is visible, it may not have access to the clipboard, if it lacks the focus. In other words, if your activity has been called with onResume()
but not onPause()
, you should have access to the clipboard.
android:sharedUserId
Deprecated
android:sharedUserId
is an attribute that you can have in the manifests of multiple apps to try to have them share a common Linux-style user account when installed on Android. This would allow them to read and write each other’s internal storage directly.
This feature was added largely for the benefit of pre-installed apps. It always represented a fair amount of risk for ordinary app developers. In particular, if you changed the android:sharedUserId
value — including adding one when one did not exist — now your own app would be unable to access its own internal storage from any previously installed version of the app. Those files were owned by some other Linux-style user account, not the new account requested by the new android:sharedUserId
value.
In Android 10, android:sharedUserId
is deprecated, and it is slated for outright removal in some future release.
If you are using android:sharedUserId
, start switching to IPC-based means of app-to-app communication, rather than having one app modify another app’s files “behind its back”.
DownloadManager
Deprecations
DownloadManager
deprecated some things, presuambly as a side effect of scoped storage:
addCompletedDownload()
-
allowScanningByMediaScanner()
onDownloadManager.Request
-
setVisibleInDownloadsUi()
onDownloadManager.Request
Items listed in the new MediaStore.Downloads
collection will be what appears in the Downloads UI now. So, if you have content that was not downloaded to the Downloads/
directory by DownloadManager
, and you want it to appear in the Downloads UI, you need to write it to a Uri
supplied by MediaStore.Downloads
.
Moar Densities!
There are four new DisplayMetrics
screen densities: 140, 180, 200, 220. Those are the first new low-end densities we have had in years, and it is unclear what hardware would have such screens. Most developers will not need to worry about these, as Android will scale mdpi
or hdpi
drawables for you. But, if you have other code that cares about these DENSITY_
constants on DisplayMetrics
, you have four more to deal with.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.