ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android 13 - FGS Task Manager
    프로그래밍/Android 2022. 5. 11. 19:11
    반응형

    What it is?

    • FGS
      • Foreground Service
    • FGS Task Manager
      • Regardless of target SDK version. 
      • Android 13 allows users to stop foreground services. 

     

    더보기

    Foreground services

    Foreground services perform operations that are noticeable to the user.

    It show a status bar notification, so that users are actively aware that your app is performing a task in the foreground and is consuming system resources. The notification cannot be dismissed unless the service is either stopped or removed from the foreground.

    Examples of apps that would use foreground services include the following:

    • A music player app that plays music in a foreground service. The notification might show the current song that is being played.
    • A fitness app that records a user's run in a foreground service, after receiving permission from the user. The notification might show the distance that the user has traveled during the current fitness session.

     

    How?

    • Foreground Services (FGS) Task Manager
      • New UI on notification drawer
      • Shows a list of apps that are currently running a foreground service.
    • On Android 13 device

     

    Why?

    • Foreground service needs to start with notification.
    • From Android 13,
    • Service needs to be aware but the user can forget if something is still running in the background. This can be abuse. To solve this problem Android 13 allows the user stops foreground service.

    swipe_away.mp4
    1.21MB

     

    User action stops

    When the user presses the Stop button next to your app in the FGS Task Manager, then your entire app stops, not just the running foreground service.

    Comparing behavior with "swipe up" and "force stop" user actions

    "swipe up" : Finish the application from the Recents screen

    "force stop" : Finish the application for a misbehaving app. ex) crash, won't response

     

    Comparing application behavior

      FGS Task Manager Swipe up Force stop
    Immediately removes app from memory O   O
    Media playback is stopped O   O
    FGS stopped / Associated notification removed O   O
    Removes the activity back stack O O O
    Removes app from history   O O
    Scheduled jobs are canceled     O
    Alarms are canceled     O
    • Foreground service -> Exception -> JobScheduler doesn't work.
    • Foreground service -> "Stop" -> JobScheduler works well.

     

    Comparing lifecycle

    // Example codes
    // MyApplication.kt
    class MyApplication : Application {...}
    
    // MainActivity.kt
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(Intent(this, MyPlayerService::class.java))
            } else {
                startService(Intent(this, MyPlayerService::class.java))
            }
        }
    }
    
    // MyPlayerService.kt
    class MyPlayerService : Service() {
        override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
            ...
            startForeground(NOTIFICATION_ID, notification)
        }
        
        override fun onTaskRemoved(rootIntent: Intent?) {
            super.onTaskRemoved(rootIntent)
            stopSelf()
        }
    }
    // Swipe up
    MyApplication: onCreate()
    LogActivity: onCreate() savedInstanceState=null
    LogActivity: onStart()
    LogActivity: onResume()
    MyPlayerService: onCreate()
    MyPlayerService: onStartCommand()
    
    // swipe up
    LogActivity: onPause()
    LogActivity: onStop()
    LogActivity: onDestroy()
    MyPlayerService: onTaskRemoved()
    MyPlayerService: onDestroy()
    
    // relaunched.
    LogActivity: onCreate() savedInstanceState=null
    LogActivity: onStart()
    LogActivity: onResume()
    MyPlayerService: onCreate()
    MyPlayerService: onStartCommand()

     

    // Crash
    MyApplication: onCreate()
    LogActivity: onCreate() savedInstanceState=null
    LogActivity: onStart() 
    LogActivity: onResume() 
    MyPlayerService: onCreate()
    MyPlayerService: onStartCommand()
    
    // crashed application
    // relaunched.
    MyApplication: onCreate()
    LogActivity: onCreate() savedInstanceState=null
    LogActivity: onStart() 
    LogActivity: onResume()
    MyPlayerService: onCreate()
    MyPlayerService: onStartCommand()

     

    // FGS stop button
    LogActivity: onCreate() savedInstanceState=null
    LogActivity: onStart()
    LogActivity: onResume() 
    MyPlayerService: onCreate()
    MyPlayerService: onStartCommand()
    
    // tap stop button
    // relaunched.
    MyApplication: onCreate()
    LogActivity: onCreate() savedInstanceState=null
    LogActivity: onStart()
    LogActivity: onResume()
    MyPlayerService: onCreate()
    MyPlayerService: onStartCommand()

     

    No callbacks sent when user stops app from FGS Task Manager

    The system doesn't send your app any callbacks when the user presses the Stop button. When your app starts back up, it might be helpful for you to check for the new REASON_USER_REQUESTED reason that's part of the existing ApplicationExitInfo API.

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val applicationExitInfos = am.getHistoricalProcessExitReasons(null, 0, 0)
        val exitInfo = applicationExitInfos.firstOrNull() ?: return
        
        Log.d("TAG", """applicationExitInfos: reason=${exitInfo.reason} " +
                "|description=${exitInfo.description} " +
                "|date=${Date(exitInfo.timestamp)}""".trimMargin())
    }
    
    // applicationExitInfos: reason=REASON_USER_REQUESTED 
    // description=fully stop com.example.examplefgs/0 by user request 
    // date=Mon May 16 22:22:08 GMT+09:00 2022

     

    System prompts related to long-running foreground services

    If the system detects that your app runs a foreground service for a long period of time—at least 20 hours within a 24-hour window—the system sends a notification to the user, inviting them to interact with the FGS Task Manager.

    https://developer.android.com/about/versions/13/changes/battery#system-notification-long-running-fgs

     

    배터리 리소스 사용률  |  Android 13 개발자 프리뷰  |  Android Developers

    Android 13 개발자 프리뷰에 오신 것을 환영합니다. Android 13을 최고의 버전으로 만들 수 있도록 의견을 제공해 주세요. 배터리 리소스 사용률 Android 13에서는 다음과 같은 배터리 수명 보존 조치가

    developer.android.com

     

    Exemptions

     

    Exemptions from appearing in the FGS Task Manager at all

    The following apps can run a foreground service and not appear in the task manager at all:

    • System-level apps
    • Safety apps; that is, apps that have the ROLE_EMERGENCY role. (RoleManager from Android 10)
    • Devices that are in demo mode
     
     

    Exemptions from being stoppable by users

    When the following types of apps run a foreground service, they appear in the FGS Task Manager, but there is no Stop button next to the app's name for the user to press:

     

    RoleManager?

    // AndroidManifest.xml
    // ROLE_DIALER
    <activity
        android:name=".rolemanager.RoleManagerActivity"
        android:exported="true">
    
        <intent-filter>
            <action android:name="android.intent.action.DIAL" />
            <category android:name="android.intent.category.DEFAULT" />
    
            <data android:scheme="tel" />
        </intent-filter>
    
        <intent-filter>
            <action android:name="android.intent.action.DIAL" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
    
    class RoleManagerActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            requestRole()
        }
        
        private fun requestRole() {
            val roleManager: RoleManager = this.getSystemService(ROLE_SERVICE) as RoleManager
            
            // Check whether a role is available in the system.
            val isDialerRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_DIALER)
            
            // Check whether the calling application is holding a particular role.
            val isDialerRoleHeld = roleManager.isRoleHeld(RoleManager.ROLE_DIALER)
            if (isDialerRoleAvailable && !isDialerRoleHeld) {
                launchRequestDialerRoleIntent(roleManager)
            }
        }
    
        private fun launchRequestDialerRoleIntent(roleManager: RoleManager) {
            val intent: Intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER)
            val resultLauncher =
                registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                    if (result.resultCode == RESULT_OK) {
                        // do nothing
                    }
                }
            resultLauncher.launch(intent)
        }
    }

     

    How to migration?

    Use JobSchedule or AlarmManager rather than believe in foreground service.

    Because lifecycle can be strange.

    // Not called when tap "Stop" button in FGS Task Manager
    LogActivity: onPause()
    LogActivity: onStop()
    LogActivity: onDestroy()
    MyPlayerService: onTaskRemoved()
    MyPlayerService: onDestroy()

    https://youtu.be/t1_8WSEguDY?t=648 

    https://developer.android.com/about/versions/13/changes/fgs-manager

     

     

    반응형
Designed by Tistory.