Aby zwiększyć bezpieczeństwo urządzenia, Android 7.0 dzieli monolityczny proces mediaserver
na wiele procesów z uprawnieniami i funkcjami ograniczonymi tylko do tych wymaganych przez poszczególne procesy. Te zmiany
zmniejszają luki w zabezpieczeniach w ramach frameworku multimediów, ponieważ:
- Podział komponentów ścieżki przetwarzania multimediów na procesy piaskownicy dla poszczególnych aplikacji.
- Włączanie komponentów multimedialnych, które można aktualizować (wyodrębnianie, kodeki itp.).
Te zmiany poprawiają też bezpieczeństwo użytkowników, ponieważ znacznie zmniejszają powagę większości luk w zabezpieczeniach związanych z multimediami, chroniąc w ten sposób urządzenia i dane użytkowników.
Producenci OEM i producenci układów SoC muszą wprowadzić zmiany w HAL i ramówce, aby zapewnić zgodność z nową architekturą. Ponieważ kod Androida dostarczony przez dostawcę często zakłada, że wszystko działa w tym samym procesie, dostawcy muszą zaktualizować kod, aby przekazywać natywnych uchwytów (native_handle
), które mają znaczenie w różnych procesach. Implementację zmian związanych z zabezpieczeniem multimediów znajdziesz w dokumentach frameworks/av
i frameworks/native
.
Zmiany architektoniczne
W poprzednich wersjach Androida używano pojedynczego monolitycznego procesu mediaserver
z wiele uprawnieniami (dostęp do aparatu, dostęp do dźwięku, dostęp do sterownika wideo, dostęp do plików, dostęp do sieci itp.). Android 7.0 dzieli proces mediaserver
na kilka nowych procesów, z których każdy wymaga znacznie mniejszego zestawu uprawnień:
Rysunek 1. Zmiany w architekturze w celu wzmocnienia bezpieczeństwa mediaserver
Ta nowa architektura zapewnia, że nawet jeśli proces zostanie naruszony, złośliwy kod nie będzie miał dostępu do pełnego zestawu uprawnień wcześniej posiadanych przez mediaserver
. Procesy są ograniczone przez zasady SELinux i seccomp.
Uwaga: ze względu na zależności od dostawcy niektóre kodeki nadal działają w ramach mediaserver
i w konsekwencji przyznają mediaserver
więcej uprawnień niż to konieczne. W szczególności Widevine Classic nadal działa w mediaserver
na Androidzie 7.0.
Zmiany w MediaServer
W Androidzie 7.0 proces mediaserver
służy do obsługi odtwarzania i nagrywania, np. przekazywania i synchronizowania buforów między komponentami i procesami. Procesy komunikują się za pomocą standardowego mechanizmu Binder.
W standardowej sesji odtwarzania pliku lokalnego aplikacja przekazuje plik opisowy (FD) do mediaserver
(zwykle za pomocą interfejsu API MediaPlayer w języku Java), a mediaserver
:
- Owija FD w obiekt Binder DataSource, który jest przekazywany do procesu extractor, który używa go do odczytu z pliku za pomocą Binder IPC. (MediaExtractor nie pobiera danych z FD, ale zamiast tego wywołuje ponownie Bindera w przypadku
mediaserver
, aby pobrać dane). - Sprawdza plik, tworzy odpowiedni ekstraktor dla typu pliku (np. MP3Extractor lub MPEG4Extractor) i zwraca interfejs Binder dla ekstraktora do procesu
mediaserver
. - Wykonuje wywołania IPC Binder do ekstraktora, aby określić typ danych w pliku (np. dane MP3 lub H.264).
- Wywołuje proces
mediacodec
w celu utworzenia kodeków wymaganego typu; odbiera interfejsy Binder dla tych kodeków. - Wykonuje powtarzające się wywołania interfejsu Binder IPC do ekstraktora w celu odczytania zakodowanych próbek, używa interfejsu Binder IPC do wysyłania zakodowanych danych do procesu
mediacodec
w celu dekodowania i odbiera zdekodowane dane.
W niektórych przypadkach nie jest używany żaden kodek (np. w przypadku odciążonego odtwarzania, w którym zakodowane dane są wysyłane bezpośrednio do urządzenia wyjściowego), albo kodek może renderować bezpośrednio odkodowane dane zamiast zwracać bufor odkodowanych danych (odtwarzanie wideo).
Zmiany w MediaCodecService
Usługa kodeka to miejsce, w którym znajdują się kodery i dekodery. Ze względu na zależności od dostawcy nie wszystkie kodeki są jeszcze dostępne w procesie. W Androidzie 7.0:
- Niezabezpieczone dekodery i oprogramowanie do kodowania działają w procesie kodeka.
- Bezpieczne dekodery i kodery sprzętowe są dostępne w
mediaserver
(bez zmian).
Aplikacja (lub mediaserver
) wywołuje proces kodeka w celu utworzenia kodeka odpowiedniego typu, a następnie wywołuje ten kodek, aby przekazać zakodowane dane i pobrać odkodowane dane (do dekodowania) lub przekazać odkodowane dane i pobrać zakodowane dane (do kodowania). Przesyłanie danych do kodeków i z nich korzysta już z pamięci współdzielonej, więc ten proces nie uległ zmianie.
Zmiany w MediaDrmServer
Serwer DRM jest używany podczas odtwarzania treści chronionych przez DRM, takich jak filmy w Filmach Google Play. Odszyfrowuje zaszyfrowane dane w bezpieczny sposób, a więc ma dostęp do certyfikatu, magazynu kluczy i innych wrażliwych komponentów. Ze względu na zależności od dostawcy proces DRM nie jest jeszcze używany we wszystkich przypadkach.
Zmiany w AudioServer
Proces AudioServer hostuje komponenty związane z dźwiękiem, takie jak wejście i wyjście audio, usługa policymanager, która określa kierowanie dźwięku, oraz usługa radia FM. Szczegółowe informacje o zmianach dotyczących dźwięku i wskazówki dotyczące wdrożenia znajdziesz w artykule Wdrażanie dźwięku.
Zmiany w CameraServer
Usługa CameraServer kontroluje kamerę i jest używana podczas nagrywania wideo do pobierania klatek wideo z kamery, a następnie przekazywania ich do usługi mediaserver
w celu dalszego przetwarzania. Szczegółowe informacje o zmianach i wskazówki dotyczące wdrażania zmian w CameraServer znajdziesz w artykule Wzmacnianie interfejsu Camera Framework.
Zmiany w ExtractorService
Usługa wyodrębniania hostuje wyodrębnianie, czyli komponenty, które analizują różne formaty plików obsługiwane przez framework multimediów. Usługa ekstraktora ma najmniejsze uprawnienia ze wszystkich usług – nie może czytać FD, więc zamiast tego wykonuje wywołania do interfejsu Binder (udostępnionego przez każdą sesję odtwarzania), aby uzyskać dostęp do plików.mediaserver for
Aplikacja (lub mediaserver
) wywołuje proces ekstrakcji, aby uzyskać IMediaExtractor
, wywołuje IMediaExtractor
, aby uzyskać IMediaSources
dla utworu zawartego w pliku, a następnie wywołuje IMediaSources
, aby odczytać dane z tych plików.
Aby przenosić dane między procesami, aplikacja (lub mediaserver
) umieszcza dane w elementach reply-Parcel jako część transakcji Bindera lub używa współdzielonej pamięci:
- Korzystanie z wspólnej pamięci wymaga dodatkowego wywołania Bindera, aby zwolnić wspólną pamięć, ale jest szybsze i wymaga mniej energii w przypadku dużych buforów.
- Korzystanie z in-Parcel wymaga dodatkowego kopiowania, ale jest szybsze i zużywa mniej energii na bufory mniejsze niż 64 KB.
Implementacja
Aby umożliwić przeniesienie komponentów MediaDrm
i MediaCrypto
do nowego procesu mediadrmserver
, dostawcy muszą zmienić metodę przydzielania bezpiecznych buforów, aby umożliwić współdzielenie buforów między procesami.
W poprzednich wersjach Androida bezpieczne bufory są przydzielane w mediaserver
przez OMX::allocateBuffer
i używane podczas odszyfrowywania w ramach tego samego procesu, jak pokazano poniżej:
Rysunek 2. Przydzielanie bufora w mediaserverze w Androidzie 6.0 i starszych.
W Androidzie 7.0 proces przydzielania bufora został zmieniony na nowy mechanizm, który zapewnia elastyczność przy jednoczesnym minimalizowaniu wpływu na istniejące implementacje. W ramach nowego procesu mediadrmserver
moduły MediaDrm
i MediaCrypto
, a także bufory są przydzielane inaczej, a dostawcy muszą aktualizować uchwyty zabezpieczeń bufora, aby można je było przenosić przez binder, gdy MediaCodec
wywołuje operację odszyfrowywania na MediaCrypto
.
Rysunek 3. Przydzielanie bufora w mediaserverze w Androidzie 7.0 i nowszych.
Używanie natywnych nicków
Funkcja OMX::allocateBuffer
musi zwracać wskaźnik do struktury native_handle
, która zawiera opisy plików (FD) i dodatkowe dane całkowite. native_handle
ma wszystkie zalety korzystania z FD, w tym obsługę bindera do serializacji i deserializacji, a także większą elastyczność dla dostawców, którzy obecnie nie korzystają z FD.
Użyj native_handle_create()
, aby przypisać domyślny identyfikator.
Kod frameworku przejmuje własność przydzielonej struktury native_handle
i jest odpowiedzialny za zwalnianie zasobów zarówno w procesie pierwotnego przydzielenia native_handle
, jak i w procesie deserializacji. Platforma zwalnia natywne uchwyty za pomocą funkcji native_handle_close()
, a następnie native_handle_delete()
, a także serializuje i deserializuje obiekt native_handle
za pomocą funkcji Parcel::writeNativeHandle()/readNativeHandle()
.
Dostawcy SoC, którzy używają FD do reprezentowania bezpiecznych buforów, mogą wypełnić FD w native_handle
swoim FD. Dostawcy, którzy nie korzystają z buforów FD, mogą reprezentować bezpieczne bufory za pomocą dodatkowych pól w native_buffer
.
Ustawianie lokalizacji odszyfrowywania
Dostawcy muszą zaktualizować metodę odszyfrowywania OEMCrypto, która działa na native_handle
, aby wykonać operacje specyficzne dla dostawcy, które są niezbędne do użycia native_handle
w nowej przestrzeni procesu (zmiany obejmują zazwyczaj aktualizacje bibliotek OEMCrypto).
Ponieważ allocateBuffer
to standardowa operacja OMX, Android 7.0 zawiera nowe rozszerzenie OMX (OMX.google.android.index.allocateNativeHandle
) do sprawdzania obsługi tej funkcji oraz wywołanie OMX_SetParameter
, które informuje implementację OMX, że powinna używać natywnych uchwytów.