The CommonsBlog

"Elements of Android R" Version 0.1 Released

Subscribers now have access to Version 0.1 of Elements of Android R, in PDF, EPUB, and MOBI/Kindle formats. Just log into your Warescription page to download it, or set up an account and subscribe!

As with last year’s Elements of Android Q, this will be a small book, focusing on the changes introduced by Android R. This first version covers a fair bit of ground, including:

  • Changes to scoped storage, MediaStore, and permissions
  • Data access auditing and application exit auditing
  • The new package visibility restrictions
  • Sharing UIs between apps using SurfaceControlViewHost

As new developer preview releases of R roll out, I will add more chapters plus update the existing ones to reflect any behavior changes that I see in what I have covered already.

Everyone’s timelines are scrambled due to the COVID-19 pandemic, so predicting what will happen in the coming months is difficult. In theory, R Developer Preview 3 should ship sometime in April. With luck, I will have an update to this book out a few weeks after that.

Mar 30, 2020

A Peek at SurfaceControlViewHost in Android R

One of the items that I found interesting in the second half of my R DP2 random musings was SurfaceControlViewHost. I experimented with it this week, and it at least partially works. In a nutshell: one app can embed and display a live UI from another app.

Wait, Wut?

For some developers, this sort of cross-app UI embedding has been “the Holy Grail” for years. You can do a limited version of this with RemoteViews, but the widget set is minimal by modern standards. You could create your own RemoteViews-like structure, but keeping all of the participating apps in sync can get troublesome. Android 9’s slices… well, OK, those never really caught on.

But, with Android R and SurfaceControlViewHost, it is not that hard to set up cross-process UI delivery. There are no obvious limits as to what that UI can look like, because the UI itself is not really shared. Instead, the two processes seem to be sharing a Surface, with the UI-supplying process rendering a view hierarchy to that Surface and the UI-hosting process displaying that Surface as part of a SurfaceView.

How Do You Make It Work?

I’ll have code available on Monday, as part of the Elements of Android R release. UPDATE 2020-03-29: The EmbedClient and EmbedServer modules implement what I describe here.

Here are the basic mechanics:

  • Have two apps, with some sort of IPC channel between them. I elected to use a bound service, playing with Google’s Messenger pattern for getting data between the apps. In the source code, you will see an EmbedServer and an EmbedClient module that represent these two apps.

  • Have the UI client (EmbedClient) set up a SurfaceView and identify the Display on which that SurfaceView will appear. Then, it needs to send to the other app the dimensions of the SurfaceView, the ID of the Display to use, and a “host token” obtained from the SurfaceView via getHostToken(). All of those can be stuffed into a Bundle for easy delivery via common IPC patterns (e.g., as part of a Message).

  • Have the UI provider (EmbedServer) set up that UI, such as via view binding. When it receives the details from the client, it can set up a SurfaceControlViewHost tied to the Display and “host token”. It can then attach the root view of the view hierarchy to the SurfaceControlViewHost via addView(). Then, it needs to obtain a SurfacePackage from that SurfaceControlViewHost (via getSurfacePackage()) and send that back to the client. SurfacePackage is Parcelable, so you can send it via any common IPC mechanism (e.g., as part of a return Message).

  • Once the client receives the SurfacePackage, attach it to the SurfaceView via setChildSurfacePackage().

And that’s it. At this point, the client should be showing the provided UI in the SurfaceView. If the provider updates that UI, the client should show the updates in real time.

What About Input?

The docs indicate that touch events on the SurfaceView should get sent from the client process to the provider process, with the implication that this will trigger events on the widgets in the provider’s view hierarchy.

Unfortunately, I could not get that part to work.

That’s quite possibly a bug in my experimental code. There is very little documentation on this, and I may have missed a step somewhere. Otherwise, it’s possible that there is a bug in DP2.

What’s Google Going to Do With This?

I have no idea.

Seriously, they could use this for:

  • A richer replacement for app widgets and slices

  • A richer option for custom views in notifications

  • Embedding any of their apps in any other one of their apps (e.g., more powerful options for launching a Hangout from Calendar)

But, my guess is that whatever they have in mind will be something I won’t expect.

What Can We Do With This?

Well, not much, insofar as this is only available on Android R. Since this requires new methods on SurfaceView, my guess is that this cannot be backported via a Jetpack library. For the time being, approximately 0.0% of your user base is running Android R.

However, longer-term, this opens up some interesting possibilities.

From a security standpoint, this technique should allow for us to better sandbox untrusted content. We have had options for doing that, with dedicated low-permission processes, but they had only classic IPC ways of getting information out of the sandbox. Now, they can present a full UI, yet still not have any means of attacking the client displaying that UI.

Apps with a rich third-party ecosystem of plugins could adopt this for incrementally tighter integration with those plugins. Right now, the only easy thing is for the app to start an activity in the plugin, if the plugin needs to supply UI. Otherwise, you were stuck with the UI integration options I mentioned earlier, like RemoteViews. Now, though, a plugin can provide finer-grained UI elements that could be embedded in the core app’s UI, to offer a more seamless experience to the user.

Assuming that there is no significant performance overhead for delivering a UI this way, this opens the doors for popular content publishers to get their content embedded in other apps, yet still maintain complete control over that content.

But, once again, my guess is that the best use of this tech is something that I am not currently thinking of.

Ordinarily, I would have expected presentations on this at Google I|O. Now, in our I|O-free world, I do not know when or how Google might provide more information on this API and how they (and we) might use it. But, it’s something that I will be keeping an eye on, as it’s one of the more intriguing new additions in Android R.

Mar 27, 2020

R Raw Paths and All Files Access

In R DP1, I could not get the “raw paths” feature or the “all files access” feature to work.

In R DP2, both seem to be working fine.

The documentation is a bit confusing, but here is what I have seen in light experimentation.

Raw Paths

In a nutshell, READ_EXTERNAL_STORAGE works like it did from Android 4.4 through Android 9. If you request it, and the user grants it, you can traverse external storage more or less as you were used to.

However, there are caveats:

  • The documentation mentions reduced performance. I have not attempted to do any sort of apples-to-apples comparison, but I don’t get the sense that the performance degradation is severe.

  • As the documentation notes, you still do not have access to Android/ and its subdirectories.

  • I have not tried removable storage.

  • While the documentation emphasizes native libraries, read access works fine from Java/Kotlin. I have not tried native library access, but I assume that it works.

Also, methods like getExternalStorageDirectory() and getExternalStoragePublicDirectory() on Environment are still deprecated. My guess is that we are supposed to use getDirectory() on StorageVolume, as that was added in Android R. I have not experimented with that yet. getExternalStorageDirectory() works, at least on R DP2, returning the conventional location.

All Files Access


If you request MANAGE_EXTERNAL_STORAGE in the manifest, and you use Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION to bring up the Settings screen for this, and the user grants the permission, then you can write content to external storage. The effect is akin to what we had from Android 4.4 through Android 9.

Again, though, there are caveats:

  • The same concerns exist around how we get the filesystem path to use.

  • Presumably, write performance is also reduced.

  • Even with this permission, you still do not have access to Android/ and its subdirectories.

  • I still have not tried removable storage (hey, I’ve been busy writing this blog post).

  • There is no obvious means to find out if we hold this permission. It is not a dangerous permission, so checkSelfPermission() does not work. The issue has been marked as “fixed”, but I did not notice a method for this in DP2, so perhaps the method will show up in a later developer preview.

Now What?

Given last year’s continuous changes in this area in the Q developer previews, I am not making any final recommendations until R ships as Android 11.

Also, the fact that filesystem-style access is back in R does not change the fact that we should be losing that access in Q. android:requestLegacyExternalStorage is supposed to be ignored by Android 10 once our targetSdkVersion hits 29. Assuming that remains the case (I have not re-tested it), then we still lack filesystem-style access in Android 10, even though we have it in 9 and 11. If so, you are still going to be needing to try to adopt the Storage Access Framework and/or the MediaStore to handle Android 10. Plus, the MANAGE_EXTERNAL_STORAGE permission screen is decidedly more scary-looking than the normal dangerous permission dialog, so fewer users will be willing to grant it.

So, I’ll be watching this area in DP3 and beyond, to see what changes, if anything. However, it looks like external storage is making a comeback, after I left it for dead almost exactly one year ago.

Mar 22, 2020

More Random Musings on R DP2!

Rich Hong pointed me to the DP1 -> DP2 API differences report. Which is really good, because, as it turns out, all the cool stuff isn’t in the actual R DP2 docs.

So, here are some more random musings, based on that API differences report, and building upon yesterday’s original set of R DP2 musings.

Things That Show How Android Is In Control

As XDA-Developers mentioned a few hours ago, there is a new API for adding “quick controls” to the menu we get on a long-press of the POWER button. Apps can implement a ControlsProviderService to advertise options for this “quick controls” area. This appears to work a bit like TileService does for adding tiles to the notification shade. The available types of controls are defined as constants on a DeviceTypes class, and the candidates are mostly appliances and similar sorts of household items… and, apparently, a pergola. However, I feel quite confident that enterprising developers will find ways to use this for ‘device types’ that are somewhat broader in nature.

Things That Share

In Android 10, we got SurfaceControl. I blew past it, in part because I couldn’t figure out what it was for our how we would really use it.

In R DP2, we now have SurfaceControlViewHost. Its JavaDocs have a very interesting sentence:

The primary usage of this class is to embed a View hierarchy from one process in to another.

The implication is that we might now have something that developers have been asking for since the very early days of Android: the ability to embed the UI of one app in another. Whether that is the intended purpose or not remains to be seen, as all of this is still rather under-documented.

Things That Affect Security

There is a new VpnManager API for developers of VPN clients.

We also now have VerifiedKeyEvent and VerifiedMotionEvent. We seem to get these from verifyInputEvent() on InputManager, whose JavaDocs state:

Verify the details of an InputEvent that came from the system. If the event did not come from the system, or its details could not be verified, then this will return null.

It is unclear what the scenarios are where the InputEvent would not come from “the system”, in part because there is no real definition here of “the system”. However, if you have a highly-secure input scenario (e.g., passcode for a banking app), this may be worth investigating further.

Things That Are Getting Massaged

If you have been using various ways of implementing full-screen UIs, such as setSystemUiVisibility(), your current API calls may now be deprecated. That is moving to WindowInsetsController and WindowMetrics.

The ChooserTarget and ChooserTargetService that were the original approach for direct-share targets are now deprecated in favor of Android 10’s Sharing Shortcuts API.

What Happens Next?

The collapse of Google I/O does start to raise questions about Android R’s rollout. It would not surprise me if things get delayed, both at the level of the developer previews and betas and at the level of a final release. After all, Google has to do what everyone else is doing: try to get stuff done in the face of a pandemic.

However, eventually, the sun will shine again, SARS-CoV-2 will have reduced impact, and Android R will ship. Between now and then, I will write and blog what I can on what Android R means for us developers.

Mar 20, 2020

Random Musings on the R Developer Preview 2

Each time Google releases a new developer preview, I read what I can to see if there are things that warrant more attention from developers. I try to emphasize mainstream features that any developer might reasonably use, along with things that may not get quite as much attention, because they are buried in the JavaDocs.

This time, I can’t readily do the “buried in the JavaDocs” part, because the release notes lacks a link to the DP1 -> DP2 API difference report. Instead, it has two links pointing to the API 29 -> DP2 report, which makes it more difficult for me to identify what is new to DP2. If this gets fixed, I may follow up with another blog post.

UPDATE 2020-03-20: Rich Hong pointed me to the DP1 -> DP2 API differences report.

In the meantime, here are some musings, as random as ever…

Things That Might Piss Off Users

Apparently, Android R will nag users to not use a USB headset with apps that… don’t record audio. The workaround: have the app say that it records audio. ¯\_(ツ)_/¯.

UPDATE 2020-03-25: They have refined the language here — the “nag” dialog to which I was referring is only for apps that use UsbManager. That reduces the scope of this problem dramatically.

Things That Could Break Your App

I worry that the package visibility changes are going to break more apps than perhaps Google realizes. While I haven’t tested this aspect of R DP2 yet, it appears that your app now can’t find out what other apps are installed, on a general basis. The cited example is queryIntentActivities(), but to make this really work you would need to seriously lobotomize PackageManager. You can whitelist certain packages and certain <intent-filter> structures to try to get by this for certain use cases. And, this is where the mysterious QUERY_ALL_PACKAGES permission seen in DP1 comes into play — this permission removes these new restrictions. Given the “look for Google Play to provide guidelines for apps that need this permission” caveat, it is safest to assume that if you try using it, eventually you will be banned from the Play Store by a bot.

If your app has been dutifully following the new Android 10 rules for background location access… get ready for new user flows.

If your app has been clearing the cache of other apps… sorry, but that appears to no longer be an option.

Probably the move to Conscrypt for SSLSocket will not cause problems for most apps, but it’s something to keep an eye on. UPDATE 2020-03-20: A Googler reached out to correct me on this one. Conscrypt has been used for SSLSocket for some time. However, there are some implementation tweaks in Android R — now SSLSocket is “just a wrapper” of SSLEngine. Again, there should be few problems here, but it is something to note.

Things That Are Generally Positive

android:preserveLegacyExternalStorage might be useful for some developers, though I suspect not all that many. Basically, it allows you to say that users who upgrade your app get old-style external storage while new users get scoped storage, by my read of it.

Being able to use more than one camera seems nice.

The blog post mentions some new window inset options for more synchronized behavior with soft keyboards as they collapse and expand. Strangely, I don’t see this in the main Android R release documentation. It’s a slick feature, albeit one that I do not expect will get adopted that much. As with Android 10’s gesture-based navigation, I think Google is seriously over-estimating developers’ willingness to rewrite UIs for these sorts of things. After all, we can’t get apps to support landscape, and that feature has been around since API Level 1. So, while I’m sure highly-polished apps will use this new synchronized IME stuff to become even more highly-polished… most apps will muddle along as they have been.

Things That Make Me Go “Hmmmmmm…”

The per-process network access control feature seems great… except that the supplied XML is mystifying. Where does this go? What does an empty <process /> element mean? What does a <process> element without children mean? Does the allow-permission and deny-permission element names, coupled with the fully-qualified permission names, mean that this works for all permissions, not just android.permission.INTERNET?

What is referred to as dynamic intent filters seems to overstate the case: the only dynamic element is the roster of supported MIME types. The rationale given for this feature is:

This is a problem for virtualization apps (such as virtual machines and remote desktops) because they have no way of knowing exactly what software the user will install inside them.

That rationale seems odd to me. I certainly can’t rule it out, but it is unclear under what circumstances “virtual machines and remote desktops” would know about particular MIME types that they should support dynamically. Plus, virtual machines would seem to be in violation of Play Store policies regarding dynamic code loading just by existing. The feature seems fine, but something seems fishy.

What Happens Next?

I think I have found enough stuff that works to warrant releasing an Elements of Android R in the coming weeks, so stay tuned!

UPDATE 2020-03-20: I have more musings on R DP2!

Mar 19, 2020

Older Posts