Targeting Android SDK 30

When Google announced that Play Store apps must target SDK 30 by August, our team started investigating what it would take to meet this requirement. Now, with our migration to SDK 30 complete, we’d like to detail the changes our team made. This is not meant to be an exhaustive migration guide – but hopefully you will find parts of it useful!

Google Android logo
© Google

Limiting usage of Intent#ResolveActivity

Android 30 introduces security restrictions around Package Visibility. You have a couple of options for migrating existing resolveActivity calls: 1.) using a try-catch block around startActivity calls or 2.) keeping your startActivity code as-is, while declaring whatever queries you intend to run in your application manifest.

Mostly, we migrated to try-catch blocks. The process was simple – just move the handling code previously run when myIntent.resolveActivity(context.getPackageManager()) == null into a catch block. Here’s an example of how to launch the Play Store app, with a fallback of opening a web browser:

However, we still had to declare Intent queries in our manifest, due to one use-case requiring a resolveActivity call. In certain document-gathering flows, we launch a “Chooser” Intent with various options for gathering content. Users can choose to provide the document from an existing local file (e.g. a JPEG or PDF), or from a freshly taken camera photo. 

To support the case where users don’t have a valid camera application installed, and prevent that option from appearing in the Chooser, we need to check whether ACTION_IMAGE_CAPTURE intents can be resolved by the system. Therefore we added the following to our manifest:

Camera2 deprecations – less is more

Wealthfront’s app uses the device camera for capturing documents in certain flows – for example, users can photograph a driver’s license to verify their identity before opening an account.
In developing these flows we first attempted writing our own camera interface, resulting in a library of Camera2 code for previewing and capturing from the device camera feed. While we ultimately decided to integrate with a third-party camera library instead of building our own, the remains of our Camera2 project became riddled with deprecations after targeting SDK 30. As the CameraView library was performing very satisfactorily, our solution to these deprecations was straightforward: delete our Camera2 code.

Scoped-storage compliance

Compliance with the scoped-storage framework is enforced starting with Android 30. For us, this meant verifying that ACTION_GET_CONTENT works as intended. Interestingly we found that, because these intents are satisfied by the system’s Storage Access Framework, and not our application, our manifest should no longer require the READ_EXTERNAL_STORAGE permission. 
As part of this exploration we also re-evaluated whether we should change ACTION_GET_CONTENT intents to ACTION_OPEN_DOCUMENT ones. We learned that there are significant use-case differences between the two, and that the relatively restricted (i.e. limited-term content access) ACTION_GET_CONTENT worked just fine for our purposes.

Specifying root View in Espresso tests

With the code changes in place, it was time to verify them by running our integration tests on Android 10 devices. Tests largely executed as before, with one exception: we noticed occasional timeouts in our tests, coupled with failures of Espresso’s matchers being unable to locate the correct root View.

When Espresso runs a View matcher, it does so against a particular root View. Practical examples of differing root Views on Android include the main Activity window, or an AlertDialog window layered on top. Espresso’s root-matching process is typically transparent to developers, as it does some guessing about which root View to target by default. 
While Espresso’s guessing works most of the time, we found that by explicitly specifying a root View, we could get affected matchers working 100% of the time. Below is code from our approach that specifies custom Espresso entry points, as replacements for Espresso.onView:

In addition to adding the above, we added a custom Lint rule to prevent the use of the default View-matching entry point, Espresso.onView.


If you are an app developer in the process of targeting Android 10, I hope you found this post helpful. And more importantly, if you are someone who would like to help Wealthfront build amazing financial products, take a look at our careers page!


The information contained in this communication is provided for general informational purposes only, and should not be construed as investment or tax advice. Nothing in this communication should be construed as a solicitation or offer, or recommendation, to buy or sell any security. Any links provided to other server sites are offered as a matter of convenience and are not intended to imply that Wealthfront Advisers or its affiliates endorses, sponsors, promotes and/or is affiliated with the owners of or participants in those sites, or endorses any information contained on those sites, unless expressly stated otherwise.

All investing involves risk, including the possible loss of money you invest, and past performance does not guarantee future performance. Please see our Full Disclosure for important details.

Wealthfront offers a free software-based financial advice engine that delivers automated financial planning tools to help users achieve better outcomes. Investment management and advisory services are provided by Wealthfront Advisers LLC, an SEC registered investment adviser, and brokerage related products are provided by Wealthfront Brokerage LLC, a member of FINRA/SIPC.   

Wealthfront, Wealthfront Advisers and Wealthfront Brokerage are wholly owned subsidiaries of Wealthfront Corporation.

© 2021 Wealthfront Corporation. All rights reserved.