Mark L. Murphy's Blog, page 15

September 21, 2020

���Elements of Android Room��� Version 0.3 Released

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





This update adds three more chapters, covering:





Default values and partial entities




Packaging starter data in your app




Backing up a database





In addition:





The PagedFTS sample was updated to take into account the option for
packaging a database in your app




A bunch of dependencies were updated, notably Room itself




Various bugs were fixed

 •  0 comments  •  flag
Share on Twitter
Published on September 21, 2020 05:10

September 16, 2020

App Security at Android Summit 2020!

I���ll be presenting as part of Android Summit 2020,
which has moved from the Washington DC area to the Web due to the pandemic, as have
so many other events.



This year, I wanted to return to a subject that I have presented on many times before:
app security. Generally, this subject is a bit of a backwater ��� Android experts
are orders of magnitude more likely to write or talk about animations than security.



However, in the 2020 edition of the Android Summit, it seems like there will be a few
presentations on security. This is a welcome change! However, it means that my planned
���survey of app security��� talk isn���t a great fit.



So, I���m going to be tweaking it slightly, focusing on modern app development and
where security comes into play:





How (and when!) do we think about security in an agile/SCRUM world?




Where can security flaws come from? (hint: it���s not just your code)




How do we automate security checks the way that we automate our testing?




And so on





I���ll be using concrete examples of problems along the way to illustrate these sort
of ���AppSecOps��� or ���DevSecOps��� topics.



The Android Summit is being held October 8-9. I do not know right now exactly when
I will be speaking, other than it will be in the US East Coast afternoon or early evening.
This is OK for the Western Hemisphere; if you are elsewhere, you may wind up needing
to wait for the conference video to be published.



But, at the same time, since this is being held online, anyone can attend, not just
those who are in position to get to DC! Visit https://www.androidsummit.org/ for
details on getting tickets and, in the coming weeks, the complete event schedule.



I look forward to seeing you (virtually) there!

 •  0 comments  •  flag
Share on Twitter
Published on September 16, 2020 05:29

September 13, 2020

Android R One-Time Permission Problem Really an Android Studio Problem

Last month, I wrote about an apparent bug in Android R, where
one-time permission expiration sometimes kills alarms and jobs.



Google investigated and concluded that it is really an Android Studio problem.
I can confirm their basic findings ��� the problem does not show up if
you install and run the sample app from the command line,
for example.



What is supposed to happen when a one-time permission is revoked is that the app���s
process is terminated. This is in line with how other permission revocations are
handled. Other than the process termination happening relatively rapidly after the app
leaves the foreground, there is nothing unusual here.



Android Studio, though, does not feel that this is punitive enough. Studio wants
to teach that app who is in charge. So, Studio uses adb to force-stop the app
(somehow���) when it detects that a process that Studio started was terminated.



I do not know why Android Studio feels that it needs to be so mean.



Regardless, this means that the original problem should only happen during
development, not in production. This may complicate app development work, but it
should not harm users. While this is not ideal, it is much better than what I
was fearing.



Many thanks to Nicole Borelli and the rest of the Google team that investigated
the problem!

 •  0 comments  •  flag
Share on Twitter
Published on September 13, 2020 05:04

September 5, 2020

Getting Android Studio 4.2 Canary To Run Again

If you are like me, when Android Studio 4.2 Canary 8 came out, it would crash on
startup with:



OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.
Error occurred during initialization of VM
Multiple garbage collectors selected


This has been reported for Ubuntu,
macOS,
Arch Linux,
and (albeit with an incorrect title) Chrome OS.



Thanks to this comment,
it is more clear what is going on.



When Android Studio 4.2 runs for the first time,
it creates a $HOME/.config/Google/AndroidStudioPreview4.2/
directory and dumps a bunch of files and directories in there. One is studio64.vmoptions, which
contains a bunch of java command-line options that presumably get applied when
launching Studio via studio.sh.



One of those, -XX:+UseConcMarkSweepGC, seems to be what is triggering this crash.



So, see if you have that file ��� if you are getting this crash, you should.
Just edit the file, remove that line, and save your changes. I was able to get Canary 9 to start after
that, and while I did not test Canary 8, my guess is that this fix will handle
it as well.



There may be a better fix than removing this line, and ideally we would not have
to manually edit this file. But, this is better than nothing.

 •  0 comments  •  flag
Share on Twitter
Published on September 05, 2020 13:08

August 28, 2020

Android R One-Time Permission Expiration Sometimes Kills Alarms, Jobs, More

UPDATE 2020-09-13: It turns out that the problem listed here is really
an Android Studio problem.



Android R adds one-time permissions to the user experience. The idea is that
the user can grant a runtime permission for a while. I thought that it was
for the current process invocation, but it appears as though you lose the
permission if your app moves to the background for a while as well, where ���a while���
is less than a minute.



Worse, as I found out first from this Stack Overflow question,
we lose a lot more in less than a minute��� at least sometimes.



That question, and the associated entry in the issue tracker,
point out that alarms and jobs get canceled when the one-time permissions
get revoked. I did more testing, based on their sample app, and IMHO
the effect feels a lot like a ���Force Stop���.
Specifically, we can no longer respond to broadcasts registered in the manifest,
even for those that are on the implicit broadcast exception whitelist, such as
ACTION_LOCALE_CHANGED. The only thing that I know of that would explain all those behaviors
is a ���Force Stop���, though it is certainly possible that there is something else
that happens to look similar.



In my light testing, the overall effect was inconsistent. I could get it to happen
reliably the first time I tried a one-time permission for an app installation;
after that it was ���hit or miss���.



Android developers have been dealing with rogue ���Force Stop��� operations for years.
Originally, users could only cause this via an app���s screen in the Settings app.
Then, a few device manufacturers decided to have their own task manager apps do a ���Force Stop���.
Eventually, that led them to have ���Force Stop��� take effect when swiping the task
off the overview screen. But, this is the first time that I can think of that Google
is doing this sort of unexpected ���Force Stop���.



If you have done your own analysis of this problem and can add further evidence
(more than ���me too!���), please contribute to the issue.
Frankly, I suspect that we are doomed, given that it is very late in the Android R
beta cycle. But, maybe we will get lucky, and our contributions can get this cleared
up, or at least documented, by the time Android 11 ships in stable form.

 •  0 comments  •  flag
Share on Twitter
Published on August 28, 2020 10:37

May 29, 2020

Reflection and Composables

For production code in ordinary apps, using reflection is often considered to be poor form. Looking up classes
and functions at runtime can lead
to some difficult-to-debug problems and can result in long-term maintenance headaches.
And, in fairly ordinary app code, reflection usually is unnecessary.



Where reflection starts to become more important is in libraries, tools, and
frameworks. Sometimes, you just do not know in advance what the classes and functions
are that you need to work with. Reflection is used in a bunch of places in the
Android SDK, such as instantiating activities (based on manifest entries)
and fragments (based on names retained after configuration changes).



So, someday, somebody was going to want to invoke a @Composable function from Jetpack
Compose via reflection.



A week or so ago, I was that somebody. ����



The challenge is that what you write in terms of a composable function and what
the compiler compiles are two different things, courtesy of that Kotlin compiler
plugin that powers a lot of the Compose magic.



On dev11, your function gets one additional parameter: a Composer.
Fortunately, getting the Composer is easy: just reference currentComposer.
You can then do something like this to find a zero-parameter top-level @Composable function via reflection and call it:



class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
val clazz = Class.forName("your.package.goes.here.AndYourClassNameTooKt")
val demoMethod = clazz.methods.find { it.name == "YourAwesomeComposable" }

demoMethod?.let {
it.isAccessible = true
it.invoke(null, currentComposer)
}
}
}
}


dev12 changed the rules somewhat. Now, there are 2+ additional Int
parameters. As Google���s Leland Richardson described it in a Slack thread,
the additional parameters are:




the Composer
an Int that serves as a ���key��� (and is scheduled to go away in a future update)
one Int for every 15 parameters to the function that you wrote, representing changes
one Int for every 31 parameters to the function that you wrote, if there are any default expressions in the function declaration


For the purposes of calling some high-level composable via reflection, passing 0
for the ���key��� and ���changes��� values works. So, in dev12, the invoke() changes slightly:



class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
val clazz = Class.forName("your.package.goes.here.AndYourClassNameTooKt")
val demoMethod = clazz.methods.find { it.name == "YourAwesomeComposable" }

demoMethod?.let {
it.isAccessible = true
it.invoke(null, currentComposer, 0, 0)
}
}
}
}


This approach is not particularly maintainable, in that as Compose evolves, so will
the augmented parameter list. With luck, this feature request
will give us access to something like this invokeComposableMethod()
that we can use to more safely call these functions.



On the whole, reflection is something to be used sparingly, carefully, and only
if you are working on something really really cool. But, sometimes, it is your
best option, so having a working recipe for calling composables via reflection is
important. Hence, I���m grateful for Leland���s assistance in identifying how to do
this for the current generation of Compose.

 •  0 comments  •  flag
Share on Twitter
Published on May 29, 2020 15:45

May 28, 2020

Upcoming Presentation on Jetpack Compose

On June 17th, I will be talking about Jetpack Compose at an online Meetup
jointly hosted by the GDG Boston and GDG Toronto Android communities!



This is open to anyone, not just members of those GDGs, so if you are interested,
register to attend!



My talk is entitled ���What to Expect When You���re Expecting��� Jetpack Compose���.



This is not going to be a review of Jetpack Compose syntax and APIs ���
it���s a 30-minute presentation, so anything I cover will be out of date by the
time I���m done. Instead, my focus is on planning: given what we know about Compose
and what we saw with Google���s adoption of Kotlin, how should your project be
preparing for migrating to Compose in the coming years? So, while we will see bits
of Compose code, you do not need to be familiar with Compose, or even Kotlin,
to get value out of the presentation.



I look forward to (virtually) seeing you there!

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

May 26, 2020

���Elements of Android Jetpack��� Version 0.9 Released

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



This update adds one more chapter,
covering dialogs. This also
ties into a new section in the chapter on architecture, looking at the
differences between states and events.



There is also a new section on view binding as an
alternative to data binding, Kotlin synthetic accessors, or older techniques.
Most of the samples that had used Kotlin synthetic accessors or older techniques
have been revised to use view binding.



This update also upgrades a bunch of the dependencies used by the sample apps,
with minor tweaks to the code and the prose to match.



Also, a change in the publishing process for the EPUB and MOBI editions should
improve the readability of some of the images.



And, as usual, it fixes lots of bugs.



0.9 versions are a ���release candidate��� for 1.0. I expect to release Version 1.0
in about a month, with bug fixes but no significant new material.

 •  0 comments  •  flag
Share on Twitter
Published on May 26, 2020 15:30

May 16, 2020

R Raw Paths, DP4 Style

A couple of months ago, I wrote about raw paths and ���all files access���
on Android R, using R DP2. And��� what I had there on raw paths is not completely
accurate for R DP4. I am not certain how much of that represents changes in Android R and how much
of that represents screwups in my original testing.



What I am seeing in R DP4 is:





With no permissions, your app can create files of any type in the Documents/ and Downloads/
directories. Your app can then manipulate those files afterwards.




With READ_EXTERNAL_STORAGE permission, your app can read ���media files��� in any directory
other than Android/. The precise definition of ���media files��� is indeterminate. More
on this later.




Whether files can be read will depend on whether they are indexed by the MediaStore.
So, for example, files that you push up using adb push will not be visible until
sometime after MediaStore indexes them (e.g., after a device reboot).




No additional abilities appear to be gained by holding WRITE_EXTERNAL_STORAGE
(as expected) or having a targetSdkVersion of R.





The second bullet lines up with the minimalist documentation on raw paths,
which states:




Android 11 allows apps that have the READ_EXTERNAL_STORAGE permission to read a device���s media files using direct file paths and native libraries.




(emphasis added)



The definition of ���media files��� is murky. It appears to be tied to MediaStore
indexing, not just file extensions. So, with READ_EXTERNAL_STORAGE:





We can read PNG files




We cannot read text files




We cannot read text files with a .png extension




We cannot read text files created by an app in Documents/ or Downloads/, even
though those apps were able to create the files directly in those directories




We cannot read PNG files that were put on the device that MediaStore does
not know about yet (e.g., the adb push scenario)





So, media file formats that MediaStore does not know about might not be accessible
this way, even if you and your users think of those formats as being ���media���.



If your app is mostly focused on media, this situation may be an improvement
over what we have for Android 10. But, given the undocumented definition of
���media files���, I would be somewhat hesitant to rely upon it.

 •  0 comments  •  flag
Share on Twitter
Published on May 16, 2020 14:37

May 13, 2020

Vet Your Manifest, Again

A couple of years ago, I warned you to vet the contents of your manifest.
But, if you are like me, the past two years have felt like a decade. So,
it���s not unreasonable to have forgotten the lessons from that blog post.
Since a recent bit of news
highlighted this problem again, perhaps it is time to revisit the topic.



What you ship in your app is your job to manage. That includes all
the entries in the merged manifest. It does not matter whether the entries
were added by you, by a library, or by a space alien ����. If it is in your app,
you own it and any problems it causes.



So, there���s this app.
It is not a Web browser.
However, as Kate McNamara pointed out,
the app really wants to be a Web browser, offering to handle random URLs as an
alternative to the user���s real browser.



The app has a splash screen, in the form of a SplashActivity. That activity���s
manifest entry has the following <intent-filter>:



<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:name="http" />
</intent-filter>


If another app calls startActivity() for a matching Intent, and the user chooses to
have this app handle it, this app will
start up and show its SplashActivity. However, that is a very generic <intent-filter>,
having little to do with the app or its maker.



This is not good.



Fortunately, it only honors the insecure http scheme. ����



Having an activity in your app that responds to your own domain name is reasonable.
It is a key element of deep links,
for example. But the <intent-filter> needs to be scoped to your
own site using android:host on the <data> element. In this case, that was not
done, to users��� detriment.



Every so often, scan through your merged manifest and ask yourself: do we need
all this stuff? You might be scared by what you find in there.



And, um, don���t try to be a Web browser. Unless, y���know, you are writing a Web browser.

 •  0 comments  •  flag
Share on Twitter
Published on May 13, 2020 14:39