권장사항

폴더블 및 멀티스크린 기기용 앱

일반적으로 앱은 일부 디스플레이 ID에 종속되는 정적 식별자나 논리에 의존하면 안 됩니다. 대부분의 경우 앱은 크기가 변경되어야 하고 여러 디스플레이에서 작동해야 하며 시스템은 앱을 어디서 찾아야 할지 제어해야 합니다. 폴더블 기기를 위한 새롭고 고유한 환경을 빌드하고 기기가 접혔을 때 외부 화면에 특수 앱을 실행해야 하는 경우를 예로 들 수 있습니다.

이 경우 SystemUI 또는 다른 시스템 구성요소는 접힘을 감지하고 작업을 수행해도 괜찮은지 판단한 다음 타겟 활동을 실행하고 외부 디스플레이 ID를 실행 타겟으로 지정해야 합니다. 앱은 이에 대한 반응으로 이 작업을 감지하거나 어떠한 작업도 수행하면 안 되며, 이어서 구체적인 디스플레이에서 실행 작업을 수행해도 안 됩니다. 즉, 기기에서 작동하는 작업이 다른 기기에서도 작동할 것이라고 가정하면 안 됩니다. 간단히 말해 기기별 코드는 세분화 증가로 이어집니다.

디스플레이에 대한 액세스 제한

기기 구성에서 1개 이상의 디스플레이에 대한 액세스 제한을 요구하는 경우에는 Display#FLAG_PRIVATE 플래그를 사용하여 이러한 디스플레이를 비공개로 지정하는 것이 좋습니다. 이렇게 하면 소유자를 제외한 누구도 디스플레이에 콘텐츠를 추가할 수 없도록 제한됩니다. 소유자를 제외한 누군가가 활동을 실행하거나 창을 추가하려고 시도할 경우 SecurityException이 발생합니다. 시스템에서 디스플레이를 소유한 경우 시스템은 창을 추가하고 활동을 실행할 수 있습니다.

또한 디스플레이에 삽입된 항목은 항상 해당 디스플레이에 액세스할 수 있습니다. 소유자가 디스플레이에서 활동을 실행하면 활동은 이 디스플레이에서 다른 활동을 실행할 수 있습니다. 결과적으로는 소유자가 액세스를 제한하고 신뢰할 수 있는 앱만 허용해야 합니다.

또한 모든 앱에서 사용자에게 표시되지 않는 제한사항을 생성할 수 있으므로 가상 디스플레이에 추가적인 제한사항을 더해야 합니다. 가상 디스플레이에서 시스템을 소유하지 않는 경우에는 allowEmbedded를 포함하는 활동만 허용되며, 호출자는 ACTIVITY_EMBEDDING 권한을 보유해야 합니다.

자세한 내용은 다음을 참조하세요.

  • ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay()
  • ActivityDisplay#isUidPresent()
  • DisplayManagerService#isUidPresentOnDisplay()

활동 실행을 조건부로 제어하려면 모든 활동 실행을 가로채고 시스템 구성요소가 실행에 사용된 매개변수를 수정할 수 있도록 허용하는 LaunchParamsController를 사용해야 합니다. 이는 system_server에서 사용할 수 있습니다.

디스플레이 창 지정 설정 및 시스템 장식

시스템 장식DisplayWindowSettings의 디스플레이마다 구성할 수 있습니다. 기기 구현은 /data/system/display_settings.xml에서 기본 구성을 제공할 수 있습니다.

이 값은 시스템 장식(런처, 배경화면, 탐색 메뉴 및 기타 장식 창)과 IME를 디스플레이에 표시할지 결정합니다. 자세한 내용은 DisplayWindowSettings#shouldShowSystemDecorsLocked()DisplayWindowSettings#shouldShowImeLocked()를 참조하세요.

디스플레이를 식별하려면 고유 ID(기본적으로 DisplayInfo#uniqueId를 사용) 또는 하드웨어 디스플레이의 실제 ID를 사용하세요(DisplayInfo#address 참조).

예를 들어 다음 디스플레이 구성 예시는 시스템 장식 및 IME를 시뮬레이션된 디스플레이에 사용 설정합니다.

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<config identifier="0" />
<display
  name="overlay:1"
  shouldShowSystemDecors="true"
  shouldShowIme="true" />
</display-settings>

위의 예시에서는 이름 속성의 디스플레이 식별을 위해 uniqueId이 사용되었으며, 시뮬레이션된 디스플레이에서는 overlay:1입니다. 내장형 디스플레이의 경우에는 샘플 값이 "local:45354385242535243453"일 수 있습니다. 또 다른 옵션은 하드웨어 포트 정보를 사용하고 identifier="1"DisplayWindowSettings#IDENTIFIER_PORT와 일치하도록 설정한 다음 이름이 "port:<port_id>" 형식을 사용하도록 업데이트하는 것입니다.

<?xmlversion='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<config identifier="1" />
<display
  name="port:12345"
  shouldShowSystemDecors="true"
  shouldShowIme="true" />
</display-settings>

자세한 내용은 정적 디스플레이 식별자를 참고하세요.

자세한 내용은 다음을 참조하세요.

미러링과 호스팅 작업 간에 디스플레이 전환

Android 17 이상에서 DisplayManager는 FLAG_ALLOWS_CONTENT_MODE_SWITCH 플래그를 사용하여 런타임에 디스플레이가 미러링과 호스팅 작업 간에 전환되는지 여부를 제어합니다. 기본적으로 이 플래그는 외부 디스플레이에는 사용 설정되고 다른 모든 디스플레이에는 사용 중지됩니다.

FLAG_ALLOWS_CONTENT_MODE_SWITCH가 있으면 DisplayManager가 android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY 보안 설정을 모니터링하여 작업을 미러링할지 호스팅할지 결정합니다. 이것이 기본 로직이지만 OEM은 이 동작을 맞춤설정할 수 있습니다.

토폴로지 및 포인터 움직임 표시

Android 17 이상에서 디스플레이 토폴로지는 디스플레이의 상대적 위치를 정의하고 마우스 포인터 이동을 토폴로지의 특정 디스플레이 세트로 제한합니다.

WindowManager는 토폴로지에 디스플레이를 포함하기로 결정하고 DisplayManagerInternal.onDisplayBelongToTopologyChanged를 호출합니다. DisplayManager는 디스플레이를 추가하기 전에 DisplayTopologyCoordinator.isDisplayAllowedInTopology를 확인합니다. 기본적으로 로컬 디스플레이에서 작업을 호스팅할 수 있는 경우 시스템에서 작업을 추가합니다.

작업을 호스팅할 수 있는 공개 디스플레이가 여러 개 있는 경우 기본 디스플레이를 포함할지 여부는 DisplayTopologyCoordinator에 전달되는 shouldIncludeDefaultDisplayInTopology 불리언 제공자가 처리합니다. 기본 디스플레이가 작업을 호스팅할 수 있는 유일한 공개 디스플레이인 경우 항상 토폴로지에 있습니다. AOSP에서 불리언 제공자는 기본 디스플레이가 데스크톱 창 모드를 지원하거나 보안 설정 Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGYtrue인 경우에만 true를 반환합니다.

앱은 DisplayManager.getDisplayTopology를 사용하여 현재 토폴로지를 쿼리하고 DisplayManager.registerTopologyListener에 리스너를 등록하여 토폴로지 변경에 반응합니다.