Intent Resolving in Android M

Warning! Resolving of “Implicit Intent”s in Android Marshmallow is not working as the same as before. This may break your app’s behavior.

Let me explain the expected behavior and why is not working:

I have recently been working on a small open source project called “Open Link With”. It will hopefully be in the Play Store soon.

My app gives you the ability to switch between other apps. When you share a link with me, I basically grab the link and query all the Activity’s that can handle that link. And finally I mimic the system dialog and let you switch apps.

Switching from already opened youtube web-page to Youtube app.

I was using the below method as always:

List<ResolveInfo> infos = packageManager
.queryIntentActivities(intent, MATCH_DEFAULT_ONLY);

It is a familiar method to almost all Android developers and I am sure that it is currently being used in lots of apps.

I have 2 browsers in my phone. “An Intent with Google+ URL” is expected to give a list of 3 ResolveInfo objects (Google+ app and 2 browsers).

Well, not anymore!

Welcome to Android Marshmallow!

Android Marshmallow introduced App Links. The system basically authenticates with your web-page and opens those URLs automatically using your application without asking anything to user. Or you can go to system Settings, Apps, click an app, click “Open by default” and then set “Open in this app” to use always that app.

Application default setting page in Marshmallow

In that case, queryIntentActivities method will give developers a list with only 1 Activity (which is Google+ in this case).

Even if this is the desired behavior, this should be documented because it breaks the behavior of a public API.

I researched a little bit and found MATCH_ALL flag. It’s documentation says that it will disable all system level filters.

* Querying flag: if set and if the platform is doing any filtering of the results, then
* the filtering will not happen. This is a synonym for saying that all results should
* be returned.

public static final int MATCH_ALL = 0x00020000;

It didn’t do anything for me. I opened the source code (at least we have that!) and investigated the method.

It looks like they prioritize the domain verified applications. They did not just prioritize in their internal system, they also did it in the public API.

If there is a domain verified application, it does not return anything else. MATCH_ALL flag removes some system filters but only if there is no verified application.

I couldn’t find any way to workaround this behavior. It simply eliminates browser apps even when their IntentFilters match.

There is no workaround because it is an internal component (which we cannot access) and Android SDK communicates with it using IPC communication using AIDL.

Most of the developers are using this method to find if there is at least 1 Activity to handle the implicit Intent. In most of the situations the first item in the list is the one you want anyway.

After spending hours to understand what’s going on and trying to find a workaround, I thought everyone should know this.

There are lots of behavior changes in Android M. Google actually provided a list of behavior changes. You can find it here. I am sure that just like this, there are lots of hidden behavior changes that may break applications.

So beware of this, and check your situation if you use PackageManager methods.

Thanks to the people who proofread this article: Yağmur Dalman, Sebastiano Poggi, Salim KAYABAŞI, Hasan Keklik

Follow me on @tasomaniac and +SaidTahsinDane