Random Musings on the P Developer Preview 1

Each time Google releases a new developer preview, I rummage through the API differences report and the high-level overviews, to see if there are things that warrant more attention from developers, with an emphasis on mainstream features that any developer might reasonably use. I also tend to emphasize things that may not get quite as much attention, because they are buried in the JavaDocs.

As with Android O, Android P seems to be lacking a signature user-facing feature. It even lacks what I would consider to be a signature developer-facing feature. P, at least in the first developer preview, is an incremental advance. This might represent maturation of the platform, a focus on cleaning things up in preparation for future versions of Android, or changes in staff allocation emphasizing other Google projects. It might also be “sandbagging”, with signature features showing up in future developer previews.

That being said, there are a number of things that I found curious and interesting.

AppComponentFactory

Usually in these “random musings” posts, I have something that scares me. Occasionally, I have lots of things that scare me. Fortunately, this is a fairly mild release, and while these are early P days, I’m not scared yet.

However, AppComponentFactory is going to be a double-edged sword.

You can define a subclass of AppComponentFactory and register it in the manifest in the <application> element. Then, whenever the framework needs to create an instance of an Activity, Application, ContentProvider, BroadcastReceiver, or Service, your AppComponentFactory will be asked to do that work. You are given a ClassLoader, the String of the component name that appeared in the manifest, and an Intent (for Activity, Service, and BroadcastReceiver). Your job is to return the desired object. It would appear that you could chain to the superclass for those components that you do not want to handle differently, allowing you to focus on the scenarios where you want to do something different than what the framework normally does.

Let’s use an Activity an an example. Normally, when the user taps the home screen launcher icon, Android creates an instance of the designated Activity subclass using the zero-argument public constructor. However, if the app has a registered an AppComponentFactory and overrides the instantiateActivity() method, that will be called, and it needs to return the Activity.

For example, instantiateActivity() could:

  • Use some other sort of constructor, perhaps involving dependency injection

  • Have a generic placeholder <activity> element, and decide in Java code what the actual Activity will be, by using other information in the Intent and perhaps other state information in the app

  • Create some sort of ActivityWrapper that wraps the real Activity subclass, where the wrapper can do logging and such, forwarding all calls onto the wrapped Activity

And so on. Hence, this capability could be useful for a variety of scenarios.

However, it certainly simplifies the implementation of certain types of malware and crapware. Now I don’t even need to mess with the existing code. All I need to do is:

  • Create and register an AppComponentFactory that overrides instantiateActivity() (or whatever) to put my own wrapper around the app’s real component

  • Ship the altered app by whatever my preferred distribution channel is

  • Get results from my wrappers (e.g., report everything the user types in, slap ad banners over the UI, replace the app’s own ad banners with my own)

So, app repackagers will be all over this. Right now, that’s probably a semi-manual process; this change makes it easier to automate. So, with the good, we also get the bad.

This facility first came to my attention from Mishaal Rahman of XDA-Developers. It existed in some Android 8.0 builds, and possibly Android 8.1, as hidden APIs on Application, rather than through this separate AppComponentFactory class.

Changes You Should Pay Attention To

Some deprecations that I pointed out earlier – such as accessing hidden APIs and device admin policy changes, do appear to be coming to fruition. In particular, libraries that you use might be using some hidden APIs, even if you are not. Keep a close eye on your dependencies and upgrade them as appropriate.

Also, a rumored change has come to pass: the framework Fragment- and Loader-related classes are deprecated. This is a nudge to get you to use the ones from the Support Library, for consistent implementations across device versions. Note that there are not direct analogues for everything in the Support Library (e.g., no WebViewFragment).

Once you set your targetSdkVersion to P (or, eventually, 28?), you will be forced to use https by default. Attempts to use http URLs will result in an exception. If you still need access to unencrypted Web content, you will need to opt into that behavior.

Also, once you set your targetSdkVersion to P, you need to request a new FOREGROUND_SERVICE permission. This is a normal permission, so you just need the <uses-permission> element in the manifest. It is not really clear why they introduced this, but it is something that you will need. OTOH, this seems to be the only change related to the years-old “War on Background Processing”, so perhaps we have turned the corner on that subject.

If you have ancient instrumentation tests, they may break, until you either update them to use JUnit4 or you opt into the legacy testing library.

Android 7.0-8.1 stopped requiring FLAG_ACTIVITY_NEW_TASK. I pointed out this flaw ~18 months ago, and again a year ago. Much to my shock and amazement, this has been addressed with P, and so you need that flag again when starting an activity from a non-activity Context, such as from a Service or BroadcastReceiver. This returns us to the behavior from Android 6.0 and older OS versions, and so you probably already have this flag, unless your minSdkVersion is 24 or higher.

While there are many other behavior changes, those seem to be the ones that are likely to affect the most developers.

Stuff That Needs More Explanation

Android P has slices of… something. There is a whole family of classes related to this, including SliceManager and SliceProvider.

The JavaDocs for SliceProvider has:

A SliceProvider allows an app to provide content to be displayed in system spaces. This content is templated and can contain actions, and the behavior of how it is surfaced is specific to the system surface.

However:

  • It is unclear what “system spaces” and “system surfaces” refer to (lockscreen? notification shade? something else?)

  • The JavaDocs for Slice and the release notes for 28.0.0-alpha1 Support Library suggest that the content is app-to-app, not app-to-system-surface

So, there may be something cool here, but the documentation is really scant on the subject.

Also, Activity, Dialog, and View (at minimum) have a new requireViewById() method. This works just like findViewById(), but it throws an exception when there is no View matching the ID, instead of returning null. It is unclear where and why we would use this.

In addition, there is a StatsLog class that “provides an API for developers to send events to statsd”. It is unclear what this means or why we would use it.

You can now attach a “semantic action” to a Notification.Action, such as indicating that this action will delete something, archive something, “thumbs up” something, etc. It is unclear if this will be used for accessibility or if it has some other role.

Stuff That Seems Intriguing

We now have a Magnifier that we can attach to a widget. Presumably, it embiggens things.

There is a static disableWebView() method on WebView, which tells Android to not allow any WebView use in this process. If you have a multi-process app, and you know that some of your processes do not use WebView, calling this will prevent you from accidentally loading WebView and incurring the memory overhead. However, it does this by throwing an exception, so this may be the sort of thing that you only use in debug builds for tracking down unexpected WebView usage.

StrictMode now offers callbacks. For any violation, you can have a listener called on an Executor that you supply. Your listener is handed a Violation object, which is a subclass of Throwable. Hopefully (presumably?) this Violation is generated at the point of the bad code, so you can examine the stack trace and decide what to do. This may make it possible to create more advanced StrictMode reporting tools, including tying them into your existing crash logging system.

WebView offers a TracingController that can be used to “enable/disable tracing for parts of code and to collect tracing data”. You get a JSON block back, designed for use with chrome://tracing in your favorite Web browser whose name starts with “Chrom”.

Apps can now find out what autofill service the user has chosen, if any, via getAutofillServiceComponentName() on AutofillManager. This would be useful if your app wants to have a whitelist of trusted autofill services that you are willing to use, blocking autofill if the user chose one that you do not recognize.

Other Stuff Worth Noting

An AccessibiltyService can now take screenshots on behalf of the user.

The API differences report will note things being removed that do not appear to be removed. For example, the differences report indicates that a bunch of methods were removed from AbstractCursor. Those methods are still there in the full JavaDocs.

There is a new Settings action (ACTION_STORAGE_VOLUME_ACCESS_SETTINGS) to take the user to a screen listing the removable media on the device and which apps have access to that media. In particular, if the user denied your app access to the removable media, and clicked “Don’t ask again” on the dialog, sending the user to the Settings app is the only way your app will ever get access to that storage volume.

Apps can now help the user enroll a fingerprint, via ACTION_FINGERPRINT_ENROLL.

The change to yet another APK signing system means that there are new APIs and deprecations if you check signatures at runtime. The former signatures field on PackageInfo is replaced with signingCertificateHistory, which has details about signing key revisions.

Also, the versionCode is moving from an int to a long, as apparently 232 app versions weren’t quite enough.

In addition to regular KeyEvent processing in a View, there is now the notion of a “fallback”, in the form of onKeyFallback() and addKeyFallbackListener(). If the regular KeyEvent processing does not consume the event, any fallbacks are tried, before the KeyEvent is handed to the window callback. It is not completely clear where this would be useful, other than serving as a low-priority handler of some KeyEvent (i.e., if somebody else wants the event, have it, otherwise I will use it).

You can call getMainExecutor() on Context to get an Executor that will execute its jobs on the main application thread. There are other ways of accomplishing this, using Looper and a custom Executor implementation, but this is simpler.

JobInfo.Builder has a few new options:

  • setEstimatedNetworkBytes() provides a hint for how much bandwidth should be needed to execute this job. The idea is that jobs needing a lot of bandwidth may be postponed when the device has a lousy network connection, as excessive retries may chew up the battery.

  • setImportantWhileForeground() is strange. Based on the docs, it appears as though it will relax some of the restrictions imposed by Doze mode, if your app happens to be in the foreground at the time of the job. I guess this includes cases where the screen is off and your app was the last one to be in the foreground. Otherwise, if the screen is on and you are in the foreground, there should be no Doze restrictions in the first place.

  • setIsPrefetch() indicates that this job is pre-fetching some data for the user. JobScheduler may adjust your job’s timing as a result, either to invoke it as part of launching your UI (e.g., if the user clicks on your launcher icon) or if there is a lot of spare bandwidth (even if that bandwidth is metered and you ordinarily try to avoid metered data).

  • setRequiredNetwork() allows you to spell out the details of what sort of network connection that you need, based on capabilities and transport types, as defined by a NetworkRequest.

Where I Go From Here

Over the next several days, I will be putting Android P through its paces, in part by testing and re-raising a bunch of issues that probably haven’t been addressed yet.

An update to The Busy Coder’s Guide to Android Development, with coverage of Android P, should be out by the end of the month, if not sooner.