Unable to launch arbitrary activity since Android 13 - android

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);

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

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);

If you're running a SDK target below 33 do:
if (Build.VERSION.SDK_INT < 33) {
intent.addCategory(Intent.CATEGORY_LAUNCHER)
}

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>

Related

ShowCallLog Intent not working when handled by "com.android.contacts"

I am trying to open the call logs activity from my app using an intent and startActivity(ForResult).
It does work fine, except on some devices.
After investigation, I noticed that when the intent resolve to this class:
ComponentName("com.android.contacts", "com.android.contacts.NonPhoneActivity")
then startActivity (or startActivityForResult) has "no effect" / the CallLogs is NOT displayed.
As per the name 'NonPhoneActivity', this makes sense I guess..
(info about this NonPhoneActivity activity :
https://android.googlesource.com/platform/packages/apps/Contacts/+/master/AndroidManifest.xml )
Here is the code (FYI, with startActivityForResult; I observe the same behavior with startActivity:
additionalButtonsBinding.phoneSelectContactFragmentButtonsMissedCallButton.setOnClickListener(v -> {
Intent showCallLog = new Intent();
showCallLog.setAction(Intent.ACTION_VIEW); // "android.intent.action.VIEW"
showCallLog.setType(CallLog.Calls.CONTENT_TYPE); // "vnd.android.cursor.dir/calls"
showCallLogLauncher.launch(showCallLog);
});
showCallLogLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
// result is always {resultCode=RESULT_CANCELED, data=null}
// may the CallLogs be shown OR not
// so startActivityForResult is useless: just here fore investigation purpose and completeness of my question
Log.d(TAG, result.toString());
}
});
Questions:
Why does the CallLogs intent resolve to ComponentName("com.android.contacts", "com.android.contacts.NonPhoneActivity")... even on a Phone!
On emulator, the intent resolve to
"com.android.dialer",com.android.dialer.main.impl.MainActivity",
which is able to display CallLogs.
Since ("com.android.contacts", "com.android.contacts.NonPhoneActivity") is sometimes/often the only component (*) able to handle Intent { act=android.intent.action.VIEW typ=vnd.android.cursor.dir/calls } , should I use another intent (another action) to display CallLogs?Which other Intent should I use to display CallLogs?
*: as per result PackageManager.queryIntentActivities(showCallLog, PackageManager.MATCH_ALL) , which is a list with only one item, being "com.android.contacts", "com.android.contacts.NonPhoneActivity"
Nailed it (almost):
When Google Dialer (Google Phone) app is installed, the following intent action is available: com.android.phone.action.RECENT_CALLS:
Intent googleDialerShowRecent = new Intent("com.android.phone.action.RECENT_CALLS");
if (context.getPackageManager().queryIntentActivities(googleDialerShowRecent, PackageManager.MATCH_ALL).size() > 0) {
showCallLogLauncher.launch(googleDialerShowRecent);
}
I just do not knwow how to force the Google Phone app to display the 'History'/'Recent calls' tab on launch.
Google Phone's manifest excerpt:
<activity
android:name="com.android.dialer.main.impl.MainActivity"
...>
<intent-filter android:label="#ref/0x7f150123">
<action android:name="com.android.phone.action.RECENT_CALLS" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.TAB" /> TAB HERE - Could I send some EXTRA in INTENT to force Google Dialer to display the 'recent call' TAB?
</intent-filter>
Question:
In manifest we can see this: <category android:name="android.intent.category.TAB" />
How could I use this to force Google Phone app to display the 'recent call' TAB? Adding some EXTRA in the Intent? which one?
(I know, we do not have source code of Google Phone app, but, hopefully they use some standard system / naming convention for EXTRA's in Intent)..
Anyone?

android explicit intent by name not resolving to other activity

I have 2 apps that are used in conjunction with one another. I want to create a button that launches app A from app B so I am creating an intent like so in app B:
Intent intent = new Intent("com.org.orgmobile.android.action.ACTION_CUSTOM");
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setData(Uri.parse(url));
startActivity(intent);
where url is https://org.org.com/mobile/Support/action/org/ActionEnumValue/?a=123
the intent filter I have declared in app A's manifest is like so:
<intent-filter>
<action android:name="com.org.orgmobile.android.action.ACTION_CUSTOM"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
<data android:host="org.org.com"/>
<data android:pathPrefix="/mobile/Support/action/${manifestplaceholderattr}.*/ActionEnumValue/*"/>
</intent-filter>
and manifestplaceholderattr is an empty string.
I am getting
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.org.orgmobile.android.action.ACTION_CUSTOM dat=https://org.org.com/... flg=0x20000000 }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1936)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1615)
at android.app.Activity.startActivityForResult(Activity.java:4472)
at android.app.Activity.startActivityForResult(Activity.java:4430)
at android.app.Activity.startActivity(Activity.java:4791)
at android.app.Activity.startActivity(Activity.java:4759)
What am I overlooking here? I have tried using pathPattern instead of path prefix with no success.
Edit: to provide more detail, I don't just want to launch that specific activity, I want to match the specific intent associated with that activity to launch the activity. I don't want to launch the activity directly because in the case that the user's app is out of date because it won't have the desired behavior.
Best way to do this through Package manager as below.
PackageManager manager = getPackageManager();
try {
Intent intent = manager.getLaunchIntentForPackage("app B package name here");
if (intent == null)
throw new PackageManager.NameNotFoundException();
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
Package manager checks if app with the given package name is installed, if exists it launches the app else it throws NameNotFoundException
You need to use pathPattern instead of pathPrefix. Also you should use .* instead of * at the end of the pattern:
<data android:pathPattern="/mobile/Support/action/${manifestplaceholderattr}.*/ActionEnumValue/.*"/>
This may also not work. If I recall, pathPattern isn't a real regular expression and you may not be able to match the URL like this. Try it and see. If this doesn't work, you'll probably need to rely on just the first part of the path. Let me know how it goes.
See the documentation on path, pathPattern and pathPrefix.
You need to use code as below to launch other App using custom action.
Intent intent = new Intent();
intent.setAction("com.org.orgmobile.android.action.ACTION_CUSTOM");
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setData(Uri.parse(url));
startActivity(intent);
Also make sure both of your application is installed. Also it is better to have check as below to make sure application exists on the phone to handle such Intent.
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

How to set default app launcher programmatically?

I am creating a launcher (kiosk) app that will be downloadable through google. When first installing this application the user has the ability of choosing which launcher (mine or the stock) will be the default. I am trying to bring this up manually if the user does not make my application the default launcher. I want the user to be forced into selecting ALWAYS instead of JUST ONCE when that dialog comes up, otherwise the dialog will continue to appear periodically with a friendly message. This is what I have attempted so far.
I created a method to check for if my application is the default
/**
* method checks to see if app is currently set as default launcher
* #return boolean true means currently set as default, otherwise false
*/
private boolean isMyAppLauncherDefault() {
final IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
List<IntentFilter> filters = new ArrayList<IntentFilter>();
filters.add(filter);
final String myPackageName = getPackageName();
List<ComponentName> activities = new ArrayList<ComponentName>();
final PackageManager packageManager = (PackageManager) getPackageManager();
packageManager.getPreferredActivities(filters, activities, null);
for (ComponentName activity : activities) {
if (myPackageName.equals(activity.getPackageName())) {
return true;
}
}
return false;
}
Then I make the attempt of launching the chooser
/**
* method starts an intent that will bring up a prompt for the user
* to select their default launcher. It comes up each time it is
* detected that our app is not the default launcher
*/
private void launchAppChooser() {
Log.d(TAG, "launchAppChooser()");
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
When I do this I am not receiving the choice between my app and the stock launcher. I tried using startActivity(Intent.createChooser(intent, "Please set launcher settings to ALWAYS")); and I get the choices between my app and the stock launcher, however, I don't get the options ALWAYS or JUST ONCE.
I can create a custom dialog for this instead of launching chooser but I need to know how to set the default app launcher programmatically. Thanks in advance!
This is actually possible with a little workaround:
Create an empty Activity that acts as a launcher called FakeLauncherActivity. Add it to your manifest as a disabled component:
<activity
android:name="com.path.to.your.FakeLauncherActivity"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Check whether your desired launcher activity is the default one (with the isMyAppLauncherDefault() from your question).
If not, offer the user to choose the preferred launcher activity like this:
public static void resetPreferredLauncherAndOpenChooser(Context context) {
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, com.path.to.your.FakeLauncherActivity.class);
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Intent selector = new Intent(Intent.ACTION_MAIN);
selector.addCategory(Intent.CATEGORY_HOME);
selector.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(selector);
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);
}
This method temporarily enables FakeLauncherActivity, which leads to a change in the set of available launcher activities, which forces Android to forget its default launcher. You will see something like...
521-735/system_process I/PackageManager﹕ Result set changed, dropping preferred activity for Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000000 } type null
... in your log.
The method then simply opens a launcher intent where you can see all installed launchers and the buttons "Always" / "Just once".
Finally, the method disables FakeLauncherActivity again so that it doesn't display in the list.
You could repeat that as often as you want and only let the user proceed if your desired launcher activity is set as default.
The isMyAppLauncherDefault() function in the question doesn't always work for some reason.
This code might be better for determining what is the default package for the HOME screen.
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
String currentHomePackage = resolveInfo.activityInfo.packageName;
I want the user to be forced into selecting ALWAYS instead of JUST ONCE when that dialog comes up
That is not possible, except perhaps on rooted devices, barring some security flaw in Android.
When I do this I am not receiving the choice between my app and the stock launcher
Correct. If a default has already been chosen, this will simply launch the default.
I tried using startActivity(Intent.createChooser(intent, "Please set launcher settings to ALWAYS")); and I get the choices between my app and the stock launcher, however, I don't get the options ALWAYS or JUST ONCE.
Correct. createChooser() forces a choice, but does not allow setting a default.
Android Q (API 29) has RoleManager. Let's say your app is a Launcher.
[AndroidManifest.xml]
<activity
... />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
private val startForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
// Perhaps log the result here.
}
}
private fun showLauncherSelection() {
val roleManager = requireActivity().getSystemService(Context.ROLE_SERVICE)
as RoleManager
if (roleManager.isRoleAvailable(RoleManager.ROLE_HOME) &&
!roleManager.isRoleHeld(RoleManager.ROLE_HOME)
) {
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME)
startForResult.launch(intent)
}
}
When you call showLauncherSelection(), you should see a dialog similar to the following.
There are other roles, such as ROLE_BROWSER, ROLE_DIALER, ROLE_SMS, and such.
If you are implementing Google EMM solution (Dedicated Device):
https://developer.android.com/work/dpc/dedicated-devices/cookbook
// Create an intent filter to specify the Home category.
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
// Set the activity as the preferred option for the device.
ComponentName activity = new ComponentName(context, KioskModeActivity.class);
DevicePolicyManager dpm =
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.addPersistentPreferredActivity(adminName, filter, activity);

Android: REmove icon from springboard and launch this application from other application

How to remove application icon from springboard but I don't want to uninstall, just remove icon from springboard and this app will be launched from another application. In a simple word, I want to make another springboard like this and launch application from there not from anywhere else. Is this possible?
To remove application from springboard you can try this:
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context,LauncherActivity.class);
packageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);
Now to start your application from another application you can do the following steps:
1.Create an intent with action=MAIN and category=LAUNCHER
2.Get the PackageManager from the current context using context.getPackageManager
3.packageManager.queryIntentActivity(, 0) where intent has category=LAUNCHER, action=MAIN or packageManager.resolveActivity(, 0) to get the first activity with main/launcher
4.Get the ActivityInfo you're interested in
5.From the ActivityInfo, get the packageName and name
6.Finally, create another intent with with category=LAUNCHER, action=MAIN, componentName = new ComponentName(packageName, name) and setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
7.Finally, context.startActivity(newIntent)
In the AndroidMainfest.xml of your application, remove the below line for the activity that you do not want to have the launcher.
<category android:name="android.intent.category.LAUNCHER" />
and change android.intent.action.MAIN action to something that is your app specific
<action android:name="android.intent.action.MAIN" />
For example, one can change it to below to represent custom intent action:
<action android:name="com.example.action.MAIN" />
Now, you should be able to launch this application from another application using something like this:
Intent intent = new Intent("com.example.action.MAIN");
currentActivity.startActivity(intent);
More info here: http://developer.android.com/guide/components/intents-filters.html

How to start android service from another Android app

I'm having a problem starting a service from another Android app (API 17).
However, if I do run 'am' from the shell, the service starts fine.
# am startservice com.xxx.yyy/.SyncService
Starting service: Intent { act=android.intent.action.MAIN cat=
[android.intent.category.LAUNCHER] cmp=com.xxx.yyy/.SyncService }
(service starts fine at this point)
# am to-intent-uri com.xxx.yyy/.SyncService
intent:#Intent;action=android.intent.action.MAIN;
category=android.intent.category.LAUNCHER;
component=com.xxx.yyy/.SyncService;end
So, it doesn't look like I'm missing anything from the intent when I do the same in the code:
Intent i = new Intent();
i.setAction(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_LAUNCHER);
i.setComponent(new ComponentName("com.xxx.yyy", ".SyncService"));
ComponentName c = ctx.startService(i);
if (c == null) { Log.e(TAG, "failed to start with "+i); }
What I get is (the service is not running at that time):
E/tag( 4026): failed to start with Intent {
act=android.intent.action.MAIN
cat=[android.intent.category.LAUNCHER]
cmp=com.xxx.yyy/.SyncService }
I don't have an intent filter on the service, and I don't want to set one up, I'm really trying to understand what am I doing wrong starting it through its component name, or what may be making it impossible to do so.
You should be able to start your service like this:
Intent i = new Intent();
i.setComponent(new ComponentName("com.xxx.yyy", "com.xxx.yyy.SyncService"));
ComponentName c = ctx.startService(i);
You don't need to set ACTION or CATEGORY if you are specifying a specific component. Make sure that your service is properly defined in the manifest.
Start your service like this
Intent intent = new Intent();
intent.setComponent(new ComponentName("pkg", "cls"));
ComponentName c = getApplicationContext().startForegroundService(intent);
btw you actually need to use the applicationId, instead of the pkg. it can be found in the app gradle. I was struggling with that mistake for hours!
defaultConfig {
applicationId "com.xxx.zzz"
}
the cls is the name of your service declared in the manifest. example: com.xxx.yyy.yourService.
<service android:name="com.xxx.yyy.yourService"
android:exported="true"/>
As an addition to David Wasser's answer to make it work when targeting API 30 and above you also have to add either:
Required package name in queries tag in manifest:
<queries>
<package android:name="com.example.service.owner.app" />
</queries>
or permission
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
Additional info on package visibility changes here

Categories

Resources