안드로이드 키오스크 앱 개발 (1/3)에서 안드로이드 앱을 키오스크 모드로 동작시키는 방법을 살펴봤다. 여기에서는 앞에서 살펴봤던 예제 프로젝트 앱에 관리자(Admin) 권한을 부여하는 방법과 소유자(Owner) 권한을 부여해서 삭제할 수 없고(예외로 공장 초기화로 삭제할 수 있음), 안드로이드 기기의 기능을 제한하는 방법을 살펴보자.
1. 디바이스 관리자 앱(Device Admin Apps)으로 등록
안드로이드 앱에서 기기를 관리(엔터프라이즈용 기능)할 수 있는 권한을 앱에 부여할 수 있고, 이 권한을 얻으면, DevicePolicyManager(https://developer.android.com/reference/android/app/admin/DevicePolicyManager) API를 사용해서 기기의 기능을 제한할 수 있다. 테스트 하는 기기에 등록된 앱을 살펴보면 아래와 같다.
– 디바이스 관리자 앱들
1.1 DeviceAdminReceiver 추가
앱을 기기 관리자 앱으로 등록하거나 삭제되는 경우에 시그널을 받을 수 있는 브로드캐스트 리시버를 추가한다.
public class KioskDeviceAdminReceiver extends DeviceAdminReceiver { static final String TAG = "SIMPLE_KIOSK"; @Override public void onReceive(Context context, Intent intent) { Log.i(TAG, "onReceive: " + intent.getAction()); } @Override public void onEnabled(Context context, Intent intent) { Toast.makeText(context, "Device admin enabled", Toast.LENGTH_SHORT).show(); } @Override public void onDisabled(Context context, Intent intent) { Toast.makeText(context, "Device admin disabled", Toast.LENGTH_SHORT).show(); } @Override public void onLockTaskModeEntering(Context context, Intent intent, String pkg) { Log.i(TAG, "onLockTaskModeEntering: " + pkg); } }
– KioskDeviceAdminReceiver.java 파일
이 파일은 앱이 기기 관리자로 등록/해제 등의 이벤트를 받는 관리자 브로드캐스트 리시버이다. 그리고, 관리자 기능과 관련된 시그널을 받기 위해서 아래와 같이 위의 리시버를 AndroidManifest.xml의 의 하위 요소로 추가한다.
<receiver android:name=".KioskDeviceAdminReceiver" android:description="@string/app_name" android:label="@string/app_name" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="android.app.device_admin" android:resource="@xml/device_admin_receiver" tools:ignore="DeviceAdmin" /> <intent-filter> <action android:name="android.intent.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver>
– AndroidManifest.xml에 KioskDeviceAdminReceiver 등록
KioskDeviceAdminReceiver도 다른 브로드캐스트 리시버와 동일하게 AndroidManifest.xml에 등록을 한다.
1.2 device_admin_receiver.xml 추가
이 파일을 /src/main/res/xml/ 폴더에 추가한다. 이 파일에는 앱이 관리자 권한을 얻게 되면, 사용할 정책을 정의한다. 개별 정책에 대한 내용은 DeviceAdminInfo 클래스 API에 정의되어 있다.
<?xml version="1.0" encoding="utf-8"?> <device-admin> <uses-policies> <limit-password /> <watch-login /> <reset-password /> <force-lock /> <wipe-data /> <expire-password /> <encrypted-storage /> <disable-camera /> </uses-policies> </device-admin>
– 데모 프로젝트의 device_admin_receiver.xml 파일
위 정책을 몇 개만 살펴보면, wipe-data는 로컬 스토리지를 포맷할 수 있는 권한을 가지게 되고, encrypted-storage는 스토리지를 암호화할 수 있고, disable-camera는 카메라 기능을 비활성화할 수 있다.
1.3 기기 관리자 권한 추가
기기에서 앱이 관리자 권한을 가지기 위해서는 일반 앱의 퍼미션 요청과 동일한 형태로 관리자 권한을 요청해야 한다.
private void enableAdmin() { if (isDeviceAdminApp()) { return; } Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminComponentName); // Start the add device admin activity startActivityForResult(intent, DEVICE_ADMIN_ADD_REQUEST); }
– MainActivity의 기기 관리자 권한 요청 코드
위 코드로 기기 관리자 권한을 요청할 수 있다.
– 기기 관리자 앱 활성화 화면
위 화면은 앱이 가지는 관리자 정책과 활성화를 요청하는 화면이고, 요청을 수락하면 앱은 관리자 권한을 가지고 동작한다.
// 일반 앱의 경우에는 isAdminApp, isOwnerApp의 값이 false이다. 2020-05-23 12:17:06.265 4712-4712/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isAdminApp: false 2020-05-23 12:17:06.266 4712-4712/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isOwnerApp: false // 관리자 권한을 가지게 되면 isAdminApp의 값이 true인 것을 볼 수 있다. 2020-05-23 12:19:42.083 4976-4976/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isAdminApp: true 2020-05-23 12:19:42.084 4976-4976/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isOwnerApp: false
2. 앱에 소유자 앱 권한 부여
이제 앱에 소유자 권한을 부여하는 방법을 살펴보자. 여기에서는 소유자 앱의 권한을 주기 위해서 adb shell 명령을 사용할 것이다. 소유자 앱으로 테스트 하다가 문제가 생겨서 기기를 사용할 수 없게 되거나 공장 초기화(Factory Reset)를 해야 하는 경우가 발생할 수 있다. 그래서 아래와 같이 Applicaion 요소에 android:testOnly=”true” 를 추가해서 기기의 잠금으로 인한 문제를 방지한다.
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:testOnly="true" android:theme="@style/AppTheme">
– AndroidManifest.xml의 application 요소에 android:testOnly=”true”를 추가한 예
아래는 앱에 소유자 권한을 부여하고, 관리자 앱을 비활성화하는 방법이다.
# 소유자 앱 활성화 adb shell dpm set-device-owner net.sjava.examples.kiosk/.KioskDeviceAdminReceiver # 관리자 앱 비활성화 adb shell dpm remove-active-admin net.sjava.examples.kiosk/.KioskDeviceAdminReceiver // 소유자 앱 활성화 뒤 2020-05-23 12:34:14.450 4976-4976/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isAdminApp: true 2020-05-23 12:34:14.451 4976-4976/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isOwnerApp: true // 관리자 앱 비활성화 뒤 2020-05-23 12:36:26.200 4976-4976/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isAdminApp: false 2020-05-23 12:36:26.201 4976-4976/net.sjava.examples.kiosk I/SIMPLE_KIOSK: isOwnerApp: false
만약, adb shell dpm set-device-owner 명령에 아래와 같은 에러를 보게 된다면, 설정(Settings) > 계정(Account)에 등록된 계정을 삭제하고 다시 실행하면 된다.
adb shell dpm set-device-owner net.sjava.examples.kiosk/.KioskDeviceAdminReceiver java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device at android.os.Parcel.createException(Parcel.java:2079) at android.os.Parcel.readException(Parcel.java:2039) at android.os.Parcel.readException(Parcel.java:1987) at android.app.admin.IDevicePolicyManager$Stub$Proxy.setDeviceOwner(IDevicePolicyManager.java:8392) at com.android.commands.dpm.Dpm.runSetDeviceOwner(Dpm.java:203) at com.android.commands.dpm.Dpm.onRun(Dpm.java:115) at com.android.internal.os.BaseCommand.run(BaseCommand.java:56) at com.android.commands.dpm.Dpm.main(Dpm.java:41) at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:338) Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.devicepolicy.DevicePolicyManagerService.enforceCanSetDeviceOwnerLocked(DevicePolicyManagerService.java:8647) at com.android.server.devicepolicy.DevicePolicyManagerService.setDeviceOwner(DevicePolicyManagerService.java:7809) at android.app.admin.IDevicePolicyManager$Stub.onTransact(IDevicePolicyManager.java:3270) at android.os.Binder.execTransactInternal(Binder.java:1021) at android.os.Binder.execTransact(Binder.java:994)
– adb shell dpm set-device-owner 에러 로그
아래 화면은 터미널에서 앱에 소유자 권한을 추가하고, 어드민 권한을 제거한 결과를 보여주는 화면이다.
– 소유자 권한 추가 및 해제 화면
3. 기능 제한
여기에서는 앱이 소유자 권한을 가지고 안드로이드 기기에서 기능을 제한하는 방법에 대해서 살펴보자. 간단하게 카메라 기능, 사용자의 일부 기능 제한, 그리고 앱 사용을 제한하는 방법에 대해서 살펴보자.
3.1 카메라 기능 제한
아래 코드로 기기 카메라 사용을 제한할 수 있다.
private void disableCamera(boolean disabled) { mDevicePolicyManager.setCameraDisabled(mAdminComponentName, disabled); }
– 카메라 기능 제한
이 코드로 기기의 카메라 기능을 제한할 수 있다. 그리고, 사용자의 기능 제한은 3.2에서 확인할 수 있고, 많은 기능을 제한할 수 있는 것을 알 수 있다.
3.2 사용자 기능 제한
아래 코드로 사용자의 기능을 제한할 수 있다.
static final ArrayList mUserRestrictions = new ArrayList<>( Arrays.asList( UserManager.DISALLOW_SMS, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, //UserManager.DISALLOW_USB_FILE_TRANSFER, UserManager.DISALLOW_BLUETOOTH ) ); private void setUserRestrictions(boolean restricted) { for (String restriction : mUserRestrictions) { if (restricted) { mDevicePolicyManager.addUserRestriction(mAdminComponentName, restriction); } else { mDevicePolicyManager.clearUserRestriction(mAdminComponentName, restriction); } } }
– 사용자 기능 제한
위 mUserRestrictions은 제한될 사용자의 기능을 정의했다. 코드에서 알 수 있듯이, UserManager에 제한할 기능이 정의되어 있고 필요한 기능은 확인해보면서 추가하면 된다.
3.3 앱 사용 제한
아래 코드로 설치된 앱 사용을 제한할 수 있다.
static final String[] mSuspendedPackageNames = {"com.twitter.android", "com.facebook.katana", "com.google.android.apps.nbu.files" }; private void setPackagesSuspended(String[] packageNames, boolean suspended) { if(Build.VERSION.SDK_INT >= 24) { mDevicePolicyManager.setPackagesSuspended(mAdminComponentName, packageNames, suspended); } }
– 앱 사용 제한
위 코드에서 mSuspendedPackageNames는 사용을 제한할 앱 목록을 가지고 있고, DevicePolicyManager의 setPackagesSuspended() 메서드를 호출해서 앱 사용을 제한할 수 있다. 더불어 DevicePolicyManager의 setApplicationHidden() 메서드로 앱 자체를 숨겨버릴 수도 있다.
이 프로젝트의 소스는 https://github.com/mcsong/SimpleKioskDemo에서 확인할 수 있다.
– 소유자 권한을 가진 앱으로 몇 개의 앱과 카메라 기능을 비활성화한 예제
위에서 살펴본 기능 외에도 많은 기능을 제한할 수 있다. 소유자 권한으로 안드로이드 API가 제공하는 기능 제한은 DevicePolicyManager 클래스에서 확인할 수 있다.
지금까지 안드로이드 앱을 키오스크 모드로 동작하고, 소유자 권한을 획득해서 안드로이드 기기를 온전히 키오스크 기기나 특정 용도에 맞게 사용할 수 있는 방법을 살펴봤다. 안드로이드 기기를 관리하는 방법으로 제조사에서 제공하는 MDM 솔루션(삼성 Knox나 LG Gate)을 사용할 수 있는데, MDM 솔루션을 사용하면 위의 기능에 더불어 제조사에서 자신들의 기기만을 위해서 추가한 기능도 관리할 수 있어서 조금 더 스트릭하게 관리할 수 있다. 개인적으로 몇 개의 MDM 솔루션으로 작업을 해 본 결과, 지금까지는 삼성 Knox가 가장 편리하고 기능적으로도 우수하다.
Reference
– https://developer.android.com/guide/topics/admin/device-admin
– https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode