시스템 장식 지원

이러한 디스플레이 관련 영역에 적용된 업데이트는 아래와 같습니다.

시스템 장식

Android 10에서는 보조 디스플레이를 구성하여 배경화면, 탐색 메뉴, 런처와 같은 특정한 시스템 장식을 표시할 수 있습니다. 기본적으로 기본 디스플레이에는 모든 시스템 장식이 표시되며, 보조 디스플레이에는 선택적으로 사용 설정된 항목이 표시됩니다. 입력 방식 편집기(IME) 지원은 다른 시스템 장식과는 별도로 설정할 수 있습니다.

DisplayWindowSettings#setShouldShowSystemDecorsLocked()를 사용하여 특정 디스플레이에 대한 시스템 장식 지원을 추가하거나 /data/system/display_settings.xml에서 기본값을 제공하세요. 관련 예시는 디스플레이 창 설정을 참조하세요.

구현

DisplayWindowSettings#setShouldShowSystemDecorsLocked()는 테스트를 위한 WindowManager#setShouldShowSystemDecors()에서도 노출됩니다. 시스템 장식을 사용 설정하기 위한 의도로 이 메서드를 트리거해도 이전에 누락되었던 장식 창이 추가되거나 이전에 존재했던 장식 창이 제거되지는 않습니다. 대부분의 경우 시스템 장식 지원과 관련된 변경사항은 기기 재부팅 이후에만 온전히 적용됩니다.

WindowManager 코드베이스의 시스템 장식 지원 확인 과정은 보통 DisplayContent#supportsSystemDecorations()를 거치는 반면 외부 서비스(예: 탐색 메뉴를 표시해야 할지 확인하기 위한 시스템 UI) 확인 과정에는 WindowManager#shouldShowSystemDecors()가 사용됩니다. 이 설정의 제어 대상을 이해하려면 이러한 메서드의 호출 지점을 살펴보세요.

시스템 UI 장식 창

Android 10에는 탐색 메뉴의 시스템 장식 창 지원만 추가되었습니다. 이는 탐색 메뉴가 활동 및 앱 간 이동에 중요하기 때문입니다. 기본적으로 탐색 메뉴에는 뒤로 및 홈 어포던스가 표시됩니다. 이는 타겟 디스플레이가 시스템 장식을 지원하는 경우에만 포함됩니다(DisplayWindowSettings 참조).

상태 표시줄은 좀 더 복잡한 시스템 창입니다. 여기에는 알림 창, 빠른 설정과 잠금 화면도 포함되기 때문입니다. Android 10에서는 상태 표시줄이 보조 디스플레이에서 지원되지 않습니다. 따라서 알림, 설정 및 전체 키가드를 기본 디스플레이에서만 사용할 수 있습니다.

개요/최근 사용 시스템 창은 보조 화면에서 지원되지 않습니다. Android 10에서는 AOSP가 기본 디스플레이에만 최근 사용을 표시하고 모든 디스플레이의 활동을 포함합니다. 최근 사용에서 실행되는 경우 기본적으로 보조 디스플레이에 있던 활동이 디스플레이 전면으로 이동됩니다. 이러한 접근 방식에는 몇 가지 알려진 문제가 있습니다(예: 앱이 다른 화면에 표시되는 즉시 업데이트되지 않는 문제).

구현

추가 시스템 UI 기능을 구현하려면 기기 제조업체가 디스플레이 추가/제거를 리슨하고 적절한 콘텐츠를 제시하는 단일 시스템 UI 구성요소를 사용해야 합니다.

다중 디스플레이(MD)를 지원하는 시스템 UI 구성요소는 다음과 같은 사례를 처리해야 합니다.

  • 시작 시 다중 디스플레이 초기화
  • 런타임 시에 추가된 디스플레이
  • 런타임 시에 제거된 디스플레이

시스템 UI는 WindowManager보다 먼저 디스플레이 추가를 감지할 경우 경합 조건을 생성합니다. DisplayManager .DisplayListener 이벤트를 구독하는 대신 디스플레이가 추가될 때 WindowManager의 맞춤 콜백을 시스템 UI에 구현하면 이를 피할 수 있습니다. 참조 구현은 CommandQueue.Callbacks#onDisplayReady(탐색 메뉴 지원) 및 WallpaperManagerInternal#onDisplayReady(배경화면)를 참조하세요.

또한 Android 10은 다음과 같은 업데이트를 제공합니다.

  • NavigationBarController 클래스는 탐색 메뉴와 관련된 모든 기능을 제어합니다.
  • 맞춤설정된 탐색 메뉴의 예는 CarStatusBar를 참조하세요.
  • TYPE_NAVIGATION_BAR는 더 이상 단일 인스턴스로 제한되지 않으며 디스플레이별로 사용할 수 있습니다.
  • IWindowManager#hasNavigationBar()는 시스템 UI의 displayId 매개변수만 포함하도록 업데이트됩니다.

런처

Android 10에서는 시스템 장식을 지원하도록 구성된 각 디스플레이에 WindowConfiguration#ACTIVITY_TYPE_HOME 유형을 포함하는 런처 활동을 위한 전용 홈 스택이 기본으로 포함됩니다. 각 디스플레이는 런처 활동의 개별 인스턴스를 사용합니다.

그림 1. platform/development/samples/MultiDisplay의 다중 디스플레이 런처 예시

대부분의 기존 런처는 여러 인스턴스를 지원하지 않으며 대형 화면 크기에 맞게 최적화되지 않습니다. 또한 보조/외부 디스플레이에서는 다른 종류의 환경이 예상되는 경우가 많습니다. Android 10에서는 보조 화면 전용 활동 제공을 위해 인텐트 필터에 SECONDARY_HOME 카테고리가 도입되었습니다. 이 활동의 인스턴스는 시스템 장식을 지원하는 모든 디스플레이에 사용됩니다(디스플레이당 하나).

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

활동은 여러 인스턴스를 막지 않는 실행 모드를 보유해야 하며, 다양한 화면 크기에 맞게 조정되어야 합니다. 실행 모드는 singleInstance 또는 singleTask일 수 없습니다.

구현

Android 10에서는 RootActivityContainer#startHomeOnDisplay()가 홈 화면이 실행된 디스플레이에 따라 원하는 구성요소와 인텐트를 자동으로 선택합니다. RootActivityContainer#resolveSecondaryHomeActivity()에는 현재 선택된 런처에 따라 런처 활동 구성요소를 조회하기 위한 로직이 포함되며, 필요한 경우 시스템 기본값을 사용할 수 있습니다(ActivityTaskManagerService#getSecondaryHomeIntent() 참조).

보안 제한사항

보조 디스플레이의 활동에 적용되는 제한사항 외에도, 악성 앱이 시스템 장식이 사용 설정된 가상 디스플레이를 생성하여 노출 영역에서 사용자의 민감한 정보를 읽는 것을 방지하도록 런처가 시스템 소유의 가상 디스플레이에만 표시됩니다. 런처는 시스템 외의 가상 디스플레이에 콘텐츠를 표시하지 않습니다.

배경화면

Android 10 이상에서는 배경화면이 보조 디스플레이에서 지원됩니다.

그림 2. 내부(위) 및 외부(아래) 디스플레이의 라이브 배경화면

개발자는 WallpaperInfo XML 정의에서 android:supportsMultipleDisplays="true"를 제공하여 배경화면 기능 지원을 선언할 수 있습니다. 또한 배경화면 개발자는 WallpaperService.Engine#getDisplayContext()에서 디스플레이 컨텍스트를 사용하여 애셋을 로드해야 합니다.

프레임워크는 디스플레이당 1개의 WallpaperService.Engine 인스턴스를 생성하므로 각 엔진이 자체 노출 영역과 디스플레이 컨텍스트를 보유합니다. 개발자는 각 엔진이 VSYNC에 따라 상이한 프레임 속도에서 독립적으로 그려질 수 있는지 확인해야 합니다.

개별 화면의 배경화면 선택

Android 10은 개별 화면의 배경화면 선택을 위한 직접적인 플랫폼 지원 기능을 제공하지 않습니다. 이를 위해서는 디스플레이별 배경화면 설정 지속을 위한 안정적인 디스플레이 식별자가 필요합니다. Display#getDisplayId()는 동적이므로 재부팅 이후에 실제 디스플레이가 동일한 ID를 취한다는 보장이 없습니다.

하지만 Android 10에는 실제 디스플레이의 안정적인 식별자가 포함되어 있고 향후의 온전한 구현에 사용할 수 있는 DisplayInfo.mAddress가 추가되었습니다. 안타깝게도 Android 10에 로직을 구현하기에는 너무 늦었습니다. 권장 해결 방법은 다음과 같습니다.

  1. WallpaperManager API를 사용하여 배경화면을 설정합니다.
  2. WallpaperManagerContext 객체에서 획득되며, 각 Context 객체에는 해당하는 디스플레이(Context#getDisplay()/getDisplayId())에 대한 정보가 포함됩니다. 따라서 새 메서드를 추가하지 않고도WallpaperManager 인스턴스에서 displayId를 획득할 수 있습니다.
  3. 프레임워크 측에서는 Context 객체에서 획득한 displayId를 사용하고, 이를 정적 식별자(실제 디스플레이의 포트 등)에 매핑합니다. 선택한 배경화면을 지속하려면 정적 식별자를 사용하세요.

이 해결 방법에서는 배경화면 선택기에 기존 구현을 사용합니다. 특정 디스플레이에서 열렸고 적절한 컨텍스트를 사용하는 경우에는 배경화면 설정을 호출할 때 시스템에서 자동으로 디스플레이를 식별할 수 있습니다.

현재 디스플레이 외의 다른 디스플레이에 배경화면을 설정해야 하는 경우에는 타겟 디스플레이(Context#createDisplayContext)의 새 Context 객체를 생성하고 해당 디스플레이에서 WallpaperManager 인스턴스를 획득합니다.

보안 제한사항

시스템은 자체 소유가 아닌 가상 디스플레이에 배경화면을 표시하지 않습니다. 이는 악성 앱이 시스템 장식 지원이 사용 설정된 가상 디스플레이를 생성한 후 노출 영역에서 사용자의 민감한 정보(개인 사진 등)를 읽을 수도 있다는 보안 우려에서 비롯됩니다.

구현

Android 10에서는 IWallpaperConnection#attachEngine()IWallpaperService#attach() 인터페이스에서 displayId 매개변수를 수락하여 디스플레이별 연결을 생성합니다. WallpaperManagerService.DisplayConnector는 디스플레이별 배경화면 엔진 및 연결을 캡슐화합니다. WindowManager에서는 모든 디스플레이를 위한 단일 WallpaperController 대신 각 DisplayContent 객체에 대해 배경화면 컨트롤러가 생성됩니다.

일부 공개 WallpaperManager 메서드 구현(예: WallpaperManager#getDesiredMinimumWidth())은 해당하는 디스플레이에 대해 계산하고 정보를 제공하도록 업데이트되었습니다. WallpaperInfo#supportsMultipleDisplays() 및 해당하는 리소스 속성이 추가되었으므로 해당 앱 개발자가 어떤 배경화면을 여러 화면에 사용할 수 있는지 보고할 수 있습니다.

기본 디스플레이에 표시된 배경화면 서비스가 여러 디스플레이를 지원하지 않는 경우 시스템은 보조 디스플레이에 기본 배경화면을 표시합니다.

그림 3. 보조 디스플레이에 대한 배경화면 대체 로직