I'm building a call screener app for Android using the CallScreeningService
API. When I run the app, it registers the service OK but when I call the emulator, I get the following exception:
Java.Lang.RuntimeException: 'Unable to create service com.katoapps.callblocker.CallBlockerService: java.lang.ClassNotFoundException: Didn't find class "com.katoapps.callblocker.CallBlockerService" on path: DexPathList[[zip file "/data/app/~~OvboC6Rbk2k7eFeBnJFlKg==/com.katoapps.callblocker-aJoY2QavLWrfwVyoFIXSMA==/base.apk"],nativeLibraryDirectories=[/data/app/~~OvboC6Rbk2k7eFeBnJFlKg==/com.katoapps.callblocker-aJoY2QavLWrfwVyoFIXSMA==/lib/x86_64, /data/app/~~OvboC6Rbk2k7eFeBnJFlKg==/com.katoapps.callblocker-aJoY2QavLWrfwVyoFIXSMA==/base.apk!/lib/x86_64, /system/lib64, /system_ext/lib64]]'
Here's CallBlockerService
(located in /Platforms/Android/CallBlockerService.cs)
namespace CallBlocker;
using Android.App;
using Android.Telecom;
[Service(Exported = true, Permission = "android.permission.BIND_SCREENING_SERVICE")]
[IntentFilter(new string[] { "android.telecom.CallScreeningService" })]
public class CallBlockerService : CallScreeningService
{
public override void OnScreenCall(Call.Details callDetails)
{
}
}
The main activity that registers the service:
using Android.App;
using Android.App.Roles;
using Android.Content;
using Android.Content.PM;
using Android.OS;
namespace CallBlocker;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
private static int REQUEST_ID = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
RoleManager roleManager = (RoleManager)GetSystemService(RoleService);
Intent intent = roleManager.CreateRequestRoleIntent(RoleManager.RoleCallScreening);
StartActivityForResult(intent, REQUEST_ID);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == REQUEST_ID)
{
if (resultCode == Result.Ok)
{
// Your app is now the call screening app
}
else
{
// Your app is not the call screening app
}
}
}
}
The android manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.katoapps.callblocker">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:label="Call Blocker">
<service android:name=".CallBlockerService"
android:exported="true"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
</service>
</application>
<uses-sdk android:minSdkVersion="29" />
</manifest>
And the project info (not sure if it's relevant)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0-android;</TargetFrameworks>
<OutputType>Exe</OutputType>
<RootNamespace>CallBlocker</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Display name -->
<ApplicationTitle>CallBlocker</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.katoapps.callblocker</ApplicationId>
<ApplicationIdGuid>025a85bb-9e56-4624-a3cf-3797a5a01bd2</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">29.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-android|AnyCPU'">
<ApplicationId>com.katoapps.callblocker</ApplicationId>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-android|AnyCPU'">
<ApplicationId>com.katoapps.callblocker</ApplicationId>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
</ItemGroup>
</Project>
Why is the app not finding my CallBlockerService
?
Decompiling the APK revealed this Android manifest:
The compiler modifies the Android manifest and adds the service automatically under a GUID name, so the fix was to simply remove my service from the manifest.