Wzmacnianie struktury multimediów

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 mediaserverz 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ń:

zabezpieczenie serwera multimediów

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:

  1. 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).
  2. Sprawdza plik, tworzy odpowiedni ekstraktor dla typu pliku (np. MP3Extractor lub MPEG4Extractor) i zwraca interfejs Binder dla ekstraktora do procesu mediaserver.
  3. Wykonuje wywołania IPC Binder do ekstraktora, aby określić typ danych w pliku (np. dane MP3 lub H.264).
  4. Wywołuje proces mediacodec w celu utworzenia kodeków wymaganego typu; odbiera interfejsy Binder dla tych kodeków.
  5. 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 MediaDrmMediaCrypto 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 MediaDrmMediaCrypto, 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.