How to start android service from another Android app - android

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

Related

Unable to launch arbitrary activity since Android 13

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>

Switching between apps in same platform using xamarin Implimentation

I have a question in mobile apps using Xamarin. Suppose i have implemented 2 Apps like App1 and App2 in same platform(Exp - Android).
As per the business scenario From the App1 I need to switch to App2 in same platform(Assume both apps deployed in android device). But I am not sure how to do this using the Xamarin. Could you please help on this.
Do you mean you want to open App2 in App1 ?If yes
Here are several methods for it:
(1)use package name, specific Activity name pulled up
Intent intent = new Intent(Intent.ActionMain);
/** knows the packet life to jump to and the target Activity*/
ComponentName componentName = new ComponentName(package name, class name);//e.g ComponentName componentName = new ComponentName("kuyu.com.xxxx", "kuyu.com.xxxx.xxx.login.WelcomeActivity");
intent.SetComponent(componentName);
StartActivity(intent);
App2 needs to be added in the manifest file corresponding to the Activity:
android:exported="true"
(2) the package name is pulled up (this is going into the startup page)
Intent intent = PackageManager.GetLaunchIntentForPackage(package name);//e.g Intent intent = PackageManager.GetLaunchIntentForPackage("kuyu.com.xxxx");
if (intent != null)
{
intent.SetFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
(3) url pull up (which as the answer above)
using Xamarin.Essentials;
await Browser.OpenAsync(uri, BrowserLaunchMode.SystemPreferred); }
Or
https://xamarinhelp.com/launching-mobile-app-via-uri-scheme/
I think this will help, and look deep linking in Xamarin :)

Binding to background services fails on Android OS 8

I need to start a few services provided by other apps, let them execute whatever the want to execute, and kill them once they call me back and I receive the data collected. Sadly startService(serviceIntent); crashes with
Unable to start activity ComponentInfo{…}: java.lang.IllegalStateException: Not allowed to start service Intent {…}: app is in background uid UidRecord{70fcf01 u0a90 CEM bg:+1h49m29s56ms idle procs:1 seq(0,0,0)}
It appears that this is by design, here's the crashing AOSP code.
My code crashing looks like this:
final PackageManager pm = getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory("my.special.service.CATEGORY");
List<ResolveInfo> results = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
for (ResolveInfo info : results) {
ComponentName name =
new ComponentName(info.serviceInfo.applicationInfo.packageName, info.serviceInfo.name);
Intent serviceIntent = new Intent();
serviceIntent.setComponent(name);
startService(serviceIntent); // 💥 K A B O O M
bindService(serviceIntent, boundServiceConnection, BIND_AUTO_CREATE);
}
The way service is defined in another app's Manifest.xml:
<service
android:name=".ServiceFromAnotherApp"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<categoy android:name="my.special.service.CATEGORY" />
</intent-filter>
</service>
Any ideas if there're any workarounds, maybe different architecture?
Core trait that I want to retain is:
to be able to create a service
tell it to fetch additional data (potentially several times during its lifecycle)
get callback from the service once it finishes every subsequence fetch

"Launching" another app with services

I have 2 different apps in 2 different Application Projects in Eclipse. I will call them H and G. Until now, I have been able to launch H from G by using
Intent intent = getPackageManager().getLaunchIntentForPackage("com.xxx.h");
startActivity(intent);
This has been working great, but in reality, I don't really need the H app to launch, I just need to do something with that app in the background, so I started looking into services. So now I am trying to use
Intent intent = new Intent();
intent.setClassName("com.xxx.h","com.xxx.h.MyService");
startService(intent);
But now I am getting the error saying
W/ActivityManager(1044): Unable to start service Intent { cmp=com.xxx.h/.MyService } U=0: not found
Very new to Services and even Intents so I am guessing it is something simple that I am missing and hoping you guys can help.
EDIT
I fixed the issue with is saying that I was unable to start service intent. That was fixed by including
service android:name=".MyService"
to the AndoirdManifest.xml in H. Now I am getting
E/AndroidRuntime(1022): java.lang.IllegalStateException: Could not execute method of the activity
EDIT 2
Found out it was not letting me start without permission so I had to include android:exported="true" in the AndroidManifest.xml as well
Try this:
private void startService(String aServiceName) {
if (aServiceName.trim().length() > 0) {
try {
Context ctx = getApplicationContext();
Intent iServiceIntent = this.ctx.getPackageManager().getLaunchIntentForPackage(aServiceName);
ctx.startActivity(iServiceIntent);
Thread.sleep(800);
} catch (Exception e) {
}
}
}
The initial problem of it being unable to start the service was because I did not include the <service> to the AndroidManifest.xml which fixed that issue, and then the could not execute error was caused by a permissions issue. So all in all, all it took to fix my issues was including
<service
android:name=".MyServiceHomework"
android:exported="true"
/>
To the AndroidManifest.xml of my H (the service) project

Android : Launch Music Player via Service

I am trying to launch the MediaPlayer from a service, and its not wroking as expected. I m getting the following exception,
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.MUSIC_PLAYER flg=0x10000000 }
Please find the snippet of code that gets invoked in the service,
Intent intent = new Intent(MediaStore.INTENT_ACTION_MUSIC_PLAYER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Android Manifest
<service android:name="com.lakshmi.shakenfun.AlertService" >
<intent-filter >
<action android:name="android.intent.action.MUSIC_PLAYER" />
</intent-filter>
</service>
Please do let me know, where I am doing wrong.
My target platform is 8
Thanks,
Ramesh
Perhaps your Target Platform of 8 is too low for that api? Do you have this music player loaded? https://play.google.com/store/apps/details?id=com.google.android.music&feature=search_result#?t=W251bGwsMSwyLDEsImNvbS5nb29nbGUuYW5kcm9pZC5tdXNpYyJd
MediaStore.INTENT_ACTION_MUSIC_PLAYER is deprecated from API 15 the new code is:
try {
String pkgname = "com.sec.android.app.music";
PackageManager pkgmanager = getPackageManager();
Intent intent = pkgmanager.getLaunchIntentForPackage(pkgname);
startActivity(intent);
} catch(Exception e) {
// music player not found
}
Just add this code on your button listener and it will call the default music player.

Categories

Resources