관리 메뉴

드럼치는 프로그래머

[안드로이드] Device Administration 완벽 분석 본문

★─Programing/☆─Android

[안드로이드] Device Administration 완벽 분석

드럼치는한동이 2017. 5. 19. 13:44

제목은 거창하게 완벽 분석이지만, 내용은 Device 관리자에 대한 예제입니다.
예전에 이것에 대해 언급한적이 있었는데요.
http://swlock.blogspot.com/2015/05/android.html

구글의 device 관리자에 대한 설명은 아래 링크에서 확인할 수 있습니다.
http://developer.android.com/intl/ko/guide/topics/admin/device-admin.html

정리하자면, 디바이스 관리자API는 보안 환경을 생성하는데 유용한 API 입니다. 내용으로 봤을때 사실 개인이 쓸일은 거의 없다고 보여지는 부분이 있어 보입니다. 내용을 살펴보면 아래와 같습니다.

Policies

In an enterprise setting, it's often the case that employee devices must adhere to a strict set of policies that govern the use of the device. The Device Administration API supports the policies listed in Table 1. Note that the Device Administration API currently only supports passwords for screen lock:
Table 1. Policies supported by the Device Administration API.

Policy Description
Password enabled Requires that devices ask for PIN or passwords.
Minimum password length Set the required number of characters for the password. For example, you can require PIN or passwords to have at least six characters.
Alphanumeric password required Requires that passwords have a combination of letters and numbers. They may include symbolic characters.
Complex password required Requires that passwords must contain at least a letter, a numerical digit, and a special symbol. Introduced in Android 3.0.
Minimum letters required in password The minimum number of letters required in the password for all admins or a particular one. Introduced in Android 3.0.
Minimum lowercase letters required in password The minimum number of lowercase letters required in the password for all admins or a particular one. Introduced in Android 3.0.
Minimum non-letter characters required in password The minimum number of non-letter characters required in the password for all admins or a particular one. Introduced in Android 3.0.
Minimum numerical digits required in password The minimum number of numerical digits required in the password for all admins or a particular one. Introduced in Android 3.0.
Minimum symbols required in password The minimum number of symbols required in the password for all admins or a particular one. Introduced in Android 3.0.
Minimum uppercase letters required in password The minimum number of uppercase letters required in the password for all admins or a particular one. Introduced in Android 3.0.
Password expiration timeout When the password will expire, expressed as a delta in milliseconds from when a device admin sets the expiration timeout. Introduced in Android 3.0.
Password history restriction This policy prevents users from reusing the last n unique passwords. This policy is typically used in conjunction with setPasswordExpirationTimeout(), which forces users to update their passwords after a specified amount of time has elapsed. Introduced in Android 3.0.
Maximum failed password attempts Specifies how many times a user can enter the wrong password before the device wipes its data. The Device Administration API also allows administrators to remotely reset the device to factory defaults. This secures data in case the device is lost or stolen.
Maximum inactivity time lock Sets the length of time since the user last touched the screen or pressed a button before the device locks the screen. When this happens, users need to enter their PIN or passwords again before they can use their devices and access data. The value can be between 1 and 60 minutes.
Require storage encryption Specifies that the storage area should be encrypted, if the device supports it. Introduced in Android 3.0.
Disable camera Specifies that the camera should be disabled. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. Introduced in Android 4.0.

Other features

In addition to supporting the policies listed in the above table, the Device Administration API lets you do the following:
  • Prompt user to set a new password.
  • Lock device immediately.
  • Wipe the device's data (that is, restore the device to its factory defaults).

패스워드 길이를 제한한다거나, 패스워드의 정책을 결정하기도 하며, 카메라 동작을 막기도 합니다. 또한 Other feature항목을 보면 디바이스를 잠그거나 유저 데이터를 초기화 하는 기능도 합니다. 이러한 내용을 하는것이 enterprise 환경에서의 동작들만 생각 해볼 수 있을것 같기도 한데 개인이 device 관리자를 사용하는 대표적인 어플이 구글에서 제공하는 android 기기 관리자라는 어플이 있습니다.
https://play.google.com/store/apps/details?id=com.google.android.apps.adm&hl=ko

 




● 사용자의 Google 계정에 연결된 Android 기기의 위치 찾기 
● 기기의 화면 잠금 PIN 재설정 
● 휴대전화의 모든 데이터 삭제

이 어플에서 하는 기능은 디바이스를 분실해서 찾을때 사용하며, 디바이스를 분실한거면 잠그거나 모두 초기화를 진행할 수 있습니다.

이러한 기능을 Device Admin API를 이용해서 구현이 가능합니다.

또한 이러한 어플 외에 삭제가 안되게 하는 어플을 만들때 사용합니다. Device 관리자로 등록된 어플은 관리자 해제를 한 후에만 삭제가 되기 때문에 삭제가 어렵습니다.

정리하자면 안드로이드 Device 관리자는 아래와 같은 필요가 있을때 사용합니다.
1. Password 정책을 관리하고자 할때
2. 화면을 잠그고 싶을때
3. 삭제가 어려운 어플을 만들고 싶을때


예제

예제를 통해서 알아보도록 하겠습니다.
예제는 아래 site 소스를 기반으로 변형한 예제입니다.
http://www.truiton.com/2014/01/android-devicepolicymanager-example/



Device Management Policies

device_policies.xml 파일을 아래 내용으로 res/xml 폴더에 만듭니다. xml 폴더가 없으면 생성하면 됩니다. 해당 내용은 어떤 권한을 사용할건지에 대한 내용입니다. 카메라 잠금 기능을 사용하고 싶지 않으면 disable-camera 부분을 지우면 됩니다.

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
        <limit-password />
        <watch-login />
        <force-lock />
        <wipe-data />
        <expire-password />
        <disable-camera />
    </uses-policies>
</device-admin>



Android DeviceAdminReceiver
receiver를 등록하기위해서는 manifest 파일에 아래 내용을 추가합니다. $ 붙은 부분은 class 안의 inner class를 의미합니다. 그리고 특별한 permission이 필요한데 receiver에 같이 적어둡니다. intent filter는 필요한 항목만 추가 하면 됩니다.

        <receiver
            android:name=".MainActivity$MyDevicePolicyReceiver"
            android:description="@string/device_admin_description"
            android:label="@string/device_admin"
            android:permission="android.permission.BIND_DEVICE_ADMIN" >
            <meta-data
                android:name="android.app.device_admin"
                android:resource="@xml/device_policies" />

            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
                <action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
                <action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
                <action android:name="android.app.action.ACTION_PASSWORD_CHANGED" />
                <action android:name="android.app.action.ACTION_PASSWORD_EXPIRING" />
                <action android:name="android.app.action.ACTION_PASSWORD_FAILED" />
                <action android:name="android.app.action.ACTION_PASSWORD_SUCCEEDED" />
            </intent-filter>
        </receiver>

string.xml에 아래내용을 추가합니다.

<string name="device_admin">Device Admin</string> <string name="device_admin_description">device admin implementation which shows the usage

of DevicePolicyManager class. This implementation provides you a way to control a device\'s security.</string> <string name="admin_explanation">Device Admin helps the user to get special privileges, for eg.

to access the Truiton network data. These privileges can only be acquired by accepting these policies.</string>


이것으로 Receiver를 넣을 준비는 되었는데 Receiver는 inner class이므로 다음에 소스를 나타내도록 하겠습니다.

 Android DevicePolicyManager MainActivity

MainActivity를 만들도록 하겠습니다. 그전에 화면 ui를 보여야 하는데 activity_device_policy_admin.xml 을 만들어 아래 내용을 만듭니다. 별건없고 CheckBox하나를 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="Device Admin Enabled"
        android:textStyle="bold" />

</LinearLayout>

그리고 마지막으로 가장 중요한 MainActivity 내용입니다.


public class MainActivity extends Activity { private final static String LOG_TAG = "DevicePolicyAdmin"; DevicePolicyManager tDevicePolicyManager; ComponentName tDevicePolicyAdmin; private CheckBox tAdminEnabledCheckbox; protected static final int REQUEST_ENABLE = 1; protected static final int SET_PASSWORD = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_device_policy_admin); tDevicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); tDevicePolicyAdmin = new ComponentName(this, MyDevicePolicyReceiver.class); tAdminEnabledCheckbox = (CheckBox) findViewById(R.id.checkBox1); } @Override protected void onResume() { super.onResume(); if (isMyDevicePolicyReceiverActive()) { tAdminEnabledCheckbox.setChecked(true); } else { tAdminEnabledCheckbox.setChecked(false); } tAdminEnabledCheckbox .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { Intent intent = new Intent( DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); intent.putExtra( DevicePolicyManager.EXTRA_DEVICE_ADMIN, tDevicePolicyAdmin); intent.putExtra( DevicePolicyManager.EXTRA_ADD_EXPLANATION, getString(R.string.admin_explanation)); startActivityForResult(intent, REQUEST_ENABLE); } else { tDevicePolicyManager .removeActiveAdmin(tDevicePolicyAdmin); } } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case REQUEST_ENABLE: Log.v(LOG_TAG, "Enabling Policies Now"); tDevicePolicyManager.setMaximumTimeToLock( tDevicePolicyAdmin, 30000L); tDevicePolicyManager.setMaximumFailedPasswordsForWipe( tDevicePolicyAdmin, 5); tDevicePolicyManager.setPasswordQuality( tDevicePolicyAdmin, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); tDevicePolicyManager.setCameraDisabled( tDevicePolicyAdmin, true); boolean isSufficient = tDevicePolicyManager .isActivePasswordSufficient(); if (isSufficient) { tDevicePolicyManager.lockNow(); } else { Intent setPasswordIntent = new Intent( DevicePolicyManager.ACTION_SET_NEW_PASSWORD); startActivityForResult(setPasswordIntent, SET_PASSWORD); tDevicePolicyManager.setPasswordExpirationTimeout( tDevicePolicyAdmin, 10000L); } break; } } } private boolean isMyDevicePolicyReceiverActive() { return tDevicePolicyManager .isAdminActive(tDevicePolicyAdmin); } public static class MyDevicePolicyReceiver extends DeviceAdminReceiver { @Override public void onDisabled(Context context, Intent intent) { Toast.makeText(context, "Device Admin Disabled", Toast.LENGTH_SHORT).show(); } @Override public void onEnabled(Context context, Intent intent) { Toast.makeText(context, "Device Admin is now enabled", Toast.LENGTH_SHORT).show(); } @Override public CharSequence onDisableRequested(Context context, Intent intent) { CharSequence disableRequestedSeq = "Requesting to disable Device Admin"; return disableRequestedSeq; } @Override public void onPasswordChanged(Context context, Intent intent) { Toast.makeText(context, "Device password is now changed", Toast.LENGTH_SHORT).show(); DevicePolicyManager localDPM = (DevicePolicyManager) context .getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName localComponent = new ComponentName(context, MyDevicePolicyReceiver.class); localDPM.setPasswordExpirationTimeout(localComponent, 0L); } @Override public void onPasswordExpiring(Context context, Intent intent) { // This would require API 11 an above Toast.makeText( context, "Device password is going to expire, please change to a new password", Toast.LENGTH_LONG).show(); DevicePolicyManager localDPM = (DevicePolicyManager) context .getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName localComponent = new ComponentName(context, MyDevicePolicyReceiver.class); long expr = localDPM.getPasswordExpiration(localComponent); long delta = expr - System.currentTimeMillis(); boolean expired = delta < 0L; if (expired) { localDPM.setPasswordExpirationTimeout(localComponent, 10000L); Intent passwordChangeIntent = new Intent( DevicePolicyManager.ACTION_SET_NEW_PASSWORD); passwordChangeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(passwordChangeIntent); } } @Override public void onPasswordFailed(Context context, Intent intent) { Toast.makeText(context, "Password failed", Toast.LENGTH_SHORT) .show(); } @Override public void onPasswordSucceeded(Context context, Intent intent) { Toast.makeText(context, "Access Granted", Toast.LENGTH_SHORT) .show(); } @Override public void onReceive(Context context, Intent intent) { Log.i(LOG_TAG, "MyDevicePolicyReciever Received: " + intent.getAction()); super.onReceive(context, intent); } }

실행화면

실행하면 아래화면이 뜹니다.

 

    

 

Checkbox를 선택하면 아래와 같은 화면이 뜨고

 

 

실행을 누르면 아래와 같은 화면이 뜨는데 이것은 비밀번호를 설정을 안해놓아서 정책에 맞지 않아서 발생하는 현상입니다.

 

 

 

 

마지막

마지막으로 해당 소스에서 주로 사용할것으로 예상되는 화면 잠금은 아래와 같은 소스로 테스트 해볼 수 있습니다.
Device관리자가 등록될때 lockNow 함수를 호출하는 예제인데 필요하면 어떠한 곳에서도 사용할 수가 있습니다.


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
//        if (resultCode == RESULT_OK) {
//            switch (requestCode) {
//                case REQUEST_ENABLE:
//                    Log.v(LOG_TAG, "Enabling Policies Now");
//                    tDevicePolicyManager.setMaximumTimeToLock(
//                            tDevicePolicyAdmin, 30000L);
//                    tDevicePolicyManager.setMaximumFailedPasswordsForWipe(
//                            tDevicePolicyAdmin, 5);
//                    tDevicePolicyManager.setPasswordQuality(
//                            tDevicePolicyAdmin,
//                            DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
//                    tDevicePolicyManager.setCameraDisabled(
//                            tDevicePolicyAdmin, true);
//                    boolean isSufficient = tDevicePolicyManager
//                            .isActivePasswordSufficient();
//                    if (isSufficient) {
//                        tDevicePolicyManager.lockNow();
//                    } else {
//                        Intent setPasswordIntent = new Intent(
//                                DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
//                        startActivityForResult(setPasswordIntent, SET_PASSWORD);
//                        tDevicePolicyManager.setPasswordExpirationTimeout(
//                                tDevicePolicyAdmin, 10000L);
//                    }
//                    break;
//            }
//        }
        DevicePolicyManager t;
        t = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
        t.lockNow();
    }


그리고 중요한 receiver인 onDisabled,onDisableRequested 가 있습니다. Device Manager가 해제되기전에 onDisableRequested가 먼저 호출되어 문구를 화면에 보여주게되는데 삭제가 안되게 하려면 해당 시점에 사용자 동작을 막아야 하지만 기술적으로는 막기가 힘듭니다.
(화면에 top window를 띄워서 ui가 안보이게 막는 방법, 홈키를 눌러 런처로 나가는 방법, 화면을 잠그는 방법등 모두 시도해보았지만 해당 시점에는 제대로 동작하지 않습니다.)

지금까지의 소스 입니다.
https://drive.google.com/open?id=0B9vAKDzHthQIazk2MEluNmZMVjQ

 

[출처] http://swlock.blogspot.kr/2016/04/android-device-administration.html 

Comments