Permission Denial on SEND action intent - android

This is the log, I am receving on crash in Samsung running Oero OS:
Fatal Exception: java.lang.SecurityException: Permission Denial:
starting Intent { act=android.intent.action.SEND typ=text/plain
flg=0x80001 pkg=com.google.android.apps.maps
cmp=com.google.android.apps.maps/com.google.android.apps.gmm.sharing.SendTextToClipboardActivity
clip={text/plain T:"XYZ"
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
share.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
share.putExtra(Intent.EXTRA_TEXT, shareMessage);
share.setPackage(packageName);
Please suggest.
Thanks

You have a couple of options here. You can avoid this crash by specifically filtering out the SendTextToClipboardActivity that Google Maps recently added as an intent-handler. (It looks exactly like the genuine Android "Copy to clipboard" share-target, and is most likely the result of a bug on the part of Google Maps.) Or you can filter out any ResolveInfo where the corresponding Activity is not exported; however, this could result in also filtering out share-targets within your own app that you do want to show to the user, in which case you would need to allow non-exported Activities from your own app package.
For example, suppose you have something like this to obtain a list of providers for your ACTION_SEND intent:
PackageManager packageManager = mActivity.getPackageManager();
List<ResolveInfo> providers = packageManager.queryIntentActivities(sendIntent, 0);
You could then filter out providers that are known to cause problems, and/or providers for which the Activity isn't marked as exported. For example, you could use a check such as the following to build your own list of providers to be displayed to the user:
for (ResolveInfo provider : providers) {
if ("com.google.android.apps.maps".equalsIgnoreCase(provider.activityInfo.packageName)
&& "com.google.android.apps.gmm.sharing.SendTextToClipboardActivity".equalsIgnoreCase(provider.activityInfo.name)) {
continue; // Skip specific Activity you don't want to show
}
if (!BuildConfig.APPLICATION_ID.equalsIgnoreCase(provider.activityInfo.packageName) && !provider.activityInfo.exported) {
continue; // Skip providers where the Activity is not marked with exported=true, unless they're from our own app
}
acceptableProviders.add(provider);
}
How to then go about displaying the acceptableProviders as share options is left as an exercise to the reader. The key is filtering out known bad providers.
Meanwhile, you can also contact Google to complain about the bad behavior of this new intent-handler, which is essentially masquerading as the trusted Android text/plain handler used for the "Copy to clipboard" share-target.

Related

Android: Can explicit intents work without adding QUERY_ALL_PACKAGES (or package under queries tag) to my manifest?

I have small code which makes sure a url is always opened in browser. It is working so far.
Recently, I got message from Google Play Console stating:
If your app requires the QUERY_ALL_PACKAGES permission, you need to
submit the declaration form in Play Console by July 12
I have checked and verified, we don't use QUERY_ALL_PACKAGES in my app. However, we use Explicit Intents and resolveActivity in few places. One of the code (I took it from this place) is:
override fun openInBrowser(url: String) {
val dummyIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://"))
val resolveInfo = context.packageManager.resolveActivity(dummyIntent, PackageManager.MATCH_DEFAULT_ONLY)
val browserPackageName = resolveInfo?.activityInfo?.packageName
val realIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if (browserPackageName != null && browserPackageName.isNotBlank() && browserPackageName != "android") {
realIntent.setPackage(browserPackageName)
}
startActivity(context, realIntent, null)
}
I am not sure if it falls under the category of querying a package. I have gone through this link and as per my understanding I don't need to add either package under queries tag or QUERY_ALL_PACKAGES. I just want to validate my understanding.
The limited app visibility affects the return results of methods that give information about other apps, such as queryIntentActivities(), getPackageInfo(), and getInstalledApplications(). The limited visibility also affects explicit interactions with other apps, such as starting another app's service.
Some packages are still visible automatically. Your app can always see these packages in its queries for other installed apps. To view other packages, declare your app's need for increased package visibility using the element. The use cases page provides examples for common app interaction scenarios.

Which flag for standard apps in PackageManager.getInstalledApplications(flag)?

When I open the Android main menu on my Android smartphone, I get a set of apps like Youtube, Calculator, Email clients etc. No system stuff or any libraries are visible there.
To retrieve these apps programamtically, I do:
PackageManager.getInstalledApplications(flag: Int)
where I get a list of ApplicationInfo, which also contains alot more than mentioned installed standard apps. What flag do I have to set to get only the same apps, which I see when I swipe up on my Smartphone?
val mainIntent = Intent(Intent.ACTION_MAIN, null)
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER)
val appList = context.getPackageManager().queryIntentActivities( mainIntent, 0)
You can try this to get all user apps (and the ones your launcher shows)
You might need to add additional permissions in your manifest above Android 10 though
Edit: If you want ApplicationInfo instead of ResolveInfo in your list you can retrieve it like this:
appList[your_index].activityInfo.applicationInfo

DevicePolicyManager.enableSystemApp() method does not work as expected

I'm trying to use enableSystemApp method to activate default system apps after provisioning device with the app that is set to device owner mode.
There are two methods to do this:
1) void enableSystemApp (ComponentName admin, String packageName) - in this case you need to pass package name explicitly as String. It works fine, the app gets enabled.
For example, calling this
devicePolicyManager.enableSystemApp(deviceAdminComponent, "com.google.android.gm");
enables default Gmail client, which is disabled after provisioning.
2) int enableSystemApp (ComponentName admin, Intent intent) - in this case, you need to pass an implicit intent and Android should enable all system apps that match this intent. In addition, this method returns int number of apps that match the intent. And here's the problem - I can't get this method to work, it always returns 0 and doesn't enable anything.
Here's the snippet I'm trying to use:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
int i = devicePolicyManager.enableSystemApp(deviceAdminComponent, intent);
It does not work and i == 0 in this case. What am I doing wrong?
Any help is appreciated!
Under the hood, the method that accepts an intent queries to get the list of activities that respond to that intent and then loops through the list passing in the package name string to enable the package. It's similar to doing this:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
List<ResolveInfo> infoes = getPackageManager()
.queryIntentActivities(intent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
for (ResolveInfo info in infoes) {
devicePolicyManager.enableSystemApp(deviceAdminComponent, info.activityInfo.packageName);
}
Since you are able to enable the app using the package name string, the fault most likely lies in the way the intent is being resolved - which is supported by the fact that it always returns 0.
It is counter-intuitive, but my suspicion is that the application does not resolve the ACTION_MAIN intent because the app is disabled. Have you tried a less generic intent? I would try the following
Intent i;
// #1
// This goes full circle, but I expect it should work
i = getPackageManager().getLaunchIntentForPackage("com.google.an‌​droid.gm")
// #2
i = new Intent(Intent.ACTION_SEND).setPackageName("com.google.android.gm");
// #3
// Generic, but should resolve _all_ email apps - not just the default one.
// The mailto schema filters out non-email apps
i = new Intent(Intent.ACTION_VIEW , Uri.parse("mailto:"));
Option #1 and #2 are more academic. Both require the package name at which point you may as well use the string overload of enableSystemApp. Option #3 is my best guess for something generic that might still work, but it's possible that it still won't work because the app is disabled.
Note: I find it interesting that enableSystemApp only passes the MATCH_DIRECT_BOOT_AWARE and MATCH_DIRECT_BOOT_UNAWARE flags when querying activities that can resolve the intent, because the MATCH_DISABLED_COMPONENTS and MATCH_SYSTEM_ONLY flags seem much more relevant in this situation.

ActivityNotFoundException for intent android.settings.NFC_PAYMENT_SETTINGS

I'm trying to open settings on NFC Tap & Pay page with this piece of code:
startActivity(new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS));
While testing on LG Nexus 5X with Android 7.1.2 I have received this crash:
android.content.ActivityNotFoundException:
No Activity found to handle Intent { act=android.settings.NFC_PAYMENT_SETTINGS }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1809)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
at android.app.Activity.startActivityForResult(Activity.java:4228)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(SourceFile:50)
at android.support.v4.app.FragmentActivity.startActivityForResult(SourceFile:79)
at android.app.Activity.startActivityForResult(Activity.java:4186)
at android.support.v4.app.FragmentActivity.startActivityForResult(SourceFile:859)
at android.app.Activity.startActivity(Activity.java:4525)
at android.app.Activity.startActivity(Activity.java:4493)
at ...
Well, this crash can be handled easilly with try-catch but what is wierd, when I open this NFC settings manually, code works like a charm - no crash. Why? Does anyone have an explanation for this behavior?
In documentation[1] is written this:
In some cases, a matching Activity may not exist, so ensure you
safeguard against this.
Is it possible that they meant this sentence like "you have to open settings manually, then it works fine"?
[1] https://developer.android.com/reference/android/provider/Settings.html#ACTION_NFC_PAYMENT_SETTINGS
From: https://developer.android.com/reference/android/provider/Settings.html#ACTION_NFC_PAYMENT_SETTINGS
ACTION_NFC_PAYMENT_SETTINGS
added in API level 19
String ACTION_NFC_PAYMENT_SETTINGS
Activity Action:
Show NFC Tap & Pay settings
This shows UI that allows the user to configure Tap&Pay settings.
In some cases, a matching Activity may not exist, so ensure you safeguard against this.
Input: Nothing
Output: Nothing
Constant Value: "android.settings.NFC_PAYMENT_SETTINGS"
ACTION_NFC_PAYMENT_SETTINGS is not supported by your device or can at least not be handled.
Update 1
Since your minAPILevel is 19, the action should be supported by the android RT. However, it is possible, that the link between the action and the NFC settings-menu, ALTHOUGH the menu exists, is not or can not be established.
Try to use Settings.ACTION_NFC_SETTINGS as the action and see if it starts.
If so, I'd expect an implementation issue.
To guard against the exceptions, I'd recommend using:
PackageManager packageManager = getActivity().getPackageManager();
if (intent.resolveActivity(packageManager) != null) {
startActivity(<your intent>);
} else {
Log.d(TAG, "No application available to handle requested action.");
}
See: How to check if an intent can be handled from some activity? for credit and reference.

Obtaining the android:label from other applications

i'm programming an android app, a Tasker, and i really don't find the way to obtain the label of another app. This is my point, i'm using an ListActivity where you select an installed app, then when you click it creates the Intent and all the app stuff, what i wanna do is to show to the user the android:label from the app he selected, i found in ResolverInfo an attribute called .activityInfo.labelRes, and i think is the label descriptor for the R class of the app the user selected, is there anyway to obtain the string that matches to that id???
Thanks!
D.Gómez
You can use the loadLabel(PackageManager) method of ResolveInfo to get the label of an activity. Here's a full example which finds all the launcher activities on the device and prints them to logcat:
// Get the package manager
PackageManager pm = getPackageManager();
// Create an intent that matches all launcher activities
// (and ignores non-launcher activities)
Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
// Get all activites matching the intent
List<ResolveInfo> launchers = pm.queryIntentActivities(launcherIntent, 0);
for(ResolveInfo info : launchers) {
// Get the activity label and print it
CharSequence label = info.loadLabel(pm);
Log.v("LabelTest", "App found: " + label);
}
To answer the second part of your question too, about accessing the resources of an application: They can be accessed by calling getPackageManager().getResourcesForApplication(String), which will return a Resources object that you can use, though in your case, that should not be necessary.

Categories

Resources