Unable to launch arbitrary activity since Android 13
Asked Answered
V

6

7

I have a little personal app I built which allows me to specify different browsers for different URLs. Up until Android 13, it was working fine, but at some point after Android 13 it started failing. I suspect it's related to the app's authority (or lack thereof) to launch an arbitrary Activity, but wading through the docs has yielded zilch.

The process works like this:

I query all activities for an Intent that has a URI as its data property

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri); // uri is some location like 'https://www.google.com'
PackageManager pm = context.getPackageManager();
List<ResolveInfo> allTargets = pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);

I loop through allTargets looking for the browser I want based on its name:

ResolveInfo target = null;
for (ResolveInfo b : allTargets) {
    String appName = b.loadLabel(pm).toString();

    // targetBrowserName will be something like "Chrome"
    if(appName.equalsIgnoreCase(targetBrowserName)) {
        target = b;
        break;
    }
}

I then attempt to launch this browser, with the url

ActivityInfo activity = target.activityInfo;

ComponentName name = new ComponentName(activity.applicationInfo.packageName, activity.name);
targetIntent = new Intent(Intent.ACTION_MAIN);
targetIntent.addCategory(Intent.CATEGORY_LAUNCHER);
targetIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
targetIntent.setComponent(name);
targetIntent.setData(uri);
startActivity(targetIntent);

This now fails with an error like:

android.content.ActivityNotFoundException: Unable to find explicit activity class {com.android.chrome/com.google.android.apps.chrome.IntentDispatcher}; have you declared this activity in your AndroidManifest.xml, or does your intent not match its declared <intent-filter>?
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4803)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4836)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54)
        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7898)
        at java.lang.reflect.Method.invoke(Native Method)

I've tried various permutations of the launch code (which, as a reminder, was working fine). E.g.

targetIntent = pm.getLaunchIntentForPackage(activity.applicationInfo.packageName);
targetIntent.setAction(Intent.ACTION_VIEW);
targetIntent.addCategory(Intent.CATEGORY_BROWSABLE);
targetIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

But I still get the same error (although in the above case with a different Activity class that is still fails to find)

I understand there are constraints on App visibility, but I assumed I was covered as I have this in my AndroidManifest.xml

    <!-- As per guidelines, QUERY_ALL_PACKAGES is required to list all browsers -->
    <uses-permission
        android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />

Reading the docs, I noted that I didn't have a <queries> element in the manifest (is that new?), so I added this:

    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
        </intent>
    </queries>

No joy.

Does anyone know the correct way to launch a known/specific [browser] app based programmatically? Or maybe what changed in Android 13 to make this code work again?

Thanks!

Edit following correct answer below

The guidance provided in the answer below worked. This is a summarized version of the final code:

// Create an intent with the destination URL
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);

// List all activities that support this intent, and choose one:
PackageManager pm = context.getPackageManager();
List<ResolveInfo> allTargets = pm.queryIntentActivities(intent, PackageManager.MATCH_ALL);
ResolveInfo target = null;
for (ResolveInfo b : allTargets) {
    String appName = b.loadLabel(pm).toString();

    // targetBrowserName is something like "Chrome"
    if(appName.equalsIgnoreCase(targetBrowserName)) {
        target = b;
        break;
    }
}

// Set the specific component to be launched
ActivityInfo activity = target.activityInfo;
ComponentName name = new ComponentName(activity.applicationInfo.packageName, activity.name);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setComponent(name);

// Start
startActivity(intent);
Vasectomy answered 26/8, 2022 at 2:49 Comment(1)
I'm also seeing similar crash logs on the play console but am unable to reproduce it locally on an Android 13 device. How were you able to reproduce this crash?Twofaced
J
9

Seems to be related to this behavior change https://developer.android.com/about/versions/13/behavior-changes-13#intent-filters

When your app sends an intent to an exported component of another app that targets Android 13 or higher, that intent is delivered if and only if it matches an element in the receiving app. Non-matching intents are blocked.

To make sure this is indeed your issue check logcat for the tag PackageManager. Something like W/PackageManager( 1828): Access blocked: ComponentInfo{com.android.chrome/com.google.android.apps.chrome.Main} should appear there

I didn't look too much into how it works yet, but seems like now we need to query external activities before using them unless they don't have intent-filters. That's going to "activate" the component and then the intent you use needs to "match" the other apps intent-filter:

Intents are matched against intent filters not only to discover a target component to activate, but also to discover something about the set of components on the device. For example, the Home app populates the app launcher by finding all the activities with intent filters that specify the ACTION_MAIN action and CATEGORY_LAUNCHER category. A match is only successful if the actions and categories in the Intent match against the filter, as described in the documentation for the IntentFilter class.

https://developer.android.com/guide/components/intents-filters#imatch

I was having a similar issue. The reason for it not to work was that I modified the Uri by calling intent.setData after the call to queryIntentActivities. Seems like this is invalidating the activation of the component.

Summary: not calling intent.setData after the queryIntentActivities makes the intent work

Jimmy answered 26/8, 2022 at 16:33 Comment(2)
Thanks for the detailed answer. I'll give this a try and post back with findingsVasectomy
I'm also seeing similar crash logs on the play console but am unable to reproduce it locally on an Android 13 device. How were you able to reproduce this crash?Twofaced
E
2

There was an error

Unable to find explicit activity class {com.google.android.apps.translate/com.google.android.apps.translate.TranslateActivity}; have you declared this activity in your AndroidManifest.xml, or does your intent not match its declared <intent-filter>?

In my case help to remove intent.addCategory(Intent.CATEGORY_LAUNCHER) for Android 13

final Intent intent = new Intent();
intent.setAction(Intent.ACTION_PROCESS_TEXT);
intent.setType("text/plain");


//Fix for Android 13
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
}

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setComponent(name);

intent.putExtra(Intent.EXTRA_TEXT, selecteText);
intent.putExtra(Intent.EXTRA_PROCESS_TEXT, selecteText);
intent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, selecteText);

a.startActivity(intent);
Elsey answered 15/9, 2022 at 10:27 Comment(0)
F
0

Add schema to your intent filter in your exported activity. That help me for SDK 33

    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="https"/>
    </intent-filter>
Felixfeliza answered 20/1, 2023 at 13:40 Comment(0)
E
0

I had same problem but i had error for not match intent and intent filter in android 13.

I defined memeType for data tag in intent filter

and used setDataAndType instead of setData when sent intent particularly when the data is a content:
check this link

https://developer.android.com/guide/components/intents-filters#imatch:~:text=Specifying%20the%20MIME,to%20the%20system.

<receiver android:name="com.android.calendar.alerts.AlertReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="maddev.calendar2.REMIND_ME"/>
                <data android:scheme="content" android:mimeType="text/plain"/>
            </intent-filter>

Intent intent = new Intent(CalendarContract.ACTION_EVENT_REMINDER);
        intent.setComponent(
                new ComponentName(
                        ActivityUtils.INSTANCE.getInstalledEtarAppPkg(context),
                        "com.android.calendar.alerts.AlertReceiver"
                )
        );
        intent.setDataAndType(ContentUris.withAppendedId(CalendarAlerts.CONTENT_URI, alarmTime),"text/plain");
        intent.putExtra(CalendarAlerts.ALARM_TIME, alarmTime);
        intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);

in this case i solved my problem in Android 13

Emerald answered 17/4, 2023 at 12:24 Comment(0)
S
0

I ran into the same error message when I adapted my applications to Android 13. In particular Application A crashed when calling startActivity(myIntent) to open Application B.

First, the error message is not total nonsense, but highly misleading. In Android 13 both category and action in MyIntent must match those in the intent filter of Application B. In my case the category was wrong.

Second, the call succeeds without exception, while Application A successfully requested MANAGE_EXTERNAL_STORAGE. When Application A returns that permission, it will crash again. I think, one might consider, that this is a misbehaviour in Android 13.

Third, it is generally a good idea to catch the exception and show an error message to avoid sudden crashes without user feedback.

Scape answered 7/1, 2024 at 11:18 Comment(0)
P
-1

If you're running a SDK target below 33 do:

if (Build.VERSION.SDK_INT < 33) {
    intent.addCategory(Intent.CATEGORY_LAUNCHER)
}
Pepi answered 19/1, 2023 at 9:53 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.