Unable to start intent service via IMPLICIT intent - android

AndroidManifest.xml
<application android:name=".MyApplication"
android:icon="#drawable/icon"
android:label="#string/app_name"
>
<service android:name=".MyService"
android:exported="true">
<intent-filter>
<action android:name="android.service.myapp.MyService.actionA"/>
<action android:name="android.service.myapp.MyService.actionB"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
If I use the following code, my service is launched:
Intent intent = new Intent(context, MyService.class);
intent.setAction("android.service.myapp.MyService.actionA");
context.startService(intent);
But my service is not started if I launch it with this code:
Intent intent = new Intent("android.service.myapp.MyService.actionA");
context.startService(intent);

It is insecure to use an "implicit" Intent to start or bind with a Service. Starting with Lollipop, bindService() requires an explicit Intent (your first example where you specify the Context and Class for the Service.) The behavior of startService() is undefined for implicit Intents used to start a service. From the documentation on startService():
The Intent should contain either contain the complete class name of a specific service implementation to start or a specific package name to target. If the Intent is less specified, it log a warning about this and which of the multiple matching services it finds and uses will be undefined.
If you use the explicit form, you can completely remove the <intent-filter> from the manifest: it is not needed. If you need to specify some type of work to be done by the service via the Intent, consider using a extra within the Intent.

Related

Receiving broadcast from notification on Android Oreo

I have a custom button in a sticky notification.
I used to attach a PendingIntent to it for receiving button clicks:
Intent intent = new Intent();
intent.setAction("com.example.app.intent.action.BUTTON_CLICK");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 2000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
contentViewExpanded.setOnClickPendingIntent(R.id.button, pendingIntent);
When i run this code on Oreo , i get BroadcastQueue: Background execution not allowed in logcat and don't receive button click.
I registered receiver with manifest:
<receiver
android:name=".BroadcastReceiver.NotificationActionReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.example.app.intent.action.BUTTON_CLICK"/>
</intent-filter>
</receiver>
I also tried registering receiver in my code:
NotificationActionReceiver mMyBroadcastReceiver = new NotificationActionReceiver();
IntentFilter filter = new IntentFilter("com.example.app.intent.action.BUTTON_CLICK");
mContext.registerReceiver(mMyBroadcastReceiver, filter);
This works but only when the app is visible to user.
Thanks for help
Never use an implicit Intent when an explicit Intent will work.
Replace:
Intent intent = new Intent();
intent.setAction("com.example.app.intent.action.BUTTON_CLICK");
with:
Intent intent = new Intent(this, NotificationActionReceiver.class);
And remove the <intent-filter> from the NotificationActionReceiver <receiver> element.
I ran into this issue as well on Android 8 - Oreo, but given my library project requirements, I don't have the explicitly named BroadcastReceiver class implementation, that the end-client will declare in it's AndroidManifest.
Solution:
Specify the application package on the Intent using setPackage(String).
Example:
// Application unique intent action String
final String receiverAction = getApplicationContext().getPackageName()
+ BaseLibraryReceiver.ACTION_SUFFIX;
// No need for Class definition in the constructor.
Intent intent = new Intent();
// Set the unique action.
intent.setAction(receiverAction);
// Set the application package name on the Intent, so only the application
// will have this Intent broadcasted, thus making it “explicit" and secure.
intent.setPackage(getApplicationContext().getPackageName());
...
From the Android Broadcasts: Security considerations and best practices docs.
In Android 4.0 and higher, you can specify a package with
setPackage(String) when sending a broadcast. The system restricts the
broadcast to the set of apps that match the package.
Here’s an example of the BroadcastReceiver declared (or merged) in to the end-client application’s AndroidManifest:
<receiver
android:name=“com.subclassed.receiver.ReceiverExtendedFromLibrary"
android:exported="false"
android:enabled="true">
<intent-filter>
<action android:name="${applicationId}.action.MY_UNIQUE_ACTION"/>
</intent-filter>
</receiver>
Since my example revolves around a library project that broadcasts an Intent, I’ve decided to keep the <intent-filter> and <action /> in the <receiver> declaration. Otherwise, there would be non-unique broadcast actions being fired, which could lead to potential issues where multiple applications receive the wrong broadcast. This is mostly a safety precaution. Of course you still need to check the action in the implementation of the BroadcastReceiver.
Hope someone finds this helpful!

Invoking an action of an Intent Service

I just created a new intentService app in Android Studio and i'm a bit confused as to how to invoke it.
Is it only meant for invocation from another app explicitly?
Can't i declare an intent filter in the manifest and then have it respond to broadcast intents?
For example,
I have an intent filter set up like so
<service
android:name=".RemoteShopKeyService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.srini.remoteshopservice.action.FOO" />
</intent-filter>
</service>
And within the Main service class I handle the intent. However how do I invoke the service with an action other than android.intent.action.MAIN via the adb?
I start the service using adb shell am startservice

How to start a bound service via intent without class

I'm trying to start a bound (eventually remote) Android Service via an Intent. If I create the Intent with a reference to the Service class (see variable ii below) it works. If I create the Intent using Strings for the classname, bindService fails.
Ultimately I'm aiming to have a 2nd application access my service which will not have the Service class within it (that's how remote service things work...? isn't it?) - hence the desire to create the Intent without the reference to the class object.
//this Intent works
Intent ii = new Intent(this, MyService.class);
//this Intent doesn't
Intent i = new Intent();
i.setClassName("com.ghee", "MyService");
boolean b = bindService(ii,
mSvcConn,
Context.BIND_AUTO_CREATE);
if(!b) {
Log.d(TAG, "bindService returned false...");
}
If it helps, here's the majority of the manifest:
<application android:icon="#drawable/icon">
<activity android:name=".MyActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="com.ghee.MyService" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MyService"
android:exported="true"
android:process=":remote">
</service>
</application>
You need to specify the full path of the class, not just its name. This seems counter-intuitive, because you're already specifying the package in parameter 1, but....oh well.
Try this:
i.setClassName("com.ghee", "com.ghee.MyService");

Android service not starting with a warninig

I have a service that supposed to upload photos in the background. But when I try to start it, it doesn't start. In the logcat I've noticed that I get a warning Implicit intents with startService are not safe : Intent { .....}. I've already tripled checked that the action string is the same in the manifest and what I'm starting with. My code :manifest :
<service android:name=".photosupload.services.PhtosUploadService"
android:exported="false" android:process=":uploadPhotosServiceProcess">
<intent-filter>
<action android:name="com.yoovi.app.photosupload.services.action.START_UPLOAD" />
</intent-filter>
</service>
starting service code :
Intent i = new Intent(PhtosUploadService.ACTION_START_UPLOAD);
i.putExtra(PhtosUploadService.ALBUM_ID, albumID);
context.startService(i);
You need to understand implicit and explicit intents.
Explicit intent means you need to specify the exact class from which the intent will be serviced.
Your code should be similar to
Intent i = new Intent();
i.setClass(this, photosupload.services.PhtosUploadService.class);
If you want to send implicit intent to a service - Please Change to android:exported="true" in manifest.

Failing to start an activity with an implicit intent

I am a little confused why the implicit intent call is failing. When trying to start an intent I keep getting the following error:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=content://org.chrisolsen.crossfit.providers.WorkoutProvider/workouts typ=vnd.android.cursor.dir/vnd.chrisolsen.crossfit.workout }
AndroidManifest
<activity android:name=".activities.WorkoutsActivity"
android:label="#string/title_workouts" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.dir/vnd.chrisolsen.crossfit.workout"/>
</intent-filter>
</activity>
<provider
android:name=".providers.WorkoutProvider"
android:authorities="org.chrisolsen.crossfit.providers.WorkoutProvider" />
Calling activity (dashboard)
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(WorkoutProvider.CONTENT_URI, "vnd.android.cursor.dir/vnd.chrisolsen.crossfit.workout");
startActivity(intent);
Called activity (workouts). It doesn't make it here
Uri uri = getIntent().getData();
...
It seems like it should be simple, but I am confused to why it says there is no activity found.
Any ideas?
In order to be started with implicit intents, An activity must declare
<category android:name="android.intent.category.DEFAULT" />
Also, make sure you are using startActivity instead of sendBroadcast. There is a difference between these methods. A broadcast will not be received by an activity's intent filter. You must use a BroadcastReceiver for that.
Note that, although the Intent class is used for sending and receiving these broadcasts, the Intent broadcast mechanism here is completely separate from Intents that are used to start Activities with Context.startActivity(). There is no way for a BroadcastReceiver to see or capture Intents used with startActivity(); likewise, when you broadcast an Intent, you will never find or start an Activity. These two operations are semantically very different: starting an Activity with an Intent is a foreground operation that modifies what the user is currently interacting with; broadcasting an Intent is a background operation that the user is not normally aware of.
Source: http://developer.android.com/reference/android/content/BroadcastReceiver.html
Android docs: sendBroadcast
Android docs: startActivity

Categories

Resources