Mark L. Murphy's Blog, page 16

May 11, 2020

���Exploring Android��� Version 1.1 Released

Subscribers now have
access to an update to Exploring Android,
known as Version 1.1, in PDF, EPUB, and MOBI/Kindle formats, in addition to the
online reader. Just log into
your Warescription page and download
away, or set up an account and subscribe!





Mostly, this update contains bug fixes, along with changes to support Android Studio 3.6.3 and
updating many of the dependencies.



I plan on a more significant overhaul of the tutorials in the next update.
Right now, I���m tentatively planning on that in August, assuming that Android
Studio 4.0 ships in stable form in the next month or so. Since it is in Beta 5
right now, that seems likely but not guaranteed.

 •  0 comments  •  flag
Share on Twitter
Published on May 11, 2020 05:58

May 6, 2020

Random Musings on the R Developer Preview 4

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. That even includes musings about developer previews
that we were not expecting, such as today���s Developer Preview 4 (DP4).



The deeper we get into the preview/beta series, the fewer changes we
see. That is particularly true for this release, squeezed in between between
the DP3 of about 2 weeks ago and the Beta 1 due out in a month.



On the whole, it feels like Google elected to back off on some changes, perhaps
ones that were unlikely to be stabilized sufficiently by some internal deadline.
So, the API differences report from DP3 to DP4
shows a bunch of removals of stuff added in previous previews. What will
be interesting is whether this leads to an 11.1 release in late fall ���
we have not had a point release in three years, but it might be that Google elects
to have some features slide. I do not expect an 11.1, but the odds seem higher
this year than they were last year, just because of the problems induced by
SARS-CoV-2 and COVID-19.



However, a few things caught my eye.



What Might Break Your App?

The big one is the change to phone number access. If you have been using
methods like getLine1Number(), you now need a different permission:




Starting in Developer Preview 4, apps that want to access phone numbers through the TelephonyManager.getLine1Number() and TelecomManager.getLine1Number() methods must request the READ_PHONE_NUMBERS permission.




READ_PHONE_NUMBERS is a dangerous permission, meaning that not only will you
need a manifest change, but you will need to tweak your runtime permission
requests as well.



There is a new LimitExceededException,
with no clear picture of what limit we might be exceeding. It will be interesting
to see if anyone encounters this and in what circumstances.



If you have been using alternative build tools and are compressing
your resources.arsc file, that will not be supported
once your targetSdkVersion reaches R or higher.



What Else Changed?

If you have been playing with bubbles, there are
a few changes in Notification.BubbleMetadata.Builder.



The special info for waterfall displays
and foldables is nice,
for the handful of apps that are going to try to do special things for those form
factors.



In Android R, the Storage Access Framework has restrictions on what the user
can select (e.g., ACTION_OPEN_DOCUMENT_TREE will not let the user select
a storage root directory). However, in R DP4, that restriction
is now only taking effect with a targetSdkVersion of R or higher.



SQLiteDatabase got a new
execPerConnectionSQL()
method. You supply some SQL, and that SQL will get executed whenever a database
connection gets established. This is useful for PRAGMA statements that
configure your use of the database.



And, as Android Police reported,
the gesture to undo swiping an app off the overview screen appears to have been
removed. It is unclear whether it might make a return later in the R development
process or if it has been shelved for a longer period of time.



What���s Next?

The next milestone is Beta 1 on June 3. This comes complete with its own
beta launch show, to fill some of the gaps
created by the loss of Google I|O.



I have some experiments to run, not only related to DP4 but also due to some
undocumented changes in scoped storage.
My guess is that the next update to Elements of Android R will come
after Beta 1, sometime in mid-June.

 •  0 comments  •  flag
Share on Twitter
Published on May 06, 2020 15:34

May 4, 2020

���Elements of Android R��� Version 0.2 Released

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



The book is up to date for Android R DP3, including a few tweaks for where DP3
changed from DP2, such as:




���All files access���
Automatic permission removal
Revised APIs for data access auditing
Package visibility


There is also a new chapter on bubbles��� which looks a bit like a long-lost
chapter from Elements of Android Q that I removed when they pulled bubbles from
what users would see on Android 10. And, there is a partial ���Other Changes of Note���
chapter, including material on wireless debugging, SYSTEM_ALERT_WINDOW changes, and more.



So far, Google has been sticking pretty close to the published Android R timeline,
which is impressive given the slight pandemic going on. Sometime this month, we
should get Beta 1, and I will be aiming for another update to this book in early
June covering those changes and other Android R stuff.

 •  0 comments  •  flag
Share on Twitter
Published on May 04, 2020 04:57

April 25, 2020

Jetpack Compose��� on the Desktop

The Awesome Android newsletter���s issue #201
pointed out a very interesting Gerrit entry related to Jetpack Compose, hinting
at a future desktop offering. This possibility is something that I have been
tracking for a few months now, so let���s explore what may be going on here.



Expecting Actual Code

When I was poring over the early dev builds of Jetpack Compose, I noticed
that some elements had commented-out expect and actual keywords. For example,
Canvas.kt
had commented-out expect keywords and
AndroidCanvas.kt
had commented-out actual keywords.



Those keywords are tied to Kotlin/Multiplatform (KMP for short).



In KMP, you have Kotlin code that can run across all platforms (Kotlin/Common).
That code can expect an API that, for any given platform, something else will supply
the actual implementation of that API. For example, you can have common Kotlin code
that will expect Android and iOS modules to implement the actual version of the expected
API, using platform-specific stuff to do so. This is a bit reminiscent of how your
main/ source set in an Android app can ���expect��� that each of your product flavors
have an ���actual��� implementation of some class (e.g., a strategy pattern implementation
tied to that flavor).



Since the keywords are commented out, we do not really need a KMP setup to be
able to build or use Compose. And there are fairly few occurrences of these
commented-out keywords, so this may have been just a form of documentation,
leveraging KMP keywords for showing the relationship between functions.



But, somebody was thinking about Compose on multiple platforms, at least to some level.



Android as API

We often say that our code depends upon Android. However, much of the time,
that is really shorthand for ���our code depends upon Android APIs���. We use android
classes and expect them to do what we want them to do. Exactly what those
implementations are and how they accomplish their aims usually is beyond what we
really care about as app developers.



As a result, as long as we satisfy the symbols that we reference and that the
compiler needs, exactly what sits behind those symbols is immaterial, so long as
our code works.



Android developers take advantage of that on a daily basis:





Your compileSdkVersion is used to identify a JAR containing all the public
symbols from the Android SDK. But the actual implementation of all the Java
methods is simply throw new RuntimeException("Stub!"). The real implementation
of Android is added to our classpath at runtime; the android.jar that we compile
against is not packaged in our APKs.




Robolectric, in the end, is an implementation of a slice of the Android SDK that
does not have any Android ties. Robolectric���s implementation gets used in unit
tests that run on the JVM, not on Android, allowing us to test a bit more of our
code than we might otherwise, while also allowing us to avoid having to roll our
own mocks of those android classes.





What That Gerrit Entry Shows

The main Gerrit link
from the Awesome Android newsletter represents a KMP project, targeting the JVM desktop.
In there, desktopMain/ is a source set for code that will be used for desktop apps.



And in that source set, we see class names that look an awful lot like a subset
of the Android SDK, such as android.content.Context and android.view.View.



The implementations of those classes look nothing like Android. For one, they are all
in Kotlin, and last I checked, the AOSP framework classes are still in Java.
And those classes implement a tiny subset of the real framework APIs. Right now, the
desktopMain/ implementation of Context, for example, is 43 lines of code���
including the copyright header comment. There are individual methods in the
real Context class that are longer than that.



The objective appears to be to satisfy the compiler to allow Compose code to build
an Android app, with class and function implementations that use desktop Java instead of Android.



The actual UI is rendered by a SkiaWindow, which relies on JetBrains��� Skija project,
which offers Skia bindings for Java. Skia is an open source
2D graphics library, one that powers rendering in Android, Chrome OS, and in browsers
like Chrome and Firefox.



So, at least at an experimental level, Google appears to be working on getting
Jetpack Compose to support desktop apps, as well as Android apps.



Implementing ���fake��� Android framework classes may be somewhat of a stop-gap.
If Google elects to take Jetpack Compose more fully into KMP, uncommenting
those expect and actual keywords and expanding upon them, then the desktop code would switch
to providing the actual API that Compose is trying to expect.



Of course, right now we have no idea if Google will ever bring this to fruition.
For all we know, this is just an experiment, one that will never see the light of
day (beyond blog posts like this one). We know that it is actively being worked on ���
one of the developers uploaded a new patchset while I was writing this post.



Regardless, implementing something like this is
likely to happen. I have seen other developers start to poke at this sort of thing,
and I considered doing it myself. It���s possible that all of those efforts will come
to naught as well, but there���s a decent chance that somebody will succeed.



If Jetpack
Compose becomes Google���s recommended UI approach for Android, having the ability to
also develop desktop apps from much of the same code base is a nice value-add, at minimum���
and possibly much more.

 •  0 comments  •  flag
Share on Twitter
Published on April 25, 2020 05:51

April 23, 2020

Random Musings on the R Developer Preview 3

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.



The deeper we get into the release series for a new Android version,
the fewer ���big ticket��� changes appear. Android R is no exception. Many
of the changes are tweaks to things that debuted earlier, such as new class
and method names around data access auditing.



So, What���s New?

Besides stuff like the improved wireless ADB access
that Google pointed out in the DP3 release blog post,
I see a few other new items of interest:





We now have isExternalStorageManager() methods on Environment.
This is how we can find out if the user has granted us MANAGE_EXTERNAL_STORAGE.
The zero-parameter version tells us if we have such rights for external storage;
the one-parameter version tells us if we have such rights on whatever storage
volume contains the supplied File. This was a clear gap in the earlier developer
previews, so I am glad that this was added!




There is a new option with the keystore to require authentication per use
on designated keys, with developer-controlled authenticators (e.g., strong biometrics
or a device credential).




ContentResolver now has a notifyChange() method that takes a collection of Uri values,
presumably as an optimization.




ParcelFileDescriptor now has a wrap() method.
You also supply an OnCloseListener, which is notified when the underlying ParcelFileDescriptor
is closed. This could be useful for tracking file descriptor leaks ��� keep a roster
of those that you create and remove them from the list when they are closed.





Also, Android Police pointed out
that there is a new Settings option to ���auto-revoke permissions���. Basically,
if the user does not use your app for a while (���a few months���), Android will automatically
revoke certain permissions that the user has granted. Well-written apps should not
be harmed by this, but it is yet another wrinkle with runtime permissions added
by Android R, on top of the one-time permission option that debuted with DP1.



What Significantly Changed?

Besides the name changes in data access auditing,
we have:





android.view.inline moved to android.widget.inline and got a bit more in the way
of documentation. InlineContentView
feels like something layered atop of the SurfaceControlViewHost that I profiled
here
and here.
Still, it is unclear how this is going to get used in practice.




ApplicationExitInfo
got three new reasons of why your process died. Also, we can now register a
���process state summary��� via ActivityManager
and get that back in ApplicationExitInfo, to provide a bit more context about
what the app was doing at the time its process died. And, if we crashed (particularly
with an ANR), we can get details of what the threads were doing at the time.





What���s Gone?

Whatever FEATURE_CONTEXT_HUB was, it no longer exists.



Also, as part of revamping the ���resource loader��� system, they got rid
of DirectoryAssetLoader��� despite still referring to it in the preview
documentation.



While technically not ���gone���, a couple of WebView methods were deprecated:




setAllowFileAccessFromFileURLs()
setAllowUniversalAccessFromFileURLs()


What Now?

I will work on releasing an updated version of Elements of Android R
in the coming weeks, covering some of this and other things that are part of
earlier developer previews.

 •  0 comments  •  flag
Share on Twitter
Published on April 23, 2020 15:13

April 7, 2020

SurfaceControlViewHost Input in Android R

In a previous post,
I reviewed the basics of how you can use SurfaceControlViewHost
in Android R to have the UI from one app be displayed in another app.



The one part that I was missing was user input, as that did not seem to work, though
it was advertised as being supported.



Primarily, that was a matter of setting the SurfaceView to be on top from a Z axis standpoint.
After that, user input worked just fine, at least for click events ���
I have not yet experimented with anything more complex. In particular, it will
be interesting to see if text input works, given the three-way interaction
between the app supplying the UI, the app displaying the Surface, and the input
method editor.



This tagged edition of EmbedClient
has the revised implementation. The equivalent EmbedServer
did not have to change to implement the fix.



Many thanks to the Google engineer who pointed out the problem!

 •  0 comments  •  flag
Share on Twitter
Published on April 07, 2020 14:12

April 5, 2020

Android R, Package Visibility, and Some Holes

One of the changes that showed up in R DP2 is
the package visibility restriction.
Once your targetSdkVersion reaches R, you will not be able to ���see��� some
of the other apps on the device:





Explicit Intents cannot be used to start or bind to components




Many (if not all) PackageManager methods filter them out





So, PackageManger methods like getInstalledApplications(), getInstalledPackages(),
getPackagesHoldingPermissions(), and queryIntentActivities() all give you
a filtered view of what is really on the device.



The argument for this change is fingerprinting, at least in part. Many users
do not install or remove apps frequently, so libraries can use that information to generate identifiers that work between apps.
This is much like how ad networks use various bits of your browser
settings to track you across sites, which you can visualize in
the EFF���s Panopticlick tool.



The Android R documentation is a bit ���hand-wavy��� as to what your app can and
cannot see. Based on some light experimentation, it appears that
your app can still see pre-installed apps but not user-installed ones.
From a fingerprinting standpoint, this seems reasonable ��� all users with the
same model device should have the same mix of pre-installed apps.



If you need to see more apps than that, you can use a <queries> manifest
element to whitelist certain packages or Intent structures. There
is no user acceptance required; whatever you request in the whitelist, you can see.
This allows something like a launcher to whitelist apps with a component
that supports ACTION_MAIN and CATEGORY_LAUNCHER, for example.



Hole #1: Whitelisting the World

And that���s the bigger of the holes: anyone can whitelist anything, so long as it
fits the <queries> structure.



So, the simple way to bypass this restriction is to whitelist ACTION_MAIN and CATEGORY_LAUNCHER,
as the vast majority of Android apps will have a launcher activity. And now you can
see (nearly) everything again.



Right now, at least, the whitelist affects the visibility of apps, across multiple
use cases. Whitelisting ACTION_MAIN and CATEGORY_LAUNCHER does not just allow
those activities to be visible via queryIntentActivities(). Apps with a launcher activity
become visible in getInstalledApplications() and getInstalledPackages() as well.
My guess is that they become as visible as a built-in app.



A possible upcoming counter-move would be to tie certain <queries> configurations
to certain roles in RoleManager, so only the launcher can whitelist
ACTION_MAIN and CATEGORY_LAUNCHER.



But, from the standpoint of fingerprinting, that might be insufficient. An app
could whitelist a bunch of common Intent structures, such as ACTION_BOOT_COMPLETED
and ACTION_SEND. Similarly, an app could elect to whitelist common user-installed packages, rather
than Intent structures. Given enough packages, you might be able to develop
a decent fingerprint just by seeing who has what mix of those packages installed.



Android old-timers like me will remember the Android 1.x/2.x days,
when malware would register a BroadcastReceiver for a zillion common actions, in
hopes that their app would get control before the user had realized that the malware
was, indeed, malware. That same sort of attack seems likely here. That too
could be policed, perhaps at the Play Store approval level, but that can become
fraught with false positives (developers getting bans for legitimate activities)
and false negatives (fingerprinters slipping through because their list wasn���t quite
big enough to raise a warning).



Perhaps Android starts warning users about these whitelists, or starts requiring
dangerous permissions for using certain PackageManager APIs used by fingerprinters.



Hole #2: Disabled Things Are Disabled

The objective of fingerprinting is to identify a user. This package visibility change is focused
on one way of identifying the user: seeing what user-installed apps exist.



However, sometimes, users disable built-in apps. That still affects PackageManager results.
queryIntentActivities() does not return activities from disabled apps, for example.



I do this a fair bit, disabling a bunch of pre-installed crap that I don���t want
and cannot uninstall. So, a fingerprinter could try to identify people by the ���negative space���:
what should be on their device (given the model) but does not show up.



My guess is that not all that many people disable apps,
so perhaps it is not worthy of a fingerprinter trying to exploit it. And I don���t know
what Google could do about it anyway. So, this is just a small hole.





There may be other holes in this system ��� I have not tried all that much, and
this ���isn���t really my jam���. I���m just trying to figure out what to tell developers
about how this works.



Plus, this is only Developer Preview 2, so some of this may be tweaked in future updates.



But my fear is the package visibility change winds up not satisfying anyone, by:





Adding stumbling blocks for legitimate scenarios, and




Not really solving the intended problem(s), such that attacks are still usable

 •  0 comments  •  flag
Share on Twitter
Published on April 05, 2020 15:39

April 4, 2020

Scoped Storage Stories: MediaStore Metadata Madness

Android 10 and higher are greatly restricting access to external storage
via filesystem APIs. Instead, we need to use other APIs to work with content.
This is the 14th post in a seemingly never-ending series, where we will explore how to work with those
alternatives.





Previously, I wrote about modifying the content of other apps
and responding to a RecoverableSecurityException.
In that latter post, I wrote:




Unfortunately, that RecoverableSecurityException is not going to do you a lot of good in this case��� because DESCRIPTION cannot be modified, even if the user grants your app permission to modify the content. For some columns, MediaStore just refuses to apply the update.




It turns out that there is a recipe for making this work,
though the approach is bizarre:





First, update the content to set IS_PENDING to 1




Then, update the content to modify your desired bits of metadata and also set IS_PENDING back to 0





IS_PENDING was added in Android 10. The idea is that you set IS_PENDING to 1
to indicate that you are setting up the content, such as copying data to the Uri
supplied by the MediaStore. Apps, when querying the MediaStore, can opt out of
seeing pending items, as they may not be useful yet. Later, you flip IS_PENDING
to 0, to indicate that the content is ready for use.



Inexplicably, in an update() call on ContentResolver for MediaStore, where
you flip IS_PENDING from 1 to 0, you can also update other metadata columns
like DESCRIPTION. If IS_PENDING is not already 1, this does not work, so you
wind up having to set IS_PENDING to 1 first, then set it to 0 while updating
the rest of your metadata.



I���m sure that this system makes sense to somebody��� but not me. However, it works
on Android 10. I am still running into problems in general with MediaStore
and RecoverableSecurityException on R DP2, so I do not know if anything with
this changes there.



The v2 tagged version of my MovieLister sample
demonstrates this approach. VideoRepository uses a setMetadata() function
to update metadata using this IS_PENDING hack:



private suspend fun setMetadata(id: Long, values: ContentValues) =
withContext(Dispatchers.IO) {
val uri = ContentUris.withAppendedId(collection, id)

if (Build.VERSION.SDK_INT >= 29) {
val tempValues = ContentValues().apply {
put(MediaStore.Video.Media.IS_PENDING, 1)
}

context.contentResolver.update(uri, tempValues, null, null)
}

val updatedValues =
if (Build.VERSION.SDK_INT >= 29) {
ContentValues(values).apply {
put(MediaStore.Video.Media.IS_PENDING, 0)
}
} else {
values
}

context.contentResolver.update(uri, updatedValues, null, null)
}


You pass in the ContentValues that you wish to apply, and setMetadata():




Sets IS_PENDING to 1
Makes a new ContentValues based on your passed-in one
Adds IS_PENDING as 0 to that copy of the ContentValues
Updates the item with that augmented ContentValues copy
Only goes through all that crap on Android 10 and higher, skipping it for older devices


To me, this approach screams ���unintended side effect���, and I would not be surprised
if it breaks in Android R or future versions. Also, bear in mind that some of these
metadata values may get clobbered if the MediaStore re-indexes the content. update()
is updating the MediaStore, not metadata that might be in the media itself (e.g., EXIF
tags in a JPEG).



But, at least for Android 10, it is an option that you can consider.





The entire series of ���Scoped Storage Stories��� posts includes posts on:




The basics of using the Storage Access Framework
Getting durable access to the selected content
Working with DocumentFile for individual documents
Working with document trees
Working with DocumentsContract
Problems with the SAF API
A specific problem with listFiles() on DocumentFile
Storing content using MediaStore
Reading content from the MediaStore
Modifying MediaStore content from other apps
Limitations of MediaStore.Downloads
The undocumented Documents option
More on RecoverableSecurityException
How to modify more metadata in MediaStore
 •  0 comments  •  flag
Share on Twitter
Published on April 04, 2020 11:11

April 1, 2020

Introducing jetc.dev!

I am running a weekly email newsletter about Jetpack Compose. You can find the latest
issue and archives at jetc.dev. It���s your typical ���roundup of
new resources��� newsletter, with a little bit of personal flair. New issues should
come out on Tuesdays.



The newsletters are rather slim at the moment, because Compose is still in early
developer previews. As Compose matures and becomes more adopted, I expect that
the newsletters will have a lot more material.



If you think of things that you believe should get mentioned in the newsletter,
reach out!

 •  0 comments  •  flag
Share on Twitter
Published on April 01, 2020 05:17

March 30, 2020

���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.

 •  0 comments  •  flag
Share on Twitter
Published on March 30, 2020 05:07