Externen Inhalt sicher empfangen
Externe Inhalte per Intent-Filter empfangen und sicher verarbeiten. Dieser Artikel zeigt, wie du MIME-Typen prüfst und fehlende Daten abfängst.
Sobald Nutzer einen Text, ein Bild oder eine Datei aus einer anderen App heraus mit deiner App teilen, landet ein Intent in deinem Code. Wer diesen Intent unkritisch verarbeitet, riskiert Abstürze, Sicherheitslücken und eine schlechte User Experience. Dieser Artikel zeigt dir, wie du externen Inhalt robust empfängst, sorgfältig validierst und sauber in deine App-Architektur integrierst.
Was ist das?
„Receiving Shared Content” bezeichnet die Fähigkeit einer Android-App, Daten zu empfangen, die ein Nutzer über das Betriebssystem-eigene Share-Sheet aus einer anderen App sendet. Das Share-Sheet erscheint, wenn jemand in einer App auf „Teilen” tippt – etwa ein Foto in der Galerie oder einen Link im Browser. Android fragt dann alle installierten Apps, welche diesen Inhalt verarbeiten können, und zeigt eine Auswahlliste an.
Technisch basiert das auf dem Android-Intent-System: Die sendende App erstellt einen Intent mit der Aktion ACTION_SEND (oder ACTION_SEND_MULTIPLE für mehrere Dateien) und einem MIME-Typ, der den Inhalt beschreibt. Deine App muss im AndroidManifest.xml einen passenden Intent-Filter registrieren, damit das System sie als Empfänger anbietet. Ohne diesen Eintrag bleibt deine App im Share-Sheet unsichtbar.
Im Gegensatz zu Daten, die intern in deiner App erzeugt werden, kommen hier Daten von außen – von einem Absender, dessen Korrektheit du nicht kontrollierst. Das macht diesen Einstiegspunkt zu einem der wenigen Punkte in einer Android-App, an dem echte Eingabevalidierung unerlässlich ist.
Wie funktioniert es?
Intent-Filter im Manifest
Du registrierst einen Intent-Filter in der Activity, die den geteilten Inhalt anzeigen soll:
<activity android:name=".ShareReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Der mimeType-Wert entscheidet, für welche Inhaltsarten deine App angeboten wird. Du kannst mehrere Filter definieren, um verschiedene Typen zu unterstützen. Mit */* akzeptierst du theoretisch alles – das ist selten sinnvoll, weil du dann jeden denkbaren MIME-Typ robust verarbeiten müsstest.
Den Intent auslesen
In deiner Activity liest du den eingehenden Intent in onCreate oder onNewIntent aus:
class ShareReceiverActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIncomingIntent(intent)
}
private fun handleIncomingIntent(intent: Intent) {
if (intent.action != Intent.ACTION_SEND) return
val mimeType = intent.type ?: return
when {
mimeType.startsWith("text/") -> {
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: return
processText(text)
}
mimeType.startsWith("image/") -> {
@Suppress("DEPRECATION")
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) ?: return
processImage(uri)
}
else -> return
}
}
}
Beachte die defensiven ?: return-Guards: Jeder empfangene Wert kann fehlen. Sendende Apps sind nicht verpflichtet, alle Extras korrekt zu befüllen, und du hast keinen Einfluss darauf.
MIME-Typ und Validierung
intent.type gibt den MIME-Typ zurück, den die sendende App deklariert hat – dieser Wert ist jedoch nicht vom System verifiziert. Eine bösartige oder fehlerhafte App könnte eine ausführbare Datei als image/png deklarieren. Prüfe deshalb den tatsächlichen Inhalt, bevor du ihn weiterverarbeitest:
- Für URIs: Frage den
ContentResolvernach dem echten MIME-Typ:contentResolver.getType(uri) - Für Dateien: Lese die Magic Bytes oder nutze eine Bibliothek zur Typerkennung, wenn du den Inhalt speicherst oder anzeigst.
In der Praxis
Typische Stolperfalle: Content-URI ohne gültige Berechtigung
Wenn die sendende App eine content://-URI übergibt, hat deine App temporäre Leseberechtigung für diese URI – aber nur, solange die Activity aktiv im Vordergrund gestartet ist. Speicherst du die URI und versuchst zu einem späteren Zeitpunkt, etwa aus einem Hintergrundservice heraus, darauf zuzugreifen, erhältst du eine SecurityException.
Regel: Kopiere den Inhalt sofort in deinen eigenen Speicher (App-internes Verzeichnis, Room-Datenbank oder DataStore), solange du die Berechtigung hast. Speichere niemals eine fremde content://-URI für späteren Zugriff.
private fun processImage(uri: Uri) {
val realMime = contentResolver.getType(uri)
if (realMime?.startsWith("image/") != true) {
showError("Ungültiger Dateityp empfangen.")
return
}
contentResolver.openInputStream(uri)?.use { stream ->
val bytes = stream.readBytes()
saveToLocalStorage(bytes) // In eigenen App-Speicher kopieren
}
}
Architektur-Hinweis
Halte deine ShareReceiverActivity schlank. Sie ist nur Einstiegspunkt: Sie validiert den Intent, extrahiert die Rohdaten und delegiert sofort an ein ViewModel oder einen Repository-Layer aus deiner Data-Layer-Architektur. So bleibt der I/O-Code isoliert und testbar, und deine Activity bleibt auf ihre einzige Aufgabe fokussiert – den Nutzer zu empfangen und ihm Feedback zu geben, während die Verarbeitung im Hintergrund läuft.
Fazit
Receiving Shared Content ist eine der wenigen Stellen in Android, an der fremde, unkontrollierte Daten direkt in deine App fließen. Ein fehlender Null-Check oder eine ungeprüft übernommene MIME-Typ-Angabe reicht für einen Absturz oder eine Sicherheitslücke. Mit konsequenten ?: return-Guards, einem Doppelcheck über ContentResolver.getType() und sofortigem Kopieren der Daten in eigenen Speicher ist das Risiko gut beherrschbar. Öffne jetzt dein Manifest, füge einen Intent-Filter hinzu, und schreibe anschließend einen Instrumentierungstest, der einen gefälschten Intent mit fehlendem EXTRA_STREAM an deine Activity schickt – du wirst schnell sehen, ob dein Code sauber damit umgeht oder abstürzt.