If you're an Android developer working with foreground services, 2026 has brought some significant changes you need to know about. Starting with Android 14 (API 34), Google introduced strict foreground service type requirements that affect how apps like screen translators, media players, and location trackers operate.
In this post, I'll walk through what changed, why it matters, and how we adapted our app — Screen Translator — to comply with the new rules.
What Changed in Android 14+
Before Android 14, you could start a foreground service without specifying a type. The system didn't care what your service was doing — as long as you showed a notification, you were good.
Android 14 changed that. Now, every foreground service must declare a foregroundServiceType in the manifest. If you don't, your app will crash with a MissingForegroundServiceTypeException.
Here are the available types:
<service
android:name=".MyForegroundService"
android:foregroundServiceType="mediaProjection|camera|microphone|location|dataSync|mediaPlayback|phoneCall|connectedDevice|remoteMessaging|health|systemExempted|shortService|specialUse"
android:exported="false" />
You must pick the type(s) that match what your service actually does.
The MediaProjection Challenge
For apps that capture the screen — like Screen Translator — the relevant type is mediaProjection. This is one of the most restricted types because screen capture has obvious privacy implications.
Here's what you need to declare in your AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<service
android:name=".ScreenCaptureService"
android:foregroundServiceType="mediaProjection"
android:exported="false" />
And when starting the service in Kotlin:
val serviceIntent = Intent(this, ScreenCaptureService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForegroundService(serviceIntent)
} else {
startForegroundService(serviceIntent)
}
Inside the service, you must call startForeground() with the correct type:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
)
} else {
startForeground(NOTIFICATION_ID, notification)
}
return START_STICKY
}
Android 15: Even More Restrictions
Android 15 (API 35) added another layer. Now, dataSync foreground services have a 6-hour time limit. After that, the system will stop your service. If your app relied on long-running data sync services, you need to rethink your architecture.
For screen translators, the key restriction is that mediaProjection services now require the user to grant permission every time the app starts a new session. You can no longer cache the MediaProjection token across app restarts.
// This no longer works in Android 15+
// val savedToken = sharedPrefs.getString("projection_token", null)
// You must request a new MediaProjection each time
val mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
startActivityForResult(captureIntent, REQUEST_CODE)
How Screen Translator Adapted
When building Screen Translator, we had to make several architectural changes:
Declared the correct service type: We use
mediaProjectionfor screen capture andspecialUsefor our floating bubble overlay service.Handled the per-session permission flow: Instead of caching the projection token, we now guide users through a clean permission flow each time they start a translation session.
Optimized service lifecycle: We stop the foreground service as soon as the user dismisses the floating bubble, rather than keeping it running in the background.
Graceful degradation: On older Android versions, we fall back to the simpler foreground service API without type declarations.
fun startCaptureService(resultCode: Int, data: Intent) {
val serviceIntent = Intent(context, ScreenCaptureService::class.java).apply {
putExtra("resultCode", resultCode)
putExtra("data", data)
}
ContextCompat.startForegroundService(context, serviceIntent)
}
Common Pitfalls
Here are mistakes I've seen developers make:
Declaring types you don't use: Google Play will reject your app if you declare
cameraorlocationforeground service types but don't actually use those capabilities.Not handling the permission dialog: On Android 14+, the MediaProjection permission dialog looks different and includes a "single app" vs "entire screen" option. Make sure your app handles both cases.
Forgetting the notification channel: Foreground services still require a notification, and on Android 13+ you need the
POST_NOTIFICATIONSpermission too.
Testing Tips
Test your foreground service changes on multiple API levels:
// In your test setup
@Test
fun testForegroundServiceType() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// Verify service type is declared correctly
val serviceInfo = packageManager.getServiceInfo(
ComponentName(context, ScreenCaptureService::class.java),
PackageManager.GET_META_DATA
)
assertTrue(
serviceInfo.foregroundServiceType and
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION != 0
)
}
}
Wrapping Up
The foreground service changes in Android 14 and 15 are significant, but they make sense from a user privacy perspective. As developers, we need to be explicit about what our services do and why they need to run in the foreground.
If you're building an app that uses screen capture, OCR, or floating overlays, these changes will directly affect you. Plan your migration early and test thoroughly across API levels.
For a real-world example of an app that handles all of this, check out Screen Translator on Google Play — it's a floating bubble translator that uses MediaProjection for OCR-based screen translation.
Have questions about foreground services or MediaProjection? Drop a comment below — happy to help!
Top comments (0)