I'm creating an app using the new Android build system, and using a LocalBroadcastManager to fire notifications from an IntentService back to an Activity.
I've defined the following product flavours as part of the build for 3 separate apps;
productFlavours {
london {
packageName 'com.example.lo'
}
dubai {
packageName 'com.example.du'
}
}
When I build the app using either of these product flavours I am not getting any broadcasts through the manager. I've confirmed that both the service and the activity are running in the same process.
A broadcast receiver is bound as required in the Activity#onCreate method of my activity but does not receive any events. I have also confirmed by printing out the process ID of both the activity and service and both are reporting that this is the same process, so this can't be an issue.
As a test I removed the product flavours and built the app with just the single packagename as defined in the app manifest file and now the broadcast manager seems to function as expected. On the intents that I'm sending I am only setting the action.
Do I need to set anything else?
If you look at the source code of LocalBroadcastManager you'll see that if you give your Intent the flag Intent.FLAG_DEBUG_LOG_RESOLUTION (by using Intent.addFlags(int)), it will print in the logcat pretty useful information: you might want to try that with and without the flavours and compare the result to see what goes wrong.
I have org.cmpny.app and org.cmpny.app.additional namespaces in same project; LocalBroadcastManager works fine, nothing is required to be tuned in AndroidManifest (except that LocalBroadcastManager can't be used with ":remote" services, but that's another point)
Related
We have an app with an Activity that can be started in two ways:
From another Activity - always with some extra data filled in
From deep linking
As far as I can see this is always working just fine. We either get the Intent.ACTION_VIEW with a data URI, or we get some string extras.
However, we have a very few instances where action is Intent.ACTION_MAIN and there are no extra data.
The toString() of the Intent is as follows (class name changed):
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10400000 cmp=com.example.OurActivity }
Intent.getExtras() returns null, Intent.getDataString() returns null.
In which cases can this happen? Why is the category for the Activity Intent.CATEGORY_LAUNCHER? How can we get the data needed to show the user the right contents?
launchMode is not specified for the Activity. The only IntentFilter in AndroidManifest.xml is for the deep linking (and not the launcher category).
The issue happens on Android 4-6 on a wide range of devices.
Edit: Forgot to mention the flags:
As the print out suggests the flags for the Intent are FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_BROUGHT_TO_FRONT. I suppose that could be relevant here.
I believe, I nailed it:
There're launchers, like Nova Launcher which allows users to start with any of app's Activities, instead of the normal flow:
For example, you can add a shortcut on your desktop to start Gmail client with Account setup Activity.
And in this case, Activity is being started with empty Extras and technically it becomes a launcher's Activity.
Now that AndroidManifest.xml is manipulated by the build system, it often happens that libraries that you include also add things to the manifest, which I suspect may be happening here.
Although you state that there is only one IntentFilter in the manifest, have you actually checked the installed app to see what its manifest says (rather than relying on what you think you put in your source code)?
Various apps are available in the Play Store to show you the manifest of an installed app - including App Detective (which I wrote).
I'm looking to expand my app to handle Guest Mode, introduced in Android L. I found that if I create a service with android:singleUser in AndroidManifest, with permission INTERACT_ACROSS_USERS, and I'm a system app by installing it in /system/priv-app, then my service is running even as I switch user. But my app needs to interact with the user, by being able to launch an activity, show a toast or notification. All of those things seems to not be possible. Is there a particular flag I need to set when I call startActivity so that it will launch a new activity from my service?
I found a way to do it. Basically have a singleton Service, which is a service with the android:singleUser="true" and with INTERACT_ACROSS_USERS and have the APK installed in /system/priv-app. Then have it broadcastAsUser to all users. You'll need to use reflection to access methods in UserManager. Then have a receiver instance which will receive the broadcast in the guest user's space, and then have the receiver startActivity.
There are several internal apis (comments as #hide) like Context.startActivityAsUser, NotificationManager.notifyAsUser to support it, but it needs build from source also with platform signature.
I have two android applications with different packages App1 and App2. Suppose I want to call a method Method1 written in App1, from App2. One solution I found in the following link, Android call method from another app, suggested that we should register a BroadcastReceiver in App1 and call sendBroadcast() from App2. But the problem is, I could call the Method1 only if App1 is running in the background. Otherwise, nothing is happening.
How to resolve this issue? Are there any other ways to call Method1 without having to start App1?
But the problem is, I could call the Method1 only if App1 is running in the background.
This is incorrect, if you register any component (BroadcastReceiver, Service, Activity, etc.) in the AndroidManifest.xml and it is exported, other applications can trigger it with an Intent regardless of the current state of the application process.
Perhaps the issue you are running into is that the example you linked to registers the BroadcastReceiver in Java code. If you instead publish the <receiver> in your manifest, it will be externally accessible always. This is explained in the SDK Documentation for BroadcastReceiver.
I have an app which uses a service that is included in the application's package.
I am now making a new app which uses a newer version the same service, bundled within its own application package.
If only one app is installed on the device, this works fine. However, if both apps are installed on the device, they both use the service from whichever package was least recently installed -- i.e., the oldest one. This is problematic for two reasons:
1) the old service doesn't handle the new calls (obviously)
2) when using the service bundled with the other app, the service ends up using that other app's saved data instead of its own.
Short-term, I really just want each app to talk to its own version of the service. Is it possible to do this without making each bundled service respond to a unique intent, and having each app use that unique intent?
Long-term, it would be nice to optionally have both apps talk to the newest version of the service, and have them share data, and play nice with either app getting uninstalled. What's the right way to do that? The data is primarily in an SQLite database, which gets stored where Context.getDatabasePath() specifies, which is of course application-specific and wiped if that application is uninstalled.
Here's what I've tried so far:
I can use PackageManager.queryIntentServices() to get a list of ResolveInfo with both versions of the service. By looking at ResolveInfo.serviceInfo.applicationInfo.packageName, I can tell which is which. However, there doesn't seem to be any way to use that ResolveInfo to specify which of the two services I want to handle the intent.
This seems like it should do the right thing:
serviceIntent.setPackage(context.getApplicationContext().getApplicationInfo().packageName);
as that packageName is the same as the one I want to match. Indeed, when I call queryIntentServices with that modified intent I get a list back containing only the service within my package. However, when I try to bindService with that intent, it throws a security exception.
java.lang.RuntimeException: Unable to bind to service my.service.package.name.MyServiceClass#40531558 with Intent { act=my.intent.string pkg=my.app.package.name }: java.lang.SecurityException: Not allowed to start service Intent { act=RuntimeException: Unable to bind to service my.service.package.name.MyServiceClass } without permission private to package
Based on some googling, I have also tried creating the intent by doing:
Intent serviceIntent = new Intent().setClassName(
"my.service.package.name",
"my.service.package.name.ServiceClassName");
That doesn't resolve to any services at all.
I have also tried changing android:exported= in the service entry in the AndroidManifest.xml to true, false, or not specified, in various combinations in both apps, to no avail. (I was hoping that if I set them both to false, then each app would only be able to see its own copy of the service.)
Here is a solution that works:
In the manifest add a meta-data for the Service. The name should be something like "service launcher" and the value must be identical to the name of the action in the intent filter.
<meta-data android:name="Service Launcher"
android:value="your.service.action.string.here" />
<intent-filter>
<action android:name="your.service.action.string.here" />
</intent-filter>
Then in your code add an access method to get the meta-data and use that to build the Intent.
public static String getServiceIntentAction(Context context) {
String action = "";
try {
PackageManager pm = context.getPackageManager();
android.content.ComponentName cn = new android.content.ComponentName(context.getPackageName(), "com.your.ServiceClass");
android.content.pm.ServiceInfo si = pm.getServiceInfo(cn, PackageManager.GET_META_DATA);
action = si.metaData.getString("Service Launcher");
} catch(android.content.pm.PackageManager.NameNotFoundException nnfe) {}
return action;
}
Then anything that is building an Intent to launch the service calls this method. Every user of the service in their app can have a different action to only communicate to their app with just one version of the library and only a change to the manifest.
To have both apps use the same service the service should probably be install as its own entity.
Have you tried using categories? For every category in an intent, the potentially receiving component has to match each category to be considered a receiver.
Service A has category "1" and intents to category "1" go there.
Service B has category "1" and category "2" (assuming it still supports everything in version "1". With this an older app could talk to the newer service but a newer app could not communicate with an older service.
I have a free and a premium version of the same app (almost same code, same classes with "if"s here and there, different packages in the manifest, same process name in the manifest). The main activity calls a service to execute some stuff, using an IMPLICIT intent.
When I install both apps on the phone, it turns out that the premium activity actually starts a "free" service sometimes and a "premium" service another.
I have been playing around with categories and the packagemanager but it seems too complicated.
Questions :
how does Android handle multiple components responding to a same Intent?
how would you do what I am trying to do: I have the same service in multiple apps and I only want one instance being called from all the apps?
I guess you could add an extra boolean isPremium to the intent. Naturally you will need some more of those "if's".
Activity:
//send broadcast
Intent serviceStarted = new Intent(Actions.ACTION_START_SERVICE);
serviceStarted.putExtra(Extras.EXTRA_PREMIUM_VERSION, PREMIUM_VERSION);
sendBroadcast(serviceStarted);
Receiver:
if (!intent.getExtras().getBoolean(Extras.EXTRA_PREMIUM_VERSION)) {
Log.v(TAG, " - ignoring wrong version");
return;
}