![How To Implement Screen Sharing To Your Android App [2023]. With Code Examples — cover illustration](https://cdn.prod.website-files.com/64e8910adc5a63966a68acea/69e38bbfbcb595eaa2d77d68_64e8910adc5a63966a68ae22_%25D1%2581%25D0%25BA%25D1%2580%25D0%25B8%25D0%25BD%25D1%2588%25D0%25B0%25D1%2580%25D0%25B8%25D0%25BD%25D0%25B3%2520%25D0%25BD%25D0%25B0%2520android.avif)
Key takeaways
• Android screen sharing is three parts: MediaProjection + a foreground service + a WebRTC ScreenCapturerAndroid. Miss one and Android 14+ either crashes your app or silently refuses to start the broadcast.
• Android 14 enforces foregroundServiceType="mediaProjection" + a matching manifest permission. Without FOREGROUND_SERVICE_MEDIA_PROJECTION, startForeground() throws SecurityException.
• Since August 31, 2026, Google Play wants new apps and updates on Android 16 (API 36); the extension window closes November 1, 2026. The projection token has been single-use since Android 14, and Android 15 QPR1 stops every share when the screen locks. Code that cached the consent intent or ignored onStop() is already broken.
• The old org.webrtc:google-webrtc artifact no longer resolves. In 2026 you pull a maintained fork such as io.getstream:stream-webrtc-android. Most tutorials still hand you a dependency that Gradle can't find.
• We ship this in production. The MediaProjection + SFU pattern runs on ProVideoMeeting, Nucleus, and TransLinguist. See the mini case below.
Updated October 2026: checked against Android 17 (API 37), added the Android 15 QPR1 lock-screen auto-stop and status-bar chip, stream-webrtc-android 1.3.10, and a MediaProjection vs AccessibilityService screenshot comparison.
Why Fora Soft wrote this playbook
We have built Android WebRTC clients since 2013: video-conferencing products, telemedicine apps, on-premise secure-comms tools, interactive classrooms. Screen sharing looks like a weekend feature in the docs, then breaks the moment Android ships a new version. Android 10 forced a foreground service. Android 13 added POST_NOTIFICATIONS. Android 14 tied each foreground-service type to its own permission and made the projection token single-use. Android 15 QPR1 added a stop chip to the status bar and ends every share the moment the screen locks.
The 2023 version of this guide covered the basic MediaProjection + WebRTC flow. That flow is still correct, but it stops short. It never showed the API 34+ crashes a target-SDK bump triggers, the SFU-direct pattern for large rooms, the on-device audio trick behind every "share sound" button, or the fact that the library everyone imports has gone dark. This rewrite fixes all of that as of October 2026, with the Kotlin we actually ship. For the deeper WebRTC-transport view, we keep a companion piece: Android WebRTC screen sharing: the complete implementation guide. If you are still building the call client itself, start with our WebRTC on Android client guide.
A targetSdk bump killed your screen share?
We have migrated MediaProjection pipelines across a dozen Android video apps. Send your Crashlytics trace and manifest; we will pin the missing permission or policy in 30 minutes.
What is MediaProjection? The short answer
MediaProjection is the Android API that captures what is on the display into a Surface. It has existed since Android 5.0 (API 21). A screen-sharing app asks for consent with MediaProjectionManager.createScreenCaptureIntent(), receives a MediaProjection token, and renders the screen into a VirtualDisplay that WebRTC encodes and sends (Android media projection docs, 2026).
FOREGROUND_SERVICE_MEDIA_PROJECTION is the manifest permission Android 14 (API 34) added for that capture. Apps targeting API 34+ must declare it together with a service of android:foregroundServiceType="mediaProjection", or the foreground service fails to start. It is granted at install time, so there is no extra dialog; the user-facing consent is the capture prompt itself.
Capture frequency and resolution are not MediaProjection settings. Resolution is the size of the VirtualDisplay and Surface you create (read it from WindowMetrics on API 30+), and frame rate is what your capturer requests, in WebRTC ScreenCapturerAndroid.startCapture(width, height, fps). In our builds a static screen produces few new frames, so slides cost far less bandwidth than shared video.
What changed in Android 14, 15, 16, and 17 — the 2026 briefing
Six platform shifts decide whether your screen share works today. Skim them before copying any snippet.
1. Android 14 (API 34): type + permission enforcement. Your foreground service must declare android:foregroundServiceType="mediaProjection" and your manifest must include <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"/>. Miss the permission and startForeground() throws a SecurityException; miss the type and you get a MissingForegroundServiceTypeException (Android docs, 2026).
2. Android 14 QPR2: partial, single-app capture. This is the one every 2024-era article gets wrong by pinning it to Android 15. The share picker gained a "single app" option in Android 14 QPR2, alongside new MediaProjection callbacks so you can react when the user shares a window instead of the whole screen (Android 14 app screen sharing).
3. Android 14+: consent is per session. A session is one createVirtualDisplay() call. Pass the same consent Intent to getMediaProjection() twice, or call createVirtualDisplay() twice on one MediaProjection, and Android throws SecurityException (media projection, Android 14 rules). Every broadcast needs a fresh createScreenCaptureIntent() and user prompt. Android 15, 16, and 17 kept this rule as it is.
4. Android 15 QPR1: status-bar chip and auto-stop on lock. A large status-bar chip shows any active projection and lets the user stop it with one tap, and projection now stops automatically when the screen locks. Both paths end in MediaProjection.Callback.onStop(), so test by tapping the chip and by locking the phone (Android 15 features). Android 15 also lets the app being shown detect the capture through WindowManager.addScreenRecordingCallback(), so a banking app may hide its content during your share.
5. Android 16 and 17: no new MediaProjection rules. Android 17 (API 37) shipped on June 16, 2026. As of October 2026 neither the Android 16 nor the Android 17 change list has a MediaProjection behavior change. The screen-capture news in Android 17 is the EyeDropper API (ACTION_OPEN_EYE_DROPPER), which picks a color from any pixel without media projection permission (Android 17 release post).
6. Google Play now points at API 36. Since August 31, 2026, new apps and updates must target Android 16 (API 36) to reach Google Play; existing apps need API 35 to stay visible to new users. The extension window closes on November 1, 2026, and that's it (Play target-API requirements). If you're still on targetSdk 33, screen sharing is the least of your worries.
Reach for the single-app picker (Android 14 QPR2+) when you share sensitive apps in the same session and want the presenter to expose one window, not their whole notification shade. Handle the Surface resize when the chosen window rotates, and you inherit the feature for free.
Which WebRTC library actually builds in 2026
Start here, because most guides send you into a wall. Google's org.webrtc:google-webrtc (last real build 1.0.32006) has been unmaintained for years and dropped off Gradle-reachable repositories after the JCenter shutdown. Paste implementation 'org.webrtc:google-webrtc:1.0.32006' into a fresh project in 2026 and the build fails to resolve it.
The maintained answer is a prebuilt fork that mirrors upstream WebRTC and keeps the same org.webrtc.* package, so ScreenCapturerAndroid, PeerConnection, and VideoTrack keep their import paths. The one we reach for is GetStream's, on Maven Central:
// build.gradle.kts (module) — maintained WebRTC, same org.webrtc.* package
dependencies {
implementation("io.getstream:stream-webrtc-android:1.3.10")
// alternative: io.github.webrtc-sdk:android (webrtc-sdk build)
}
Everything below imports from org.webrtc unchanged, so if you migrate an old client you swap the dependency line and keep your capture code. GetStream publishes the source and versioning on its webrtc-android repo; 1.3.10, released September 11, 2025, is the newest build on Maven Central as of October 2026. Pin an explicit version rather than a range so a silent bump can't change encoder behavior mid-release.
Reach for stream-webrtc-android (or webrtc-sdk) when your build can no longer resolve org.webrtc:google-webrtc. If your CI still fetches it from a cached mirror, treat that as tech debt: the mirror will vanish and take a release with it.
Architecture — the five moving parts
Every production Android screen share we ship has the same shape. Knowing which component owns which job keeps you from rewriting half the app when Android changes another rule.

Figure 1. The five components, top to bottom, and what each one owns from consent prompt to remote peer.
- MediaProjectionManager & consent intent. Asks the user to allow screen capture; returns an
Intentyou hand to WebRTC. Single-use on Android 14+. - A dedicated foreground service (
ScreencastService). TypemediaProjection, with a persistent CallStyle or progress notification. - WebRTC
ScreenCapturerAndroid. Wraps the consent intent and delivers frames to aVideoSource. - Signalling & renegotiation. Fires on track swap so the remote peer learns about the new stream.
- Audio path. Optional
AudioPlaybackCapturefor on-device sound (API 29+) or the microphone for narration.
Step 1 — request MediaProjection consent
Use the ActivityResult API. startActivityForResult is deprecated and fights Compose-first navigation.
private val screenPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK && result.data != null) {
startScreencastService(result.data!!)
} else {
// user declined; fall back to camera-only UX
}
}
fun requestScreenCapture() {
val manager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
screenPermissionLauncher.launch(manager.createScreenCaptureIntent())
}
One trap for Android 14+: don't reuse the Intent for a second session. The system invalidates the token after the first use, so every broadcast needs a fresh prompt.
Step 2 — manifest and ScreencastService
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<service
android:name=".ScreencastService"
android:foregroundServiceType="mediaProjection"
android:exported="false"
android:stopWithTask="true" />
class ScreencastService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = ScreencastNotificationBuilder(this).build()
ServiceCompat.startForeground(
this,
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION,
)
return START_STICKY
}
}
Two details save days later. First, use ServiceCompat.startForeground with the type argument so the compat library passes the right flag on every API level. Second, keep the notification build synchronous: do no I/O before startForeground or you overrun the few seconds Android allows and trip ForegroundServiceDidNotStartInTimeException. Pair the service with a CallStyle notification so the OS treats it as an active call; we walk through that in custom Android call notifications.
Step 3 — wire ScreenCapturerAndroid into WebRTC
fun startScreenTrack(permissionData: Intent): VideoTrack {
val surfaceTextureHelper = SurfaceTextureHelper.create(
"ScreencastThread", eglBase.eglBaseContext,
)
val mediaProjectionCallback = object : MediaProjection.Callback() {
override fun onStop() { stopScreencastService() }
}
val capturer = ScreenCapturerAndroid(permissionData, mediaProjectionCallback)
val source = peerConnectionFactory.createVideoSource(/* isScreencast = */ true)
capturer.initialize(surfaceTextureHelper, context, source.capturerObserver)
val metrics = context.resources.displayMetrics
capturer.startCapture(metrics.widthPixels, metrics.heightPixels, 24) // 24 fps is plenty
return peerConnectionFactory.createVideoTrack(SCREEN_TRACK_ID, source)
}
isScreencast = true tells the encoder to use content-type=screen settings: lower keyframe frequency, higher quality for motion-free regions, and an RTP layer tuned for text. Leave it false and you get a smeary broadcast that looks worse than the camera stream it replaced.
Step 4 — renegotiate the peer connection
Swap the camera track for the screen track and WebRTC flags the peer connection dirty, firing onRenegotiationNeeded. Your signalling layer has to rerun the offer/answer exchange, or the remote peer keeps staring at a frozen camera track.
val peerObserver = object : PeerConnection.Observer {
override fun onRenegotiationNeeded() {
scope.launch {
val offer = peerConnection.createOfferSuspend()
peerConnection.setLocalDescriptionSuspend(offer)
signallingClient.send(SignalingMessage.Offer(offer.description))
}
}
// ... other callbacks
}
Use RtpSender.setTrack() instead of removeTrack / addTrack and WebRTC can often skip full renegotiation: the SDP doesn't change, only the track binding does. That's the pattern LiveKit and the current Google sample use. The picture below shows why that choice matters once you leave 1-1 calls.

Figure 2. Replace-the-track works for two people; group rooms want a dedicated screen track so renegotiation doesn't churn per participant.
Need Android + iOS screen share on one SFU?
We ship cross-platform screen sharing end-to-end on LiveKit, Janus, and custom SFUs. Describe your call topology and we will scope a delivery plan fast.
Stopping the broadcast cleanly
Three stop paths exist, and your code has to handle all three or the notification lingers:
1. In-app stop button. Call capturer.stopCapture(), remove the screen track, swap the camera track back in, then stopSelf() on the ScreencastService.
2. User ends the share from the status bar. Android fires MediaProjection.Callback.onStop(). Mirror the teardown from path 1 and tell the remote peer over your signalling channel.
3. Call ends entirely. Shut the service down from the same call-teardown path that releases your peer connection, or the notification outlives the call.
On-device audio capture — the underused piece
Android 10 introduced AudioPlaybackCaptureConfiguration (API 29), which lets a screen-share session capture the audio the device is playing: a training video, a music app, a podcast. Bolt it onto ScreenCapturerAndroid and you get parity with the "share sound" toggle in Zoom, Meet, and Discord (Android playback capture).
val config = AudioPlaybackCaptureConfiguration.Builder(projection)
.addMatchingUsage(AudioAttributes.USAGE_MEDIA)
.addMatchingUsage(AudioAttributes.USAGE_GAME)
.build()
val audioRecord = AudioRecord.Builder()
.setAudioPlaybackCaptureConfig(config)
.setAudioFormat(AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(48_000)
.setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
.build())
.setBufferSizeInBytes(bufferSize)
.build()
Hook the PCM frames into a custom AudioSource and publish them as a second audio track alongside the screen video. Keep the microphone path alive so the presenter can talk over the captured audio, and mix at the remote peer or on the SFU. One caveat: the app producing the sound can opt out with allowAudioPlaybackCapture="false", so DRM-protected media may stay silent by design.
Reach for AudioPlaybackCapture when presenters demo a video or a music tool and the room needs to hear it. Skip it for slide-only sharing: the mic track is enough, and you avoid the RECORD_AUDIO prompt and the opt-out edge cases.
Performance and encoder choices
Three levers control CPU, battery, and picture quality:
1. Resolution. Capture at device resolution and let WebRTC scale down via simulcast. On a Galaxy S23 that means 1080p at source with 540p and 360p simulcast layers for the SFU.
2. Codec. H.264 hardware encoder for broad compatibility. VP9 or AV1 on devices that support them for sharper screen text; WebRTC negotiates automatically when both peers agree.
3. Frame rate. 15 fps for slides, 24 fps for general content, 30 fps for games and video. Higher rates drain battery fast and rarely help. If you want the fundamentals behind frame rate and bitrate, our Learn primer on digital video covers the basics.

Figure 3. Where content-type=screen and simulcast do their work before the SFU forwards the right layer to each viewer.
Always create the source with createVideoSource(isScreencast = true). The screencast flag flips the encoder into content-type=screen mode, which keeps text crisp and cut bitrate by roughly 25% on static slides in our Nucleus measurements.
Reach for VP9 or AV1 when the shared content is a text-heavy IDE or a spreadsheet and both peers can decode it. The keyframe savings show up exactly where H.264 smears: thin fonts on flat backgrounds. Keep an H.264 fallback for the phones that can't.
MediaProjection vs AccessibilityService.takeScreenshot()
For live screen sharing, use MediaProjection. AccessibilityService.takeScreenshot() (API 30) and takeScreenshotOfWindow() (API 34) return one still frame per call as a HardwareBuffer. The system rejects calls that come too fast with ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT, and the app must run as an accessibility service the user turns on in Settings (AccessibilityService reference). That suits an assistant that glances at the screen now and then, not a 15–30 fps stream.
| Criterion | MediaProjection | AccessibilityService screenshot |
|---|---|---|
| Output | Continuous frames into a Surface | One still image per call |
| Frame rate | Whatever the capturer asks for (15–30 fps typical) | Rate-limited by the system |
| User consent | Capture prompt every session (Android 14+) | User enables the service in Accessibility settings |
| Foreground service | Required, type mediaProjection | Not required |
| Google Play review | Standard permission declarations | Reviewed under the Accessibility API policy |
| Best for | Calls, webinars, remote support | Occasional screen reading by an assistant |
The trade-off showed up in 2026: Android Authority reported on May 1, 2026 that OpenAI was testing an Accessibility-plus-Bubbles route for ChatGPT screen help, because continuous MediaProjection capture costs battery and CPU (Android Authority, 2026). For a call where people watch the screen live, nothing replaces MediaProjection.
Publishing the screen as a second track to your SFU
Small 1-1 calls survive a track swap. Rooms with 10+ participants should publish the screen as a separate SFU track instead of replacing the camera. Three reasons:
- Participants decide whether to show camera, screen, or both in a tiled grid.
- Renegotiation cost scales with room size when you swap tracks; a dedicated screen track avoids SDP churn per participant.
- Mobile data savings: SFUs with simulcast can drop the screen layer independently when a viewer is on cellular.
LiveKit, Agora, Janus, Daily, 100ms, and mediasoup all accept a second RtpSender from the same peer connection with a different MediaStreamTrack. Label the screen track clearly (say screen_presenter_123) so the UI can tell it apart from camera tiles. If you're still choosing a media topology, our P2P vs MCU vs SFU breakdown covers the trade-offs.
Reach for a dedicated SFU screen track when your rooms pass three or four people. Below that, a plain camera-to-screen swap is simpler and fine. Above it, the second track is what keeps join latency and CPU flat as the room grows.
Mini case — screen share for an on-premise comms platform
On Nucleus, an on-premise comms product used in regulated environments, we shipped Android screen sharing for Samsung Galaxy A-series fleets running Android 13 and 14. The original build replaced the camera track, forced full SDP renegotiation, and crashed on any device where the target hit API 34 after a firmware bump.
Our fix, over three weeks: added FOREGROUND_SERVICE_MEDIA_PROJECTION and POST_NOTIFICATIONS, moved to ServiceCompat.startForeground with the type argument, switched to publishing the screen as a second SFU track, and added the AudioPlaybackCapture path so users can share a training video with sound. The numbers moved the way you'd hope.

Figure 4. Same app, after the Android 14 foreground-service and SFU-track migration.
Crash rate on API 34 devices went to zero inside 10 days. Median time-to-first-frame after the picker tap dropped from 3.1s to 1.4s. Total effort was roughly 140 engineering hours including QA across seven device models, accelerated with our Agent Engineering workflow. Want the same triage for your app? Book a 30-min review.
Permissions and policy matrix (Android 13–17)
| Item | Android 13 | Android 14 | Android 15 | Android 16 | Android 17 |
|---|---|---|---|---|---|
FOREGROUND_SERVICE |
Required | Required | Required | Required | Required |
FOREGROUND_SERVICE_MEDIA_PROJECTION |
— | Required | Required | Required | Required |
foregroundServiceType="mediaProjection" |
Required | Required | Required | Required | Required |
POST_NOTIFICATIONS |
Runtime prompt | Runtime prompt | Runtime prompt | Runtime prompt | Runtime prompt |
| MediaProjection token reuse | Allowed | One-shot, consent per session | One-shot, consent per session | One-shot, consent per session | One-shot, consent per session |
| Partial-app-window sharing | — | QPR2+ | User-selectable | User-selectable | User-selectable |
| Status-bar stop chip + auto-stop on lock | — | — | QPR1+ | Yes | Yes |
| Google Play target-SDK to reach new users | — | Legacy only | Minimum (existing apps) | Required (new apps & updates) | Not required (October 2026) |
A decision framework — pick the right pattern in five questions
1. 1-1 call or group? 1-1 → track swap is fine. Group → publish the screen as a second SFU track.
2. Do presenters share audio? Yes → AudioPlaybackCapture + a second audio track. No → skip it.
3. What's your content? Slides → 15 fps, 1080p. Demo video or game → 30 fps, let simulcast downscale. Text-heavy IDE → VP9 or AV1 if both peers support it.
4. Which Android minimum? API 26 is the realistic floor; API 29+ for AudioPlaybackCapture; target API 36 for new Play releases.
5. Do you have a WebRTC SFU already? Yes → done. No → stand up LiveKit or mediasoup before building screen share on peer-to-peer, which won't scale past four participants.

Figure 5. The same five questions as a decision tree; follow the trunk down until an outcome branches right.
Six pitfalls we keep finding in audits
These six come up in nearly every Android client our WebRTC consulting engineers review. Each is a one-line fix once you know where to look.
1. Importing org.webrtc:google-webrtc. The build fails to resolve it, or worse, resolves from a cached mirror that will disappear. Move to io.getstream:stream-webrtc-android or webrtc-sdk.
2. SecurityException: Media projections require a foreground service of type FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION. You forgot the permission or the foregroundServiceType attribute. Add both.
3. Starting the foreground service from the background. Android 12+ throws ForegroundServiceStartNotAllowedException. Start it from the user-gesture activity that also launches the consent picker, or from a high-priority FCM callback.
4. Passing isScreencast = false to createVideoSource. You waste 25–40% bandwidth on static slides (the range we see in audits) and the text looks blurry. Flip it on.
5. Caching the MediaProjection intent across sessions. Worked on Android 13, throws on 14+. Request fresh consent every broadcast.
6. Ignoring MediaProjection.Callback.onStop. The user can end the share from the status bar; if your service doesn't tear down, the notification lingers and Android may flag the app. Wire onStop to stopSelf(). Many of these overlap with generic foreground-service rules we cover in foreground services and deep links on Android.
KPIs — what to measure after shipping
Quality KPIs. Time-to-first-frame after consent (target < 1.5s), crash rate of ForegroundService*Exception per 1,000 sessions (target 0), and frozen-screen rate (target < 0.5%).
Business KPIs. Screen-share adoption per call (baseline vs post-launch), paid-plan conversion lift when the feature is gated, and the retention delta on sessions that included a screen share.
Reliability KPIs. Clean-teardown rate (target > 99%), median battery drain over a 30-minute broadcast (benchmark < 8% on a Pixel 8), and on-device audio-capture success rate (target > 97% on devices at API 29+).
When not to ship Android screen sharing
1. Peer-to-peer only, three or more participants. Screen share multiplies media paths; without an SFU, uplink on a mid-tier phone collapses after the third viewer. Move to a media server first.
2. Strict compliance environments without MediaProjection approval. Some MDM profiles disable MediaProjection entirely; confirm before you promise the feature. A server-rendered document-sharing flow may be the right alternative.
3. Android TV or kiosk devices. MediaProjection exists there but rarely helps: the screen is the product. Ship in-app slides instead.
How much does Android screen sharing cost to build?
On an app that already has a working WebRTC client, budget 7–10 engineering days for the core: MediaProjection consent, the foreground service, the capturer, renegotiation, and QA across three or four device classes. Add 3–5 days for AudioPlaybackCapture and SFU multi-track publishing. Here's the arithmetic we use to sanity-check a quote:
Core screen share 8 dev-days Audio + SFU track 4 dev-days QA (7 device models) 2 dev-days -------------------------------- Total 14 dev-days x ~7 focused hrs = ~98 hours
That lines up with the ~140 hours the Nucleus fix took once you add the platform-migration and regression work a live app carries. Green-field is cheaper; a tangled legacy client is more. We use Agent Engineering to compress the routine parts, so our estimates land below the typical agency day-count for the same scope. If a number feels too precise to trust, treat it as a range and let us scope the real thing against your codebase. Our video chat and calling app development team ships screen sharing as part of Android and iOS call clients.
Planning an Android 14–17 screen-share migration?
We have done it for telemedicine, video-calling, and regulated comms apps. Send your manifest and we will scope a fixed delivery.
FAQ
Why does my app crash with SecurityException on Android 14 the moment screen sharing starts?
Android 14 needs two things: the FOREGROUND_SERVICE_MEDIA_PROJECTION permission in the manifest, and android:foregroundServiceType="mediaProjection" on the <service>. The manifest declaration and the startForeground() type argument must match exactly.
Which WebRTC library still builds for Android screen sharing in 2026?
Not org.webrtc:google-webrtc — it's unmaintained and no longer resolves through Gradle. Use a maintained prebuilt fork such as io.getstream:stream-webrtc-android or the webrtc-sdk build. Both keep the org.webrtc.* package, so ScreenCapturerAndroid keeps working unchanged.
Is the MediaProjection consent reusable for a later broadcast?
Not on Android 14+. A token is good for one createVirtualDisplay() call; reusing the Intent or the token throws SecurityException. Android 15, 16, and 17 keep the same rule. Every session calls createScreenCaptureIntent() and re-prompts the user.
Replace the camera track, or add a separate screen track?
Replace for 1-1 calls where bandwidth is tight. For group calls on an SFU, publish the screen as a second track so participants can show camera and screen at once, and simulcast can downscale the screen layer for viewers on cellular.
Why does my shared screen look blurry, especially the text?
You almost certainly called createVideoSource(false). Passing true flips WebRTC into content-type=screen mode: fewer keyframes, tuned bitrate, sharper text. You can also raise the simulcast high layer to 1080p and drop the frame rate to 15 fps for slide-heavy content.
How do you capture device audio (not the mic) during screen sharing?
Use AudioPlaybackCaptureConfiguration (API 29+) on the same MediaProjection token, build an AudioRecord with it, and push the PCM frames into a custom WebRTC audio track. This is how "share audio" works in Zoom, Meet, and Discord. Apps can opt out, so DRM-protected media may stay silent.
The remote peer never sees the screen after a track swap. What's wrong?
You're missing the renegotiation step. Swapping tracks fires onRenegotiationNeeded; your signalling code must recreate the SDP offer and send it to the remote. Or use RtpSender.setTrack(), which often avoids renegotiation entirely.
How long does it take to ship production-quality Android screen share?
On an existing WebRTC Android client, budget 7–10 engineering days for MediaProjection integration, the foreground service, and QA across three or four device classes. Add 3–5 days for AudioPlaybackCapture and SFU multi-track publishing. We usually land the full scope in about 10 calendar days with Agent Engineering.
What is FOREGROUND_SERVICE_MEDIA_PROJECTION?
It is the manifest permission Android 14 (API 34) requires for screen capture. Declare it next to FOREGROUND_SERVICE and give your capture service android:foregroundServiceType="mediaProjection". It is granted at install time, so users see no extra dialog.
Why does screen sharing stop when the phone locks?
Since Android 15 QPR1, any media projection stops automatically when the screen locks, and users can also end it from a status-bar chip. Handle MediaProjection.Callback.onStop() to tear down the track and tell the remote side.
MediaProjection or AccessibilityService.takeScreenshot() for screen capture?
MediaProjection for anything live. takeScreenshot() (API 30) returns rate-limited still frames and needs an accessibility service the user enables, which fits an occasional screen reader, not a 15–30 fps share.
What to read next
WEBRTC
Android WebRTC Screen Sharing: Complete Guide
The deeper WebRTC-transport companion to this platform-focused playbook.
ANDROID PLATFORM
Foreground Services and Deep Links on Android
The Android 14 type + permission rules your screen-share service has to follow.
iOS
Implement Screen Sharing in an iOS App
The ReplayKit Broadcast Upload Extension counterpart to this Android guide.
MEDIA ARCHITECTURE
P2P vs MCU vs SFU — Which Media Server Fits?
The media topology that dictates how you publish the screen track.
Ready to ship Android screen sharing that survives the next platform bump?
Android screen sharing in 2026 comes down to five decisions: pull a WebRTC library that still builds, request consent the modern way, run a foreground service of type mediaProjection with the matching permission, wire ScreenCapturerAndroid into a WebRTC source with isScreencast = true, and let your SFU own the track so group calls scale. Add AudioPlaybackCapture when the presenter shares sound.
If you want a team that has shipped this across WebRTC, telehealth, and regulated-comms products, we have the Kotlin templates, the Android 14–17 compliance checklist, and the QA matrix ready to go.
The book · Volume 3 of 7
Video Streaming: A Complete Guide to Video Delivery: From ABR and CDNs to Players, WebRTC, DRM, and the Economics of Streaming
Getting MediaProjection to capture is the first half; the second is sending a screen track that stays sharp on a weak network. Volume 3 covers WebRTC and real-time delivery, simulcast and bitrate adaptation, how SFUs forward extra tracks, and QoE monitoring. It gives Android teams the delivery side of screen sharing. Written by Nikolay Sapunov, CEO at Fora Soft.
Read it on Amazon →Kindle & paperback · Fora Soft Video Engineering Handbook
Book a 30-minute review of your Android screen-share design
We will critique your manifest, service, WebRTC pipeline, and SFU track strategy, then hand back a concrete punch list. Agent Engineering-accelerated.
