Для мультимедийных проекций требуется служба переднего плана типа ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION в Android Pie и Q

любой орган знает, почему возникает эта ошибка. Для мультимедийных проекций требуется служба переднего плана типа ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION в getMediaProjection (), даже если я вызываю метод getMediaProjection () после вызова метода startforeground () ..!

Stack Trace - это:

  Process: com.al1.screenrecorder, PID: 24641
java.lang.RuntimeException: Unable to start service com.al1.screenrecorder.service.DisplayRecorderService@9ef2101 with Intent { cmp=com.al1.screenrecorder/.service.DisplayRecorderService (has extras) }: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4661)
    at android.app.ActivityThread.access$2900(ActivityThread.java:292)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2246)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:213)
    at android.app.ActivityThread.main(ActivityThread.java:8147)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
 Caused by: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
    at android.os.Parcel.createException(Parcel.java:2071)
    at android.os.Parcel.readException(Parcel.java:2039)
    at android.os.Parcel.readException(Parcel.java:1987)
    at android.media.projection.IMediaProjection$Stub$Proxy.start(IMediaProjection.java:231)
    at android.media.projection.MediaProjection.<init>(MediaProjection.java:75)
    at android.media.projection.MediaProjectionManager.getMediaProjection(MediaProjectionManager.java:104)
    at com.al1.screenrecorder.service.DisplayRecorderService.createMediaProjection(DisplayRecorderService.java:118)
    at com.al1.screenrecorder.service.DisplayRecorderService.onStartCommand(DisplayRecorderService.java:107)
    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4641)
    at android.app.ActivityThread.access$2900(ActivityThread.java:292) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2246) 
    at android.os.Handler.dispatchMessage(Handler.java:107) 
    at android.os.Looper.loop(Looper.java:213) 
    at android.app.ActivityThread.main(ActivityThread.java:8147) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101) 
 Caused by: android.os.RemoteException: Remote stack trace:
    at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(MediaProjectionManagerService.java:498)
    at android.media.projection.IMediaProjection$Stub.onTransact(IMediaProjection.java:135)
    at android.os.Binder.execTransactInternal(Binder.java:1028)
    at android.os.Binder.execTransact(Binder.java:1001)

строгий текст private void startForeground () {

    Intent activityIntent = new Intent(this, MainActivity.class);
    activityIntent.setAction("stop");
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

        String channelId = "001";
        String channelName = "myChannel";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE);
        channel.setLightColor(Color.BLUE);
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if (manager != null) {
            manager.createNotificationChannel(channel);
            Notification notification = new Notification.
                    Builder(getApplicationContext(), channelId)
                    .setOngoing(true)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setCategory(Notification.CATEGORY_SERVICE)
                    .setContentTitle(getString(R.string.ClickToCancel))
                    .setContentIntent(contentIntent)
                    .build();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                startForeground(0, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
            } else {
                startForeground(0, notification);
            }
        }
    } else {
        startForeground(0, new Notification());
    }

}

и onStartCommand:

public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i(TAG, "Service onStartCommand() is called");

    startForeground();

    mResultCode = intent.getIntExtra("code", -1);
    mResultData = intent.getParcelableExtra("data");
    mScreenWidth = intent.getIntExtra("width", 720);
    mScreenHeight = intent.getIntExtra("height", 1280);
    mScreenDensity = intent.getIntExtra("density", 1);
    isVideoSd = intent.getBooleanExtra("quality", true);
    isAudio = intent.getBooleanExtra("audio", true);


    mMediaProjection = createMediaProjection();
    mMediaRecorder = createMediaRecorder();
    mVirtualDisplay = createVirtualDisplay();
    mMediaRecorder.start();

    return Service.START_STICKY;
}

и исключение в этом методе getMediaProjection:

 private MediaProjection createMediaProjection() {
    Log.i(TAG, "Create MediaProjection");
    return ((MediaProjectionManager) Objects.requireNonNull(getSystemService(Context.MEDIA_PROJECTION_SERVICE))).
            getMediaProjection(mResultCode, mResultData);
}

person Mirza Ali    schedule 17.04.2020    source источник


Ответы (7)


Добавьте android:foregroundServiceType="mediaProjection" в элемент <service> в манифесте своей службы.

person CommonsWare    schedule 17.04.2020
comment
пожалуйста, просмотрите еще раз, я отредактировал свой пост и добавил трассировку стека - person Mirza Ali; 17.04.2020
comment
@MirzaAli: AFAIK, Caused by: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION решается за счет наличия android:foregroundServiceType="mediaProjection" в элементе <service> для службы в манифесте. - person CommonsWare; 17.04.2020
comment
‹Сервис android: name = .service.DisplayRecorderService android: foregroundServiceType = mediaProjection /› - person Mirza Ali; 17.04.2020
comment
Привет, @MirzaAli, ты нашел решение? - person mdroid; 17.07.2020

Убедитесь, что вы предоставили необходимые разрешения FOREGROUND_SERVICE. Если ваше targetSdkVersion = 28, использующее службы переднего плана, должно запрашивать разрешение FOREGROUND_SERVICE. Поэтому добавьте разрешение FOREGROUND_SERVICE в свой файл манифеста, без которого он генерирует SecurityException.

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Подробнее об этих примечаниях по миграции можно прочитать: https://developer.android.com/about/versions/pie/android-9.0-migration#tya можно прочитать на этом снимке экрана

Надеюсь, что это поможет вам.....

person Android_id    schedule 18.04.2020

Помещение getMediaProjection(mResultCode, mResultData) внутрь Handler().postDelayed() решило проблему для меня.

override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // We've bound to LocalService, cast the IBinder and get LocalService instance
        val binder = service as MediaProjectionService.LocalBinder
        mediaProjectionService = binder.getService()

        // delay needed because getMediaProjection() throws an error if it's called too soon
        Handler().postDelayed({
            mediaProjection = mediaProjectionManager.getMediaProjection(activityResultCode, activityData)
            startStreaming()
            serviceBound = true
        }, 1000)
    }
person f4b    schedule 24.08.2020

Перед созданием мультимедийной проекции необходимо запустить служебный бит переднего плана.

Если у вас уже есть служба, в большинстве случаев будет задержка для запуска службы, даже если вы запустите службу до инициализации проекции мультимедиа. Начните проецирование через небольшую задержку после запуска сервиса. Я думаю, это поможет.

пример кода:

startService(new Intent(AppController.getInstance(),
            ScreenCaptureService.class));

 new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            MediaProjectionManager projectionManager = (MediaProjectionManager)
                    context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);

           if (isServiceActive(context, ScreenCaptureService.class)){
               mMediaProjection =
                       projectionManager.getMediaProjection(resultCode, data);
               processAndSaveScreenshotFromDevice(context);
           }
        }
    }, 200);
person Abhijith Brumal    schedule 19.01.2021

После добавления разрешения переднего плана <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> вам необходимо создать класс обслуживания. Итак, в новом файле Java есть следующее:

public class BackgroundService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        createNotificationChannel();
        Intent intent1 = new Intent(this, ClassWithError.class);
        Intent intent2 = new Intent(this, AnotherClassWithError.class);

        PendingIntent pendingIntent1 = PendingIntent.getActivity(this, 0, intent1, 0);
        PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, intent2, 0);

        Notification notification1 = new NotificationCompat.Builder(this, "ScreenRecorder")
                .setContentTitle("yNote studios")
                .setContentText("Filming...")
                .setContentIntent(pendingIntent1).build();

        Notification notification2 = new NotificationCompat.Builder(this, "ScreenRecorder")
                .setContentTitle("yNote studios")
                .setContentText("Filming...")
                .setContentIntent(pendingIntent1).build();

        startForeground(1, notification1);
        startForeground(2, notification2);
        return super.onStartCommand(intent, flags, startId);
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            NotificationChannel channel = new NotificationChannel("ScreenRecorder", "Foreground notification",
                    NotificationManager.IMPORTANCE_DEFAULT);
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }

    @Override
    public void onDestroy() {
        stopForeground(true);
        stopSelf();

        super.onDestroy();
    }
}

Затем снова в манифесте добавьте сегмент службы с разделом имени в качестве указанного выше класса службы. Это должно быть в рамках сегмента приложения.

<service
            android:name=".Services.BackgroundService"
            android:enabled="true"
            android:exported="true"
            android:stopWithTask="true"
            android:foregroundServiceType="mediaProjection" />

После такой настройки вы можете получить доступ к этой фоновой службе в любое время (в методе onCreate) следующим образом:

Intent intent = new Intent(this, BackgroundService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent);
        } else {
            startService(intent);
        }

Но обязательно справитесь. Это может быть причиной нескольких исключений в других функциях вашего проекта. Такие вещи, как медиа-рекордеры, будут разрушаться при вызове во время работы этой службы. Для этого используйте метод onDestroy, чтобы закрыть службу.

person Orwa Ogwari    schedule 21.01.2021
comment
Я пробовал вышеуказанное решение, для получения проекций мультимедиа требуется служба переднего плана типа ServiceInfo.FOREGROUND_SERVICE TYPE_MEDIA_PROJECTION. Когда я вызываю capturer.StartCapture (). Также super.onStartCommand (intent, flags, startId) работает в бесконечном цикле. Как мы можем выйти из строя? - person user2801184; 26.01.2021
comment
Вместо этого вы должны использовать ContextCompat.startForegroundService() - person Mark; 03.03.2021

Проблема с кодом OP заключалась в том, что он использовал

startForeground(0, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);

Документы прямо говорят:

Params: id - идентификатор этого уведомления согласно NotificationManager.notify (int, Notification); не должно быть 0.

person Milan Markovic    schedule 13.05.2021

Попробуйте это: https://stackoverflow.com/a/68343645/5182769. Ключ в том, что вы должны сначала создать канал уведомлений в onStartCommand.

person Pan Gao    schedule 12.07.2021