작업 잠금 모드

이 개발자 가이드에서는 전용 기기를 단일 앱 또는 앱 세트로 잠그는 방법을 설명합니다. 엔터프라이즈 모바일 관리 (EMM) 개발자 또는 솔루션 통합자는 이 가이드를 참고하여 솔루션에 잠금 작업 모드를 추가하세요.

개요

Android는 작업 잠금 모드라는 몰입형, 키오스크와 유사한 방식으로 작업을 실행할 수 있습니다. 앱 컬렉션을 표시하는 키오스크 애플리케이션 또는 런처를 개발하고 있다면 작업 잠금 모드를 사용할 수 있습니다. 시스템이 잠금 작업 모드로 실행되면 기기 사용자는 일반적으로 알림을 보거나 허용 목록에 없는 앱에 액세스하거나 홈 화면으로 돌아가는 작업을 할 수 없게 됩니다 (홈 화면이 허용 목록에 등록된 경우는 제외).

시스템이 작업 잠금 모드일 때는 기기 정책 컨트롤러 (DPC)가 허용 목록에 추가한 앱만 실행할 수 있습니다. 기기 사용자가 항상 작업 잠금 모드를 종료할 수 없으므로 앱이 허용 목록에 포함됩니다.

잠금 작업 모드를 위해 허용 목록에 추가된 앱과 허용 목록 DPC를 결합하는 방법은 해결하려는 문제에 따라 다릅니다. 다음은 몇 가지 예입니다.

  • 키오스크 (콘텐츠 표시용)와 미니 DPC(작업 잠금 모드를 위해 허용 목록에 포함)를 결합한 단일 앱 패키지입니다.
  • 엔터프라이즈 모바일 관리 솔루션의 일부인 DPC로, 고객의 모바일 앱을 잠금 작업 모드에서 실행합니다.

지원 대상

시스템은 Android 5.0 이상에서 작업 잠금 모드로 실행할 수 있습니다. 표 1은 사용자별 허용 목록 추가를 지원하는 Android 버전을 보여줍니다.

표 1. DPC 관리 모드를 위한 Android 버전 지원
Android 버전 DPC는 Notes
Android 5.0(API 수준 21) 이상 완전히 관리되는 기기
Android 8.0 (API 수준 26) 이상 연결된 보조 사용자 보조 사용자는 기본 사용자와 연결되어 있어야 합니다. 여러 사용자 개요를 참조하세요.
Android 9.0 (API 수준 28) 이상 보조 사용자

Android 9.0 이상에서 DPC는 모든 앱의 활동을 잠금 작업 모드로 시작할 수 있습니다. 이전 버전에서는 앱이 작업 잠금 모드에서 자체 활동을 시작할 수 있도록 지원해야 합니다.

허용 목록 앱

DPC는 작업 잠금 모드에서 앱을 사용하려면 먼저 앱을 허용 목록에 추가해야 합니다. 다음 샘플과 같이 DevicePolicyManager.setLockTaskPackages()를 호출하여 작업 잠금 모드를 허용 목록에 추가합니다.

Kotlin

// Allowlist two apps.
private val KIOSK_PACKAGE = "com.example.kiosk"
private val PLAYER_PACKAGE = "com.example.player"
private val APP_PACKAGES = arrayOf(KIOSK_PACKAGE, PLAYER_PACKAGE)

// ...

val context = context
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE)
        as DevicePolicyManager
val adminName = getComponentName(context)
dpm.setLockTaskPackages(adminName, APP_PACKAGES)

Java

// Allowlist two apps.
private static final String KIOSK_PACKAGE = "com.example.kiosk";
private static final String PLAYER_PACKAGE = "com.example.player";
private static final String[] APP_PACKAGES = {KIOSK_PACKAGE, PLAYER_PACKAGE};

// ...

Context context = getContext();
DevicePolicyManager dpm =
    (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminName = getComponentName(context);
dpm.setLockTaskPackages(adminName, APP_PACKAGES);

이전에 잠금 작업 모드에서 허용 목록에 추가된 앱을 확인하려면 DPC에서 DevicePolicyManager.getLockTaskPackages()를 호출하면 됩니다. 다른 앱은 DevicePolicyManager.isLockTaskPermitted()를 호출하여 앱 패키지가 작업 잠금 모드를 지원하는지 확인할 수 있습니다.

작업 잠금 모드 시작

Android 9.0 (API 수준 28) 이상에서는 작업 잠금 모드에서 다른 앱의 활동을 시작할 수 있습니다. 활동이 이미 포그라운드 또는 백그라운드에서 실행 중인 경우 활동을 다시 실행해야 합니다. 활동을 시작할 때 ActivityOptions.setLockTaskEnabled()를 호출하고 이러한 옵션을 제공합니다. 다음 스니펫은 이 작업을 실행하는 한 가지 방법을 보여줍니다.

Kotlin

// Set an option to turn on lock task mode when starting the activity.
val options = ActivityOptions.makeBasic()
options.setLockTaskEnabled(true)

// Start our kiosk app's main activity with our lock task mode option.
val packageManager = context.packageManager
val launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE)
if (launchIntent != null) {
    context.startActivity(launchIntent, options.toBundle())
}

Java

// Set an option to turn on lock task mode when starting the activity.
ActivityOptions options = ActivityOptions.makeBasic();
options.setLockTaskEnabled(true);

// Start our kiosk app's main activity with our lock task mode option.
PackageManager packageManager = context.getPackageManager();
Intent launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE);
if (launchIntent != null) {
  context.startActivity(launchIntent, options.toBundle());
}

Android 9.0 이전의 버전에서는 앱이 Activity.startLockTask()를 호출하여 잠금 작업 모드에서 자체 활동을 시작합니다. 이 메서드를 호출하려면 활동이 포그라운드에서 실행 중이어야 하므로 (활동 수명 주기 개념 참고) Activity 또는 FragmentonResume() 메서드에서 호출하는 것이 좋습니다. startLockTask()를 호출하는 방법은 다음과 같습니다.

Kotlin

// In our Fragment subclass.
override fun onResume() {
    super.onResume()
    // First, confirm that this package is allowlisted to run in lock task mode.
    if (dpm.isLockTaskPermitted(context.packageName)) {
        activity.startLockTask()
    } else {
        // Because the package isn't allowlisted, calling startLockTask() here
        // would put the activity into screen pinning mode.
    }
}

Java

// In our Fragment subclass.
@Override
public void onResume() {
  super.onResume();

  // First, confirm that this package is allowlisted to run in lock task mode.
  if (dpm.isLockTaskPermitted(context.getPackageName())) {
    getActivity().startLockTask();
  } else {
    // Because the package isn't allowlisted, calling startLockTask() here
    // would put the activity into screen pinning mode.
  }
}

기기가 잠겨 있을 때 작업 잠금 모드를 시작하지 마세요. 사용자가 기기를 잠금 해제하지 못할 수 있습니다. KeyguardManager 메서드를 호출하여 기기가 잠겨 있는지 확인하고 Activity 수명 주기 콜백 (예: 잠금 해제 후 호출되는 onResume())을 사용하여 잠금 작업 모드를 시작할 수 있습니다.

작업 잠금 모드의 앱은 활동이 새 작업을 시작하지 않는 한 새 활동을 시작할 수 있습니다(허용 목록에 있는 앱을 실행하는 작업 제외). 작업이 활동과 어떻게 관련되는지 알아보려면 작업 및 백 스택 이해 가이드를 읽어보세요.

또는 시스템이 작업 잠금 모드에서 실행될 때 활동이 어떻게 동작해야 하는지 앱 매니페스트 파일에서 선언할 수 있습니다. 시스템이 작업 잠금 모드에서 활동을 자동으로 실행하도록 하려면 다음 예와 같이 android:lockTaskMode 속성을 if_whitelisted로 설정합니다.

<activity
    android:name=".MainActivity"
    android:lockTaskMode="if_whitelisted">
    <!-- ... -->
</activity>

앱 매니페스트 파일에서 옵션을 선언하는 방법에 관한 자세한 내용은 lockTaskMode 참조를 참고하세요.

작업 잠금 모드 중지

DPC는 허용 목록에서 앱 패키지를 삭제하여 작업 잠금 모드를 원격으로 중지할 수 있습니다. Android 6.0 (API 수준 23) 이상에서 DevicePolicyManager.setLockTaskPackages()를 호출하고 허용 목록 배열에서 패키지 이름을 생략합니다. 허용 목록을 업데이트하면 앱이 스택의 이전 작업으로 돌아갑니다.

활동이 이전에 startLockTask()를 호출한 경우 이 활동은 Activity.stopLockTask()를 호출하여 작업 잠금 모드를 중지할 수 있습니다. 이 메서드는 작업 잠금 모드를 시작한 활동에서만 작동합니다.

수명 주기 콜백

DPC는 동일한 사용자로 실행 중인 앱이 잠금 작업 모드에 진입하고 종료하는 시점을 파악하는 것이 유용할 수 있습니다. 콜백을 수신하려면 DPC의 DeviceAdminReceiver 서브클래스에서 다음 콜백 메서드를 재정의합니다.

onLockTaskModeEntering()
앱이 작업 잠금 모드로 전환된 후 호출됩니다. pkg 인수에서 앱의 패키지 이름을 가져올 수 있습니다.
onLockTaskModeExiting()
앱이 작업 잠금 모드를 종료한 후에 호출됩니다. 이 콜백은 앱에 관한 정보를 수신하지 않습니다.

다른 앱을 작업 잠금 모드로 실행하는 경우 자체 앱에서 실행 상태를 추적해야 합니다. 현재 앱이 작업 잠금 모드에서 실행 중인지 확인하려면 다음 예와 같이 ActivityManager의 메서드를 사용합니다.

Kotlin

// Check if this app is in lock task mode. Screen pinning doesn't count.
var isLockTaskModeRunning = false

val activityManager = context
        .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    isLockTaskModeRunning =
            activityManager.lockTaskModeState ==
            ActivityManager.LOCK_TASK_MODE_LOCKED
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Deprecated in API level 23.
    isLockTaskModeRunning = activityManager.isInLockTaskMode
}

if (isLockTaskModeRunning) {
    // Show the exit button ...
}

Java

// Check if this app is in lock task mode. Screen pinning doesn't count.
boolean isLockTaskModeRunning = false;

ActivityManager activityManager = (ActivityManager)
    getContext().getSystemService(Context.ACTIVITY_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  isLockTaskModeRunning = activityManager.getLockTaskModeState()
      == ActivityManager.LOCK_TASK_MODE_LOCKED;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  // Deprecated in API level 23.
  isLockTaskModeRunning = activityManager.isInLockTaskMode();
}

if (isLockTaskModeRunning) {
  // Show the exit button ...
}

UI 맞춤설정

앱이 잠금 작업 모드에서 실행되면 시스템 사용자 인터페이스 (UI)가 다음과 같이 변경됩니다.

  • 상태 표시줄이 비어 있고 알림과 시스템 정보가 숨겨져 있습니다.
  • 홈 버튼과 개요 버튼이 숨겨져 있습니다.
  • 다른 앱은 새 활동을 실행할 수 없습니다.
  • 잠금 화면 (설정된 경우)이 사용 중지됩니다.

Android 9.0 이상에서는 작업 잠금 모드가 사용 설정된 경우 DPC가 기기에서 특정 시스템 UI 기능을 사용 설정할 수 있습니다. 이는 맞춤 런처를 만드는 개발자에게 유용합니다. 다음 스니펫과 같이 DevicePolicyManager.setLockTaskFeatures()를 호출합니다.

Kotlin

// Enable the Home and Overview buttons so that our custom launcher can respond
// using our custom activities. Implicitly disables all other features.
dpm.setLockTaskFeatures(
        adminName,
        DevicePolicyManager.LOCK_TASK_FEATURE_HOME or
              DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW)

Java

// Enable the Home and Overview buttons so that our custom launcher can respond
// using our custom activities. Implicitly disables all other features.
dpm.setLockTaskFeatures(adminName,
    DevicePolicyManager.LOCK_TASK_FEATURE_HOME |
          DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW);

시스템은 flags 인수에 포함하지 않는 기능을 사용 중지합니다. 사용 설정된 UI 기능은 잠금 작업 모드로 실행되는 사이에 유지됩니다. 기기가 이미 작업 잠금 모드인 경우 작업 잠금 기능의 모든 변경사항은 즉시 표시됩니다. 표 2에서는 맞춤설정할 수 있는 UI 기능을 설명합니다.

표 2. 잠금 작업 모드에서 맞춤설정 가능한 시스템 UI 기능
시스템 UI 기능 설명
LOCK_TASK_FEATURE_HOME 홈 버튼을 표시합니다. 맞춤 런처 사용 설정 - 사용 설정된 홈 버튼을 탭해도 기본 Android 런처를 허용 목록에 추가하지 않는 한 아무 작업도 실행되지 않습니다.
LOCK_TASK_FEATURE_OVERVIEW 개요 버튼을 표시합니다. 이 버튼을 탭하면 최근 화면이 열립니다. 이 버튼을 사용 설정하는 경우 홈 버튼도 사용 설정해야 합니다.
LOCK_TASK_FEATURE_GLOBAL_ACTIONS 전원 버튼을 길게 누를 때 표시되는 전역 작업 대화상자를 사용 설정합니다. setLockTaskFeatures()가 호출되지 않았을 때 사용 설정되는 유일한 기능입니다. 일반적으로 이 대화상자를 사용 중지하면 사용자가 기기의 전원을 끌 수 없습니다.
LOCK_TASK_FEATURE_NOTIFICATIONS 모든 앱에서 알림을 사용 설정합니다. 상태 표시줄의 알림 아이콘, 헤드업 알림, 확장 가능한 알림 창에 표시됩니다. 이 버튼을 사용 설정하는 경우 홈 버튼도 사용 설정해야 합니다. 알림 작업 및 새 패널을 여는 버튼을 탭하면 작업 잠금 모드에서 작동하지 않습니다.
LOCK_TASK_FEATURE_SYSTEM_INFO 연결, 배터리, 소리, 진동 옵션과 같은 표시기가 포함된 상태 표시줄의 시스템 정보 영역을 사용 설정합니다.
LOCK_TASK_FEATURE_KEYGUARD 기기에 설정되었을 수 있는 모든 잠금 화면을 사용 설정합니다. 일반적으로 정보 키오스크 또는 디지털 사이니지와 같이 일반 사용자가 사용하는 기기에는 적합하지 않습니다.
LOCK_TASK_FEATURE_NONE 위에 나열된 모든 시스템 UI 기능을 사용 중지합니다.

DPC는 작업 잠금 모드가 사용 설정된 경우 DevicePolicyManager.getLockTaskFeatures()를 호출하여 기기에서 사용 가능한 기능 목록을 가져올 수 있습니다. 기기가 잠금 작업 모드를 종료하면 사용자 인터페이스가 기존 기기 정책에서 요구하는 상태로 돌아갑니다.

창 및 오버레이 차단

앱이 잠금 작업 모드로 실행되면 다른 앱과 백그라운드 서비스가 작업 잠금 모드에서 Android가 앱 앞에 표시하는 새 창을 만들 수 있습니다. 앱과 서비스에서는 이러한 창을 만들어 기기 사용자에게 토스트 메시지, 대화상자, 오버레이를 표시합니다. DPC는 DISALLOW_CREATE_WINDOWS 사용자 제한을 추가하여 이를 방지할 수 있습니다. 다음 예는 onLockTaskModeEntering() 콜백에서 이를 실행하는 방법을 보여줍니다.

Kotlin

// Called just after entering lock task mode.
override fun onLockTaskModeEntering(context: Context, intent: Intent) {
    val dpm = getManager(context)
    val admin = getWho(context)

    dpm.addUserRestriction(admin, UserManager.DISALLOW_CREATE_WINDOWS)
}

Java

// Called just after entering lock task mode.
public void onLockTaskModeEntering(Context context, Intent intent) {
  DevicePolicyManager dpm = getManager(context);
  ComponentName admin = getWho(context);

  dpm.addUserRestriction(admin, UserManager.DISALLOW_CREATE_WINDOWS);
}

DPC는 기기가 잠금 작업 모드를 종료할 때 사용자 제한을 삭제할 수 있습니다.

추가 리소스

전용 기기에 관한 자세한 내용은 다음 문서를 참고하세요.