I'm not an android dev. I used capacitor to convert a web app into an android app. The new sdk 31 changes have required me to add android:exported=true onto the mainActivity tag in androidManifest.xml file since it has intent filters. Basically, in order for my app to be approved by google console I need to add this one property.
ELI5 - is this safe, what does this do ?
<activity
android:name="com.test.MainActivity"
android:exported="true" <---------------------------- this
android:launchMode="singleTask"
>
Yes, it's safe to use exported true is used correctly
The exported attribute is used to define if an activity, service, or receiver in your app is accessible and can be launched from an external application.
As a practical example, if you try to share a file you’ll see a set of applications available. Clicking on one of them will open the activity responsible to handle that file, which means that it’s declared on the Manifest with exported:true.
For more details https://cafonsomota.medium.com/android-12-dont-forget-to-set-android-exported-on-your-activities-services-and-receivers-3bee33f37beb
Related
I'm experimenting with Dynamic Features and Instant Apps.
To navigate between various features, I use Deep Links.
Each time I navigate to another Activity, I see the disambiguation dialog for less than 1 second, with 1 app listed. Notice how the options for "Once" and "Always" (in dutch) are greyed out.
Sample Github Project
I created a minimalistic sample, that matches my current structure on Github. Requires Android Studio 3.5 - RC2
Some Context:
I'm quite confident, the deeplinks are configured correct. But since you guys want to check that anyway, here's the configuration:
1 - Manifest:
<activity
android:name=".ProfileActivity">
<intent-filter
android:autoVerify="true"
android:priority="100">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="giddy.entreco.nl"
android:pathPrefix="/profile"
android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity>
2 - Assetlinks
My domain contains a publicly accessible assetlinks.json
3 - Sha's are correct
The sha's I use are correct
Executing tasks: [signingReport] in project
SHA1: 3A:52:19:77:C1:AD:18:F4:98:21:77:74:37:DC:9B:89:02:64:6E:C6
SHA-256: 25:DD:C3:7B:8E:35:D3:39:D5:D4:6C:B5:EA:7D:14:AF:82:EC:9C:56:A6:F5:76:A3:E1:D7:69:B3:EC:58:72:E8
Valid until: Saturday, March 21, 2048
4 - Confirmed digital asset link file
All checks pass
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://giddy.entreco.nl&relation=delegate_permission/common.handle_all_urls
5 - Testing a URL intent
Also works! The only problem is I see the disambiguation dialog for a brief period.
Additional info
I use apply plugin: 'com.android.dynamic-feature' in all my modules (except app off course)
Android Studio: 3.5 RC2; Android-gradle-plugin: 3.5.0-rc02
My Device is a OnePlus6 - with Oxygen 9.0.7 & Android 9
The google official sample also shows this behaviour on my device
Some Samsung devices, behave different. Instead of showing the Disambiguation with 1 option, it lists my app twice, and will keep waiting until you either select Once or 'Always'. (Note, I got this from the pre-launch reports in the play store)
I see this behaviour, no matter if I build an APK, an App Bundle or download through Google Play. It's always the same.
Any suggestions to get that annoying dialog out of the way?
When I analyse the apk/bundle, I do see two entries for the specific Activity. Once in the base module's manifest, but also in the profile module's manifest. I have little understanding of how Android/PlayStore merges those manifests while installing modules, but I guess it could make sense to see the dialog in this case.
So yes... I believe I have seen this before, it is some odd behavior when navigating from one dynamic-feature (instant) to another (non-instant) via a URL intent.
Until this gets addressed, I don't recommend using a URL intent to navigate between modules, instead, use reflection to directly get to the other module's activity, example:
if (doesModuleExist()) {
val intent = Intent()
.setClassName(getPackageName(), "com.sample.ProfileActivity")
.addCategory(Intent.CATEGORY_DEFAULT)
.addCategory(Intent.CATEGORY_BROWSABLE)
startActivity(intent)
}
Where doesModuleExist() checks if either:
examining your sample manifest, it looks like your profile module is not part of your instant app dist:instant="false", so you would never have access to it, therefore you could simply do a isNotInstantApp() check instead and never attempt the launch while as instant app.
once in your installed app, then you technically don't need to check, as it is always include="true"
but if your profile module is an onDemand module, or just for safety precautions, you should utilize splitInstallManager.getInstalledModules(), see /app-bundle/playcore#manage_installed_modules (note, you can also use this API in your instant app)
And since it looks like this odd behavior varies between different devices, that might mean they've implemented subtle differences in intercepting and handling that URL intent, and/or it's just a different Android version (pre-O vs O+).
Also yes, associating multiple package names to a single website domain for common.handle_all_urls might cause some additional misbehavior when the system tries to verify the association when your app goes live.
There is a possible solution for using URI intents with dynamic features instead of switching to reflection. You can use a trick where you can get the needed class name during run time with packageManager.queryIntentActivities(), so you don't have to use the hardcoded Activity name.
The following extension function is an example of how you could convert your Intent using the URI or deep link into one that will not show the chooser dialog:
fun Intent.convertToSafeDynamicFeatureModuleIntent(context: Context) {
//Get list of all intent handlers for this Intent. This should only be the actual activity we are looking for
val options = context.packageManager.queryIntentActivities(this, PackageManager.MATCH_DEFAULT_ONLY)
//Set the activity that supported the given intent
setClassName(packageName, options[0].activityInfo.name)
}
Then you can simply do:
intent.convertToSafeDynamicFeatureModuleIntent(context)
startActivity(intent)
A longer explanation can be found here
I have uploaded my first application to google playstore
as you see in this image in manifest in package it says auc.visorimagenes.ssssss
in the left part you could see my structure code where it is as
auc.visorimagenes.
in playstore
you see it:
but What it says auc.visorimagenes
if in my manifest I had set auc.visorimagenes.sssss
I want to up a new app with same code (only changing some resources) this is would be a new app not an update.
then I must to change auc.visorimagenes.sssss ???
Contains information about a Java package. This includes implementation and specification versions. Typically this information is retrieved from the manifest.
Packages are managed by class loaders. All classes loaded by the same loader from the same package share a Package instance.
Hope this helps!
Could you try and change the android:name attribute of your application entry in the manifest as follows and see if it helps?
<application
....
android:name=".App" (or android:name=".Application.App")
/>
This will ensure that the app uses the same package name as the one declared in the manifest.
The package in android is the name of the application that makes it unique from any other android apps, therefore allow the production of multiple apps with the same product name.
In Android documentation on permission-tree, I cannot find any use scenario showing permission-tree is useful.
Now there are several questions in my mind:
Why do we need permission-tree?
Is there any real scenario to illustrate permission-tree is necessary?
Is there any example to demonstrate how the client App requests the permission-tree?
Why do we need permission-tree?
When you use permission-tree, you don't want other apps to use any permission with the same base name as you declared from permission-tree.
For example, you use
<permission-tree
android:name="com.example.project.taxes"
android:label="" />
Which means you don't want other apps to use any permission prefix with "com.example.project.taxes".
If there is any app with the same base name installed before your app, both apps' permissions are valid.
If your app installed first, and another app using a permission prefix with your base name, another app's protection level will automatically change to "signature", even it declares as "normal" in the AndroidManifest.xml.
This can be checked when you pull system packages file from devices.
adb pull /data/system/packages.xml
Normal permission is like this,
<item name="com.google.android.gms.permission.TRANSFER_WIFI_CREDENTIAL" package="com.google.android.gms" />
If there is a conflict, it will become like this.
<item name="com.google.android.gms.permission.TRANSFER_WIFI_CREDENTIAL" package="com.google.android.gms" protection="2" />
That means you will block all the future installed app to gain the normal permission with your base name.
When some app trying to use it, Logcat will log some message like this when the app is installing,
PackageManager: Un-granting permission com.example.project.taxes.deductions.MAKE_SOME_UP from package com.others.app
So be careful to choose your permission-tree name.
Is there any real scenario to illustrate permission-tree is necessary?
From the /data/system/packages.xml from a new device, I can see only a google app is using permission tree.
<permission-trees>
<item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
</permission-trees>
And this app really uses a lot of customised permission, that's why it needs to declared the permission tree and not allow others to conflict with them.
Is there any example to demonstrate how the client App requests the permission-tree?
This is example how client app request the permission tree.
<permission-tree
android:name="com.example.project.taxes"
android:label="" />
But I don't think a client app is necessary to use permission tree, it's more meaningful for a system app. Otherwise, use a long name for permission tree, make sure no one else has conflict with this name.
I am inheriting a code base that needs to be kept fairly in sync with the previous version to make updating to new versions as painless as possible when they push the changes via git. The changes I am making are related to re-styling and adding new features.
In the latest release, I was trying to install both the original version of the app alongside this custom version and got this error:
Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]
I looked through my source control and found that in previous versions I was using the same authorities and name as the parent app, but I don't recall having this problem, but maybe I just never tested the case where both apps were installed at the same time?
In the original version of the manifest, this is the provider element:
<provider
android:name="com.foo.mobile.android.provider.Provider"
android:authorities="com.foo.android.mobile.contentprovider"
android:exported="false" />
I tried changing the authority to this:
<provider
android:name="com.foo.mobile.android.provider.Provider"
android:authorities="com.bar.android.mobile.contentprovider"
android:exported="false" />
But now the app crashes shortly after launch with SecurityException:
java.lang.SecurityException: Permission Denial: opening provider com.foo.mobile.android.provider.Provider from ProcessRecord{42cbc998 2462:com.bar/u0a191} (pid=2462, uid=10191) that is not exported from uid 10189
I've looked on SO and saw a couple of questions regarding this topic and also looked at the documentation and it all says I need this authorities to be different, but how can I keep this different while keeping synergy with the base code?
maybe I just never tested the case where both apps were installed at the same time?
I would presume this is the case. You cannot have two apps with providers supporting the same authority installed at the same time.
But now the app crashes shortly after launch with SecurityException:
My guess is that you changed your manifest, but you did not change the Uri that you are using to access the provider. Hence, your com.bar app is still trying to talk to the com.foo provider, and that provider is not exported.
how can I keep this different while keeping synergy with the base code?
Either this is the same app, or it is not.
If it is the same app, your first step is to switch back to the original package name. The only way you could have gotten Failure [INSTALL_FAILED_CONFLICTING_PROVIDER] is because you changed the package name and did not change the provider's authority. Changing the package name means that, from the standpoint of everyone outside of you and your team, it is a completely different app. Once you have switched back to the original package name (and rolled back to the original provider authority), everything should be fine, except that you won't be able to have the release version and the development version on the same device at the same time.
If you do indeed plan to have this app have a separate package name (so existing users of the existing app cannot upgrade to this new app), you will need to change the authority string in all relevant places. I presume that you can do this using a string resource, where you have different versions of the string resource. Or, if this is a free-vs.-paid app scenario, move to doing your builds using Gradle for Android, and set up separate free and paid product flavors, which can patch up your package name and authority data as part of the builds, without having to tweak the source code.
I am having two android apps signed with same certificate. Also i am using the activity of one app in other android app by specifying a name in "intent-filter" tag. But because of "intent-filter" tag any third party app can call my activity.
Since both the apps are having the same certificates signed, can i restrict other apps calling my activity by providing some permisions? Any suggestions on this would be helpful to me.
-Ron...
First you could maybe remove the intent-filters if your activity is not meant to be used by other applications than yours ; why not use an explicit intent instead?
Thus your activity couldn't be created "by mistake" but only intentionally, ex :
Intent explicitIntent = new Intent(InvokingActivity.this, InvokedActivity.class);
startActivity(explicitIntent);
Then, to even prevent that from the outside, you could define your own custom permission and add it to your activity ; have a look at the android:protectionLevel attribute of a permission, whose value can be "signature".