0

I am using Delphi 10.3.2 build 6593 to compile an app that works fine in 10.2.3 build 2631. It has it's own Service. Since the SDK API level is now 28, I must specifically code for the permissions I need, but I don't know how to request for ForeGroundService permission.

There's no correlation here with any Android Java documentation I could find on this issue. This is a specific Delphi implementation for Android.

The constant I need is absent from Androidapi.JNI.Os. As a result, the Android Pie phone reports

Foreground Service permission was not granted

I'm going to imagine that Embarcadero has some catching up to do, and this problem will be resolved in 10.3.3 when it's released. Can anyone confirm this, or provide comments or a solution. Thanks.

private
  FPermittoVibrate: Boolean;
  FVibratePermission: String;
  FPermitAccessFineLocation: Boolean;
  FAccessFineLocation: String;
  FPermitNetworkState: Boolean;
  FNetworkStatePermission: String;
  FPermitWifiState: Boolean;
  FWifiStatePermission: String;
  FPermitPhoneState: Boolean;
  FPhoneStatePermission: String;
  FPermitForeGroundService: Boolean;
  FForegroundServicePermission: String;

procedure TfrmTabbed.FormCreate(Sender: TObject);
begin
  FPermittoVibrate := False;
  FVibratePermission := JStringToString(TJManifest_permission.JavaClass.VIBRATE);
  FPermitAccessFineLocation := False;
  FAccessFineLocation := JStringToString(TJManifest_permission.JavaClass.ACCESS_FINE_LOCATION);
  FPermitNetworkState := False;
  FNetworkStatePermission := JStringToString(TJManifest_permission.JavaClass.ACCESS_NETWORK_STATE);
  FPermitWifistate := False;
  FWifiStatePermission := JStringToString(TJManifest_permission.JavaClass.ACCESS_WIFI_STATE);
  FPermitPhoneState := False;
  FPhoneStatePermission := JStringToString(TJManifest_permission.JavaClass.READ_PHONE_STATE);
  FPermitForeGroundService := False;
  FForegroundServicePermission := JStringToString(TJManifest_permission.JavaClass.??? // <====

procedure TfrmTabbed.FormShow(Sender: TObject);
begin

  PermissionsService.RequestPermissions([
      FVibratePermission
    , FAccessFineLocation
    , FNetworkStatePermission
    , FWifiStatePermission
    , FPhoneStatePermission
    , FForeGroundServicePermission
    ],
    PermissionResult, PermissionRequest
    );

procedure TfrmTabbed.PermissionResult(Sender: TObject;
  const APermissions: TArray<string>;
  const AGrantResults: TArray<TPermissionStatus>);
var
  Permission: String;
  i: Integer;
begin
  for i := 0 to High(APermissions) do
  begin
    Permission := APermissions[i];
    if Permission = FVibratePermission then
      FPermitToVibrate := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FAccessFineLocation then
      FPermitAccessFineLocation := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FNetworkStatePermission then
      FPermitNetworkState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FWifiStatepermission then
      FPermitWifiState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FPhoneStatePermission then
      FPermitPhoneState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FForeGroundServicePermission then
      FPermitForeGroundService := AGrantResults[i] = TPermissionStatus.Granted
      ;
  end;

if FPermitForeGroundService then
    // START(ing)_NOT_STICKY
    StartService; 

In case you're wondering, the other permissions I requested, are indeed granted using this code, so that's alright. It's only the ForegroundService permission that isn't granted (at runtime).

Addendum

enter image description here

enter image description here

Freddie Bell
  • 2,186
  • 24
  • 43
  • Do you have an option of including an AAR Android library? I'm thinking of creating and empty AAR just with the manifest declaring the missing `uses-permission` tag. – Eugen Pechanec Oct 19 '19 at 20:00
  • https://stackoverflow.com/questions/52382710/permission-denial-startforeground-requires-android-permission-foreground-servic – Dave Nottage Oct 19 '19 at 21:27
  • @DaveNottage I've added 2 screen-snips as proof that I had already set the necessary uses permissions in the IDE, and that this added a uses-permission to the AndroidManifest.xml. And that the targetSDKVersion is set to "28". – Freddie Bell Oct 20 '19 at 05:22
  • Are you actually calling startForeground for the service? For an example, see the TServiceModule.StartForeground method here: https://github.com/DelphiWorlds/KastriFree/blob/master/Demos/AndroidLocation/Service/LS.ServiceModule.pas – Dave Nottage Oct 20 '19 at 07:27
  • @DaveNottage Too much code "app that works fine in 10.2.3". The app was originally coded under Berlin (10.1) and subsequently upgraded to Tokyo (10.2). It's an extensive application that's been in production use for over 2 years now - and run on more than a dozen different phone models - and has over 10 000 lines of code in it. All I wanted to do here, was target the higher API. You missed the part where I said that the "The constant I need is absent from Androidapi.JNI.Os" and the // <=== in the code. I think I must sign up for the 10.3.3. Beta so I can see if EMBT have addressed this yet. – Freddie Bell Oct 20 '19 at 07:59
  • What do you mean by "too much code"? If you want it to work, you need to use code that will make it work. Also, you don't even need the constant, since you do not need to request the permission at runtime (it is normal protection level) – Dave Nottage Oct 20 '19 at 08:08
  • @DaveNottage Why is the phone telling "Foreground Service permission was not granted" despite all that I've done and that the code works under a lower API level? (BTW 10 000 lines is too much code for SO) – Freddie Bell Oct 20 '19 at 08:13
  • https://developer.android.com/reference/android/Manifest.permission#FOREGROUND_SERVICE. Protection level: *normal*. As per this answer: https://stackoverflow.com/a/52382711/3164070, you need only add it to the manifest. You might want to check that the permission in the manifest is actually ending up in AndroidManifest.xml that is deployed with your app. – Dave Nottage Oct 20 '19 at 08:17

1 Answers1

0

I tracked down the problem to my incompatible (with API 28) notification code in my service. So, because the service didn't start, the FOREGROUND SERVICE permission wasn't granted. For the record, here is my amended code, which for API 28 includes the code to open a channel. I hope that someone finds this useful.

function TMainService.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
var
  ServiceChannel: JNotificationChannel;
  NotificationManager: JNotificationManager;
  Obj: JObject;
  NewIntent: JIntent;
  ncb: JNotificationCompat_Builder;
  ntf: JNotification;
  PendingIntent: JPendingIntent;
begin

  Result := TJService.JavaClass.START_NOT_STICKY;
  if TJBuild_VERSION.JavaClass.SDK_INT > TJBuild_VERSION_CODES.JavaClass.O then
  begin
    // 28 > 26
    ServiceChannel := TJNotificationChannel.JavaClass.init(
      StringtoJString(CHANNEL_ID),
      StrToJCharSequence('MyApp Service Channel'),
      TJNotificationManager.JavaClass.IMPORTANCE_DEFAULT
      );
    Obj := TAndroidHelper.Context.getSystemService(
      TJContext.JavaClass.NOTIFICATION_SERVICE);
    NotificationManager := TJNotificationManager.Wrap(Obj);
    NotificationManager.createNotificationChannel(ServiceChannel);
    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      JavaService.getApplicationContext, 0, Intent, 0
      );
    ncb := TJNotificationCompat_Builder.JavaClass.init(
      TAndroidHelper.Context,
      StringToJString(CHANNEL_ID)
      );
    ncb.setContentTitle(StrToJCharSequence('MyApp Service'));
    // ncb.setTicker(StrToJCharSequence('Communications Service'));
    ncb.setSmallIcon(JavaService.getApplicationInfo.icon);
    ncb.setContentIntent(PendingIntent);
    ncb.setOngoing(True);
    ntf := ncb.build;
  end
  else
  begin
    // earlier notification code (worked under 10.2.3, targetting API 14)
    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      JavaService.getApplicationContext, 0, Intent, 0
      );
    ntf := TJNotification.Create;
    ntf.icon := JavaService.getApplicationInfo.icon;
    ntf.setLatestEventInfo(
      JavaService.getApplicationContext,
      StrToJCharSequence('MyApp Service'),
      StrToJCharSequence('Communications Service'), PendingIntent);
  end;

  JavaService.startForeground(StartId, ntf);

end;
Freddie Bell
  • 2,186
  • 24
  • 43