Random Musings on Q Beta 2

Each time Google releases a new developer preview beta, I wander through the API differences report the high-level overviews, and even the release blog post, to see if there are things that developers should pay more attention to. 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.

And if it feels like you just read one of these, it was about three weeks ago. Time flies.

What Got Bigger and Smaller at the Same Time?

Q Beta 1 introduced a “roles” system, whereby one app per role type could hold a role and be granted superpowers. The documentation for roles was a mess, but roles seemed to work.

In Q Beta 2, things got weird(er).

On the one hand, the RoleManager JavaDocs show new roles: ROLE_ASSISTANT (presumably for the Assist API), ROLE_CALL_REDIRECTION (presumably for forwarding all your robocalls to your arch-nemesis), and ROLE_CALL_SCREENING (presumably for identifying the robocalls to forward to your arch-nemesis).

On the other hand, in the days leading up to Q Beta 2, an eagle-eyed subscriber pointed out to me that the Q documentation page for roles vanished. Even stranger, the scoped storage documentation no longer mentions roles, and you used to need to hold certain roles to have read-write access to certain content collections.

So, I have no idea what’s going on here. Q Beta 3 is due out around the time of Google I|O, so perhaps we will get some clarity then.

Speaking of Scoped Storage, WTF?

The biggest change with scoped storage in Q Beta 2 is that it is enabled by default. Once you flash your device or get your emulator up on Q Beta 2, you should see that adb shell getprop sys.isolated_storage_snapshot results in true. Now, more developers will start to encounter the stuff that I blogged about recently.

However, roles are no longer required. There are now three “strongly-typed” permissions for read access to external storage:

  • READ_MEDIA_AUDIO
  • READ_MEDIA_IMAGES
  • READ_MEDIA_VIDEO

These are dangerous permissions, so you will handle them the same as READ_EXTERNAL_STORAGE. If your app has targetSdkVersion set to Q, you can request those three permissions, and you get the same level of access that an app targeting API Level 28 or lower gets from requesting READ_EXTERNAL_STORAGE on a Q device. That is still very limited access, only through the MediaStore, but it is better than nothing.

The docs now have a section on using content with the NDK, advising you to use a FileDescriptor. What the docs do not tell you is that you can only get a FileDescriptor if the content is backed by a file or something else that can be mapped to a FileDescriptor. Not all content fits that description, particularly for "rw" modes. Be prepared to gracefully degrade if openFileDescriptor() returns null or throws a FileNotFoundException.

The recommended way for you to create audio, image, or video content that survives an app uninstall is to use MediaStore. This gets a bit more complicated due to some deprecations introduced in Q Beta 2:

  • getBitmap() is deprecated. Google steers you to ImageDecoder, though BitmapFactory would also be an option if you wanted code that works on older devices.

  • query() is deprecated. Google recommends using query() on ContentResolver, which hopefully is what you were using already anyway.

  • insertImage() is deprecated, which surprises me. Google steers you to a new “pending” capability in MediaStore, which is almost completely undocumented.

And the security bug that I filed is still present. The bug report itself has been ignored, outside of a couple of bots.

What Else Is Big?

Q finally got a user-visible feature of note: bubbles.

As a developer, I am grateful that this sort of floating interaction is being standardized. Having be an add-on for notifications should simplify development. And the fact that the actual content comes in the form of a tiny resizeable Activity means developers get another nudge towards being able to support split-screen and freeform multi-window modes.

As a user, I am grateful that I can turn this thing off.

What Else Do Manufacturers Hope Is Big?

The foldables march continues. The top-line announcement is that the emulator is supposed to now have the ability to create foldable emulator images.

“Foldable” is now also a formal system feature, in the form of PackageManager.FEATURE_FOLDABLE and android.hardware.type.foldable. So, if you create an app that requires a foldable (???), you can register in the manifest to say that you need that system feature.

What Else Changed from Q Beta 1?

BiometricPrompt.Builder used to have a setEnableFallback() method, which has been renamed to setAllowDeviceCredential(). That name is more reflective of its role: to allow the user to use a PIN or password as an alternative to biometrics. BiometricPrompt also now has a BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL error code, which appears to be returned if you called setAllowDeviceCredential(true) and the user has neither biometrics nor a PIN/password set up.

What Else Seems Intriguing?

ContentResolver now has a getTypeInfo() method. Given a MIME type, you get back a TypeInfo object that contains an icon, label, and description of the MIME type. This seems really handy for lists of content. However, the API returns all non-null values, so it is unclear what happens if you hand getTypeInfo() an invalid MIME type, such as rutabaga/marshmallow.

ContentResolver also has a wrap() method. You pass wrap() an instance of a ContentProvider, and wrap() returns a ContentResolver that only talks to that provider. I can see this being useful in testing ContentProvider implementations.

There is a new subtype of NotificationListenerService, called NotificationAssistantService. It “helps the user manage notifications”, whatever that means. It appears that a NotificationAssistantService has more power to modify the behavior of a Notification than does an ordinary NotificationListenerService.

There are a handful of new methods, such as getAttributeResolutionStack() on View, that appear to be here to help you determine how your various styles and themes are affecting particular widget and attributes of those widgets.

There is a new LocusId class, described as an “identifier for an unique state in the application”. What is curious is that it is supposed to be a very durable identifier of that state, not only surviving a reboot but surviving a backup/restore operation. That pretty much requires it to be a server-side identifier, as nothing local to the device might survive a restore operation. It is tied to an ACTION_VIEW_LOCUS Intent. However, it is unclear in what scenarios this will be used.

What Else You Got?

I will be releasing an update to Elements of Android Q in a couple of weeks. I will update what I have to reflect the changes wrought in Q Beta 2, and I will add some more chapters, probably on bubbles and the new share targets implementation.