Skip to content

ActivityAttribute.MainActivity should set exported=true #6196

Description

@jonpryor

Context: https://aster.cloud/2021/02/23/lets-be-explicit-about-our-intent-filters/

An important change is coming to Android 12 that improves both app and platform security. This change affects all apps that target Android 12.

Activities, services, and broadcast receivers with declared intent-filters now must explicitly declare whether they should be exported or not.

The short version is that many apps may need to update their [Activity] attribute usage to explicitly set ActivityAttribute.Exported.

However, we added ActivityAttribute.MainLauncher, which is also used in our templates:

https://github.com/xamarin/xamarin-android/blob/f7d32102cc3ada67aa12cc0beed2cf97226f41b3/src/Microsoft.Android.Templates/android/MainActivity.cs#L8

We should update our AndroidManifest.xml generation & related code so that when MainLauncher=true, we also automatically set exported=true.

Activity

  1. added
    Area: App+Library BuildIssues when building Library projects or Application projects.
    needs-triageIssues that need to be assigned.
    on Aug 18, 2021
  2. marcelojaf commented on Jan 31, 2022

    @marcelojaf

    Hi, I have a legacy app that is throwing this exception:
    ADB0010: Mono.AndroidTools.InstallFailedException: Unexpected install output: Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl1622133712.tmp/base.apk (at Binary XML file line #37): crc64e33e66e9a625e726.PNFirebaseIIDService: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present]

    Any tips on how to fix this? I can see the MainLauncher = true on my SplashActivity.cs file. I added Exported = true but wasn't enough.
    My Xamarin.Forms version is 5.0.0.2291

  3. jonathanpeppers commented on Jan 31, 2022

    @jonathanpeppers
    Member

    The PNFirebaseIIDService probably needs Exported=true.

    @marcelojaf if that doesn't fix it, can you file a new issue? Include the contents of obj/Debug/android/AndroidManifest.xml.

  4. marcelojaf commented on Jan 31, 2022

    @marcelojaf

    Thank you very much for the answer.
    I could not find anything in the Xamarin.Forms app searching for PNFirebaseIIDService, but I could find searching for Firebase and I see it on

    MainActivity.cs:

    [Activity(Label = "JJJJJJJ ", Icon = "@drawable/icon", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
        public class MainActivity : FormsApplicationActivity
        {
            internal static MainActivity Instance { get; private set; }
    
            protected override void OnCreate(Bundle bundle)
            {
                //RequestedOrientation = ScreenOrientation.Portrait;
    
                if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
                {
                    // Kill status bar underlay added by FormsAppCompatActivity
                    // Must be done before calling FormsAppCompatActivity.OnCreate()
                    var statusBarHeightInfo =
                        typeof(FormsAppCompatActivity).GetField("statusBarHeight", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                    if (statusBarHeightInfo == null)
                    {
                        statusBarHeightInfo =
                            typeof(FormsAppCompatActivity).GetField("_statusBarHeight", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                    }
    
                    statusBarHeightInfo?.SetValue(this, 0);
                }
    
                base.OnCreate(bundle);
    
                Xamarin.Forms.Forms.Init(this, bundle);
                Instance = this;
                ZXing.Net.Mobile.Forms.Android.Platform.Init();
                UserDialogs.Init(this);
                Plugin.CurrentActivity.CrossCurrentActivity.Current.Init(this, bundle);
                Xamarin.Essentials.Platform.Init(this, bundle);
                Android.Glide.Forms.Init();
    
                LoadApplication(new App(false));
    
                // PUSH
                FirebaseCloudMessagingManager.ProcessIntent(this, Intent);
    
                if (App.Settings.LockCurrentScreenOrientation)
                {
                    RequestedOrientation = ScreenOrientation.Locked;
                }
                else
                {
                    RequestedOrientation = ScreenOrientation.Unspecified;
                }
    
                // Reset soft input mode to adjust with keyboard
                // Must be done after calling FormsAppCompatActivity.OnCreate()
                Window.SetSoftInputMode(SoftInput.AdjustResize);
                Window.DecorView.SystemUiVisibility = StatusBarVisibility.Visible;
    
    
            }
            protected override void OnNewIntent(Android.Content.Intent intent)
            {
                base.OnNewIntent(intent);
                FirebaseCloudMessagingManager.ProcessIntent(this, Intent);
            }
    
            public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
            {
                Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
                ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    

    and

    MainApplication.cs:

    //You can specify additional application information in this attribute
    [Application]
    public class MainApplication : Application
    {
    	public MainApplication(IntPtr handle, JniHandleOwnership transer)
    	  : base(handle, transer)
    	{
    	}
    
    	public override void OnCreate()
    	{
    		base.OnCreate();
    		CrossCurrentActivity.Current.Init(this);
    
    		//Set the default notification channel for your app when running Android Oreo
    		if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
    		{
    			//Change for your default notification channel id here
    			FirebaseCloudMessagingManager.DefaultNotificationChannelId = "DefaultChannel";
    
    			//Change for your default notification channel name here
    			FirebaseCloudMessagingManager.DefaultNotificationChannelName = "General";
    		}
    
    		// for testing reset the token on each cycle
    		// FirebaseCloudMessagingManager.Initialize(this, true);
    		FirebaseCloudMessagingManager.Initialize(this, false);
    
    		CrossFirebaseCloudMessaging.Current.OnNotificationReceived += (s, p) =>
    		{
    			System.Diagnostics.Debug.WriteLine("NOTIFICATION RECEIVED", p.Data);
    		};
    
    	}
    }
    

    Here's the obj/Debug/android/AndroidManifest.xml content.

    <?xml version="1.0" encoding="utf-8"?>
    <!--
        This code was generated by a tool.
        It was generated from C:\dev\JLOFT.xPlatform\JLOFT.Droid\Properties\AndroidManifest.xml
        Changes to this file may cause incorrect behavior and will be lost if
        the contents are regenerated.
    -->
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jinny.jloft.android" android:installLocation="auto" android:versionCode="20220121" android:versionName="0.9.0.38">
      <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.CAMERA" />
      <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      <permission android:name="com.jinny.jloft.android.permission.C2D_MESSAGE" android:protectionLevel="signature" />
      <!-- used by connectivity plugin to get speeds -->
      <queries>
        <intent>
          <action android:name="android.media.browse.MediaBrowserService" />
        </intent>
      </queries>
      <uses-permission android:name="android.permission.WAKE_LOCK" />
      <!-- Required by older versions of Google Play services to create IID tokens -->
      <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
      <application android:name="crc646a42351b9329c90d.MainApplication" android:allowBackup="true" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:debuggable="true" android:extractNativeLibs="true" android:icon="@drawable/icon" android:label="JLOFT Mobile" android:theme="@style/MyTheme">
        <activity android:name="crc646a42351b9329c90d.MainActivity" android:configChanges="orientation|screenSize" android:exported="true" android:icon="@drawable/icon" android:label="JLOFT Mobile" />
        <activity android:name="crc646a42351b9329c90d.SplashActivity" android:configChanges="orientation|screenSize" android:exported="true" android:icon="@drawable/icon" android:label="JLOFT Mobile" android:theme="@style/SplashTheme">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
        <receiver android:name="crc64cea48322b3427ae9.ConnectivityChangeBroadcastReceiver" android:enabled="true" android:exported="false" android:label="Connectivity Plugin Broadcast Receiver" />
        <service android:name="crc64e33e66e9a625e726.PNFirebaseIIDService">
          <intent-filter>
            <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
          </intent-filter>
        </service>
        <service android:name="crc64e33e66e9a625e726.PNFirebaseMessagingService">
          <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
          </intent-filter>
        </service>
        <receiver android:name="crc64e33e66e9a625e726.CloudMessagingActionReceiver" />
        <receiver android:name="crc64e33e66e9a625e726.CloudMessagingDeletedReceiver" />
        <service android:name="crc64396a3fe5f8138e3f.KeepAliveService" />
        <receiver android:name="crc64a0e0a82d0db9a07d.BatteryBroadcastReceiver" android:enabled="true" android:exported="false" android:label="Essentials Battery Broadcast Receiver" />
        <receiver android:name="crc64a0e0a82d0db9a07d.EnergySaverBroadcastReceiver" android:enabled="true" android:exported="false" android:label="Essentials Energy Saver Broadcast Receiver" />
        <receiver android:name="crc64a0e0a82d0db9a07d.ConnectivityBroadcastReceiver" android:enabled="true" android:exported="false" android:label="Essentials Connectivity Broadcast Receiver" />
        <provider android:name="xamarin.essentials.fileProvider" android:authorities="com.jinny.jloft.android.fileProvider" android:exported="false" android:grantUriPermissions="true">
          <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/xamarin_essentials_fileprovider_file_paths" />
        </provider>
        <receiver android:name="crc643f46942d9dd1fff9.PowerSaveModeBroadcastReceiver" android:enabled="true" android:exported="false" />
        <activity android:name="crc6480997b3ef81bf9b2.ZxingActivity" android:configChanges="keyboardHidden|orientation|screenLayout" android:label="Scanner" />
        <provider android:name="mono.MonoRuntimeProvider" android:authorities="com.jinny.jloft.android.mono.MonoRuntimeProvider.__mono_init__" android:exported="false" android:initOrder="1999999999" />
        <provider android:name="com.google.firebase.provider.FirebaseInitProvider" android:authorities="com.jinny.jloft.android.firebaseinitprovider" android:exported="false" android:initOrder="100" />
        <service android:name="com.google.firebase.components.ComponentDiscoveryService" android:exported="false">
          <meta-data android:name="com.google.firebase.components:com.google.firebase.iid.Registrar" android:value="com.google.firebase.components.ComponentRegistrar" />
        </service>
        <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
          <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
          </intent-filter>
        </receiver>
        <!--
                 FirebaseInstanceIdService performs security checks at runtime,
                 no need for explicit permissions despite exported="true"
            -->
        <service android:name="com.google.firebase.iid.FirebaseInstanceIdService" android:exported="true">
          <intent-filter android:priority="-500">
            <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
          </intent-filter>
        </service>
        <!--
                 FirebaseMessagingService performs security checks at runtime,
                 no need for explicit permissions despite exported="true"
            -->
        <service android:name="com.google.firebase.messaging.FirebaseMessagingService" android:exported="true">
          <intent-filter android:priority="-500">
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
          </intent-filter>
        </service>
        <activity android:name="com.google.android.gms.common.api.GoogleApiActivity" android:exported="false" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
      </application>
    </manifest>
    

    If I manually include the Exported=true it will work or this is an auto generated file? If not, can you see a solution with the info I posted?

    Thank you again

  5. marcelojaf commented on Jan 31, 2022

    @marcelojaf

    So, I saw the Microsoft docs for Firebase Cloud Messaging and see that the client is using a NuGet Packkage Plugin.FirebaseCloudMessaging which is not updated since March 2020 and is not on the Microsoft docs. I will fork and try to remove this plugin and use Xamarin.Firebase.Messaging as described on the documentation. I will post the result here in case other fellow developer face the same issue.

  6. nnovalbos commented on Mar 2, 2022

    @nnovalbos

    Hi @marcelojaf ,
    I have the same problem. Were you able to solve it?

    'Exported = true' didn't solve my problem

    Thanks

  7. marcelojaf commented on Mar 2, 2022

    @marcelojaf

    I followed this guy tip

  8. nnovalbos commented on Mar 3, 2022

    @nnovalbos

    Thanks a lot! it works now

  9. ghost locked as resolved and limited conversation to collaborators on Jun 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Area: App+Library BuildIssues when building Library projects or Application projects.needs-triageIssues that need to be assigned.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions