dotnet/android

Android Service does not start if process name is specified in Service attribute

jfichtner opened this issue · 12 comments

Description

In a .NET 8 MAUI app, when I attempt to create an Android service that starts in a private process using the Service attribute, and specifying a process name with a colong as the first character as such:
[Service(Process = ":test")] public class TestService : Service

The service simply does not start when attempting to start by calling:
context.StartService(new Intent(context, typeof(TestService)));

In addition, if I remove the colon from the process string I get the following error when attempting to deploy the project:
ADB0010: Mono.AndroidTools.InstallFailedException: Unexpected install output: Failure [INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: Failed parse during installPackageLI: Failed to read manifest from /data/app/vmdl699026415.tmp/base.apk: com.android.server.pm.pkg.component.ParsedServiceImpl cannot be cast to java.lang.String]

Steps to Reproduce

Create a new .NET MAUI application from the template in VS2022 and add the following class to the MauiApplication file:

    [Service(Process = ":test")]
    public class TestService : Service
    {
        public override IBinder? OnBind(Intent? intent)
        {
            return null;
        }


        public static void start(Context context)
        {
            context.StartService(new Intent(context, typeof(TestService)));
        }

        public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags startCommandFlags, int startId)
        {
            return StartCommandResult.Sticky;
        }
    }

Then in the MainApplication constructor add:

TestService.start(this);

I deployed this to a Pixel 5 emulator for my testing.

When attempting to deploy and run this application, note the following:

  • If a breakpoint is placed in the OnStartCommand, it will never be hit.
  • If the colon is removed from the string in the Process parameter of the Service attribute, then attempting to deploy will result in the deployment error mentioned above.
  • Removing the Process parameter altogether and rerunning results in the breakpoint being hit as expected.

Link to public reproduction project repository

No response

Version with bug

8.0.3 GA

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

No response

I assume the version specified above is close enough, but here is the exact output from the dotnet workload list command:

Welcome to .NET 8.0!

SDK Version: 8.0.303

This issue has been verified using Visual Studio 17.11.0 Preview 3 (8.0.70 & 8.0.3). Can repro it.
MauiApp24.zip

Any chance for a workaround here? If not I'll have to split my service out to a separate, native Java/Kotlin apk, which I'd really rather not do.

I'm not sure we can support this. Running a service in a seperate process would require the runtime to be initialised I think, I'm not sure we have the hooks in place for that? @jonpryor am I mis remembering this? I seem to remember seperate processes for services was a no go at this time?

@dellis1972, @jonpryor thank you for looking into this. Would you have any advice on how to move forward? Is my only option to create and install a separate apk for the service to run in? Thanks...

Sorry to be a nuisance, @dellis1972 and @jonpryor, but getting an answer on this would help me tremendously in moving forward with my project. Thanks...

@dellis1972: you're thinking of isolated processes, which we don't support:

"Normal" android:process should work, or at least used to work:

@jfichtner: it works for me?

Start with MauiApp24.zip, and apply this patch:

diff --git a/Platforms/Android/MainApplication.cs b/Platforms/Android/MainApplication.cs
index 0fee904..de5faf1 100644
--- a/Platforms/Android/MainApplication.cs
+++ b/Platforms/Android/MainApplication.cs
@@ -21,8 +21,14 @@ namespace MauiApp24
     [Service(Process = ":test")]
     public class TestService : Service
     {
+        public TestService ()
+        {
+            Console.WriteLine ("# jonp: TestService is created!");
+        }
+
         public override IBinder? OnBind(Intent? intent)
         {
+            Console.WriteLine ($"# jonp: TestService.OnBind! intent={intent}");
             return null;
         }
 
@@ -33,6 +39,7 @@ namespace MauiApp24
 
         public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags startCommandFlags, int startId)
         {
+            Console.WriteLine ($"# jonp: TestService.OnStartCommand! intent={intent} startCommandFlags={startCommandFlags} startId={startId}");
             return StartCommandResult.Sticky;
         }
     }

Start capturing adb logcat:

adb logcat > log.txt &

Build, install, run:

dotnet build -f net8.0-android
dotnet build -f net8.0-android -t:Install
dotnet build -f net8.0-android -t:StartAndroidActivity

log.txt contains:

 1710  1823 I ActivityManager: Start proc 25425:com.companyname.mauiapp24/u0a416 for next-top-activity {com.companyname.mauiapp24/crc64df52bd7414134cd4.MainActivity}
25448 25448 …
 1710  1823 I ActivityManager: Start proc 25448:com.companyname.mauiapp24:test/u0a416 for service {com.companyname.mauiapp24/crc64df52bd7414134cd4.TestService}
25448 25448 …
25448 25448 I .mauiapp24:test: Late-enabling -Xcheck:jni
25448 25448 …
25448 25448 W debug-app-helper: Using runtime path: /data/app/~~iIy8UMzfnGMql_W42HYxzw==/com.companyname.mauiapp24-ZoXFZutkzgyakcaj6FUy8g==/lib/arm64
25448 25448 …
25448 25448 I DOTNET  : JNI_OnLoad: JNI_OnLoad in pal_jni.c
25448 25448 D DOTNET  : GetOptionalClassGRef: optional class com/android/org/conscrypt/OpenSSLEngineImpl was not found
25448 25448 …
25448 25448 I DOTNET  : # jonp: TestService is created!
25448 25448 I DOTNET  : # jonp: TestService.OnStartCommand! intent=Intent { cmp=com.companyname.mauiapp24/crc64df52bd7414134cd4.TestService } startCommandFlags=0 startId=1
25448 25448 I DOTNET  : # jonp: TestService.OnStartCommand! intent=Intent { cmp=com.companyname.mauiapp24/crc64df52bd7414134cd4.TestService } startCommandFlags=0 startId=2

The first line is the initial launch of MainActivity. Note that it's created on PID 25425.

Later, we see Start proc 25448; this is creating the :test process, and please note that 25448 is a different PID from 25425.

Finally, we see the added Console.WriteLine() messages from the Service, showing that TestService is indeed created, and that TestService.OnStartCommand() is invoked. Also note the PID that these messages are coming from: 25448, the PID of the :test service.

Perhaps it's debugging this app which doesn't work as expected? (I have not tried using the debugger.) It certainly appears to work as expected for me

@jfichtner: I would not expect the debugger to work in this scenario, as if I recall correctly, the debugger only wants to deal with one process, and this setup involves two: the process with the Activity, and the process with the Service. Debugging multi-process scenarios is beyond my knowledge.

@jonpryor Ah, I see! So, my test was flawed in that I was expecting the debugger to hit the breakpoint, but since the service is running in a different process the breakpoint won't be hit. I didn't even consider that, so thank you for taking the time!

This does address my issue, because I only need to run in a separate process. However, there still seems to be an issue with specifying a process that does not start with a colon, as this results in a deployment error.

@jfichtner I have created a service for the maui community toolkit. I use it in MediaElement. Here is an example scenario:

[Service(Exported = false, Enabled = true, Name = "communityToolkit.maui.media.services", ForegroundServiceType = ForegroundService.TypeMediaPlayback)]
class MediaControlsService : Service
{
 // Rest of code
}

Here is link to repo with service: https://github.com/ne0rrmatrix/MauiOld/blob/Media3/src/CommunityToolkit.Maui.MediaElement/Services/MediaControlsService.android.cs

I tested with

[Service(Process = ":test", Exported = false, Enabled = true, Name = "communityToolkit.maui.media.services", ForegroundServiceType = ForegroundService.TypeMediaPlayback)]
class MediaControlsService : Service
{
// Service stuff here
}

and my service worked fine. Maybe it is because I am using a foreground service?

@ne0rrmatrix as I mentioned above, I realized that the service does work, it just doesn't attach the debugger. It still does NOT work if the process name doesn't start with a colon.