Android 15 Lock Task 模式深度分析(第二部分)

Android 15 Lock Task 模式深度分析 (第二部分)

6. 企业管理配置

6.1 DeviceOwner/ProfileOwner配置

Lock Task的完全锁定模式(LOCKED)需要通过DevicePolicyManager(DPM)进行配置。只有DeviceOwner或ProfileOwner才能使用完整的Lock Task功能。

6.1.1 设置白名单
java 复制代码
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;

public class DeviceAdminHelper {
    private DevicePolicyManager mDpm;
    private ComponentName mAdminComponent;

    public DeviceAdminHelper(Context context, ComponentName adminComponent) {
        mDpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        mAdminComponent = adminComponent;
    }

    /**
     * 设置Lock Task白名单包
     * 只有DeviceOwner或ProfileOwner可以调用
     */
    public void setLockTaskPackages(String[] packages) {
        if (!mDpm.isDeviceOwnerApp(mAdminComponent.getPackageName()) &&
            !mDpm.isProfileOwnerApp(mAdminComponent.getPackageName())) {
            throw new SecurityException("Not a device owner or profile owner");
        }

        try {
            mDpm.setLockTaskPackages(mAdminComponent, packages);
            Log.i(TAG, "Lock task packages set: " + Arrays.toString(packages));
        } catch (SecurityException e) {
            Log.e(TAG, "Failed to set lock task packages", e);
        }
    }

    /**
     * 获取当前白名单
     */
    public String[] getLockTaskPackages() {
        return mDpm.getLockTaskPackages(mAdminComponent);
    }

    /**
     * 检查包是否在白名单中
     */
    public boolean isLockTaskPermitted(String packageName) {
        return mDpm.isLockTaskPermitted(packageName);
    }
}

使用示例:

java 复制代码
// 在DeviceOwner应用中
DeviceAdminHelper adminHelper = new DeviceAdminHelper(
    context,
    new ComponentName(context, MyDeviceAdminReceiver.class)
);

// 设置多个应用进入白名单
String[] allowedPackages = {
    "com.example.kiosk.launcher",  // Kiosk启动器
    "com.example.kiosk.app1",      // 业务应用1
    "com.example.kiosk.app2",      // 业务应用2
    "com.android.settings"         // 设置应用(可选)
};

adminHelper.setLockTaskPackages(allowedPackages);
6.1.2 配置Lock Task功能

源码 : DevicePolicyManager.java: Line 3018-3091

java 复制代码
public class LockTaskFeatureConfig {
    private DevicePolicyManager mDpm;
    private ComponentName mAdminComponent;

    /**
     * 设置Lock Task模式下允许的功能
     * @param features 功能标志位的组合
     */
    public void setLockTaskFeatures(int features) {
        mDpm.setLockTaskFeatures(mAdminComponent, features);
    }

    /**
     * 示例: 配置Kiosk模式 - 最小功能
     */
    public void configureKioskMode() {
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
        // 禁用所有系统功能
        setLockTaskFeatures(features);
    }

    /**
     * 示例: 配置受限模式 - 允许基本功能
     */
    public void configureRestrictedMode() {
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
                     | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
                     | DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
        setLockTaskFeatures(features);
    }

    /**
     * 示例: 配置考试模式 - 允许通知但禁止导航
     */
    public void configureExamMode() {
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
                     | DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
                     | DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
        setLockTaskFeatures(features);
    }

    /**
     * 示例: 配置展示模式 - 允许大部分功能
     */
    public void configurePresentationMode() {
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
                     | DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
                     | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
                     | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW
                     | DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
        setLockTaskFeatures(features);
    }

    /**
     * 获取当前配置的功能
     */
    public int getLockTaskFeatures() {
        return mDpm.getLockTaskFeatures(mAdminComponent);
    }
}
6.1.3 功能标志详解
功能标志 说明 允许的操作
LOCK_TASK_FEATURE_NONE 0 无功能 完全限制,仅锁定应用可用
LOCK_TASK_FEATURE_SYSTEM_INFO 1 系统信息 显示状态栏时钟、电池等系统图标
LOCK_TASK_FEATURE_NOTIFICATIONS 2 通知 显示通知图标和下拉通知面板
LOCK_TASK_FEATURE_HOME 4 Home按钮 允许使用Home按钮切换白名单应用
LOCK_TASK_FEATURE_OVERVIEW 8 最近任务 允许使用最近任务按钮(需配合HOME)
LOCK_TASK_FEATURE_GLOBAL_ACTIONS 16 全局操作 允许电源菜单(关机/重启)
LOCK_TASK_FEATURE_KEYGUARD 32 锁屏 允许锁屏功能
LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK 64 阻止启动Activity 阻止任务内启动非白名单Activity

注意:

  • LOCK_TASK_FEATURE_OVERVIEW必须与LOCK_TASK_FEATURE_HOME一起使用
  • 默认情况下(PINNED模式),LOCK_TASK_FEATURE_GLOBAL_ACTIONS是启用的
6.1.4 完整的DeviceOwner配置示例
java 复制代码
public class KioskDeviceAdmin extends DeviceAdminReceiver {
    private static final String TAG = "KioskDeviceAdmin";

    @Override
    public void onEnabled(Context context, Intent intent) {
        super.onEnabled(context, intent);
        Log.i(TAG, "Device admin enabled");

        // 配置Kiosk模式
        setupKioskMode(context);
    }

    private void setupKioskMode(Context context) {
        DevicePolicyManager dpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName adminComponent =
            new ComponentName(context, KioskDeviceAdmin.class);

        // 1. 设置白名单应用
        String[] lockTaskPackages = {
            context.getPackageName(),  // Kiosk应用自身
            "com.example.kiosk.content"
        };
        dpm.setLockTaskPackages(adminComponent, lockTaskPackages);

        // 2. 配置允许的功能(最小化)
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
                     | DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
        dpm.setLockTaskFeatures(adminComponent, features);

        // 3. 设置其他策略
        // 禁用状态栏
        dpm.setStatusBarDisabled(adminComponent, true);

        // 禁用安全键盘
        dpm.setKeyguardDisabled(adminComponent, true);

        // 设置用户限制
        dpm.addUserRestriction(adminComponent,
            UserManager.DISALLOW_SAFE_BOOT);
        dpm.addUserRestriction(adminComponent,
            UserManager.DISALLOW_FACTORY_RESET);
        dpm.addUserRestriction(adminComponent,
            UserManager.DISALLOW_ADD_USER);
        dpm.addUserRestriction(adminComponent,
            UserManager.DISALLOW_INSTALL_APPS);
        dpm.addUserRestriction(adminComponent,
            UserManager.DISALLOW_UNINSTALL_APPS);

        Log.i(TAG, "Kiosk mode configured");
    }

    /**
     * 启动Kiosk应用并进入Lock Task
     */
    public static void startKioskApp(Context context) {
        Intent intent = new Intent(context, KioskMainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }
}

KioskMainActivity实现:

java 复制代码
public class KioskMainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_kiosk);

        // 隐藏系统UI
        hideSystemUI();
    }

    @Override
    protected void onStart() {
        super.onStart();

        // 自动启动Lock Task
        startLockTask();
    }

    private void hideSystemUI() {
        View decorView = getWindow().getDecorView();
        int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                      | View.SYSTEM_UI_FLAG_FULLSCREEN
                      | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                      | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                      | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
    }

    @Override
    public void onBackPressed() {
        // 在Kiosk模式中禁用返回键
        // 不调用super.onBackPressed()
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // 拦截某些按键
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
            keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
            keyCode == KeyEvent.KEYCODE_POWER) {
            // 可以选择性处理或忽略
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

6.2 ADB命令配置

对于开发和测试,可以使用ADB命令配置Lock Task。

6.2.1 设置DeviceOwner(仅限未激活设备)
bash 复制代码
# 安装设备管理员应用
adb install KioskApp.apk

# 设置为DeviceOwner
adb shell dpm set-device-owner com.example.kiosk/.KioskDeviceAdmin

# 验证
adb shell dpm list-owners
6.2.2 配置Lock Task白名单
bash 复制代码
# 设置白名单(多个包用空格分隔)
adb shell dpm set-lock-task-packages com.example.kiosk \
  com.example.kiosk com.example.kiosk.content

# 查看当前白名单
adb shell dpm get-lock-task-packages
6.2.3 配置Lock Task功能
bash 复制代码
# 设置功能标志(使用十进制值)
# SYSTEM_INFO(1) + HOME(4) = 5
adb shell settings put global lock_task_features 5

# 或使用dpm命令
adb shell dpm set-lock-task-features com.example.kiosk/.KioskDeviceAdmin 5

功能标志计算:

复制代码
NONE = 0
SYSTEM_INFO = 1
NOTIFICATIONS = 2
HOME = 4
OVERVIEW = 8
GLOBAL_ACTIONS = 16
KEYGUARD = 32
BLOCK_ACTIVITY_START = 64

示例组合:
- 基本Kiosk: SYSTEM_INFO + HOME = 1 + 4 = 5
- 带通知: SYSTEM_INFO + NOTIFICATIONS + HOME = 1 + 2 + 4 = 7
- 完全受限: NONE = 0
- 全功能: 1+2+4+8+16+32 = 63
6.2.4 启动和停止Lock Task
bash 复制代码
# 启动指定包的Lock Task
adb shell am start-lock-task-mode com.example.kiosk/.MainActivity

# 停止Lock Task
adb shell am stop-lock-task-mode

# 查看当前Lock Task状态
adb shell dumpsys activity | grep "mLockTaskMode"
6.2.5 清除DeviceOwner
bash 复制代码
# 移除DeviceOwner(会清除所有策略)
adb shell dpm remove-active-admin com.example.kiosk/.KioskDeviceAdmin

# 恢复出厂设置
adb shell am broadcast -a android.intent.action.FACTORY_RESET \
  -p android --ez EXTRA_WIPE_EXTERNAL_STORAGE true

6.3 Screen Pinning用户设置

Screen Pinning(屏幕固定)可以通过系统设置启用。

6.3.1 启用Screen Pinning

通过设置界面:

  1. 打开设置 → 安全 → 屏幕固定
  2. 开启"屏幕固定"开关
  3. 可选:启用"退出时要求解锁"

通过代码检查:

java 复制代码
public class ScreenPinningHelper {
    /**
     * 检查Screen Pinning是否启用
     */
    public static boolean isScreenPinningEnabled(Context context) {
        try {
            int enabled = Settings.System.getInt(
                context.getContentResolver(),
                Settings.System.LOCK_TO_APP_ENABLED,
                0
            );
            return enabled != 0;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 检查退出时是否需要解锁
     */
    public static boolean isExitLockRequired(Context context) {
        try {
            int locked = Settings.Secure.getInt(
                context.getContentResolver(),
                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
                0
            );
            return locked != 0;
        } catch (Exception e) {
            return false;
        }
    }
}
6.3.2 Settings实现分析

源码 : /packages/apps/Settings/src/com/android/settings/security/ScreenPinningSettings.java

Screen Pinning设置界面提供:

  • 开关控件:启用/禁用功能
  • 退出锁定选项:退出时是否需要解锁设备
  • 使用说明:如何使用Screen Pinning

相关Settings常量:

java 复制代码
// Settings.System.LOCK_TO_APP_ENABLED
// 0 = 禁用, 1 = 启用

// Settings.Secure.LOCK_TO_APP_EXIT_LOCKED
// 0 = 退出时不锁定, 1 = 退出时锁定设备

7. 使用场景

7.1 Kiosk模式(专用设备)

场景描述: 将设备配置为单一用途的专用设备,如自助服务终端、信息查询机等。

实现要点:

java 复制代码
public class KioskModeManager {
    /**
     * 配置完整的Kiosk模式
     */
    public void setupFullKioskMode(Context context) {
        DevicePolicyManager dpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName admin = new ComponentName(context, MyDeviceAdmin.class);

        // 1. 设置Lock Task白名单(仅Kiosk应用)
        String[] packages = {context.getPackageName()};
        dpm.setLockTaskPackages(admin, packages);

        // 2. 最小化功能(仅系统信息)
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
        dpm.setLockTaskFeatures(admin, features);

        // 3. 禁用状态栏和键盘锁
        dpm.setStatusBarDisabled(admin, true);
        dpm.setKeyguardDisabled(admin, true);

        // 4. 添加用户限制
        dpm.addUserRestriction(admin, UserManager.DISALLOW_SAFE_BOOT);
        dpm.addUserRestriction(admin, UserManager.DISALLOW_FACTORY_RESET);
        dpm.addUserRestriction(admin, UserManager.DISALLOW_ADD_USER);
        dpm.addUserRestriction(admin, UserManager.DISALLOW_INSTALL_APPS);
        dpm.addUserRestriction(admin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
        dpm.addUserRestriction(admin, UserManager.DISALLOW_USB_FILE_TRANSFER);

        // 5. 设置不可移除
        dpm.setUninstallBlocked(admin, context.getPackageName(), true);

        // 6. 持续运行
        dpm.setLockTaskPackages(admin, packages);
    }

    /**
     * 启动Kiosk应用
     */
    public void launchKioskApp(Context context) {
        Intent intent = new Intent(context, KioskActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                       Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);
    }
}

典型应用:

  • 零售店POS系统
  • 餐厅点餐机
  • 银行ATM机
  • 酒店自助入住机
  • 博物馆导览终端

7.2 企业MDM管理

场景描述: 企业IT管理员通过MDM(Mobile Device Management)系统远程管理设备。

实现架构:

复制代码
┌──────────────────┐
│  MDM Server      │
│  (Cloud)         │
└────────┬─────────┘
         │ HTTPS/WebSocket
         │
┌────────▼─────────┐
│  MDM Agent       │
│  (Device Admin)  │
├──────────────────┤
│ • Policy Sync    │
│ • Lock Task Mgmt │
│ • App Whitelist  │
│ • Remote Control │
└────────┬─────────┘
         │
┌────────▼─────────┐
│  Managed Apps    │
│  (在Lock Task中) │
└──────────────────┘

MDM Agent实现示例:

java 复制代码
public class MdmAgent extends DeviceAdminReceiver {
    /**
     * 从MDM服务器同步策略
     */
    public void syncPolicies(Context context) {
        // 1. 获取服务器策略
        MdmPolicy policy = fetchPolicyFromServer();

        // 2. 应用Lock Task策略
        applyLockTaskPolicy(context, policy);

        // 3. 应用其他策略
        applySecurityPolicy(context, policy);
    }

    private void applyLockTaskPolicy(Context context, MdmPolicy policy) {
        DevicePolicyManager dpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName admin = new ComponentName(context, MdmAgent.class);

        // 设置白名单
        if (policy.lockTaskPackages != null) {
            dpm.setLockTaskPackages(admin, policy.lockTaskPackages);
        }

        // 设置功能
        if (policy.lockTaskFeatures != null) {
            dpm.setLockTaskFeatures(admin, policy.lockTaskFeatures);
        }

        // 自动启动Lock Task(如果配置)
        if (policy.autoStartLockTask) {
            startManagedApps(context, policy.lockTaskPackages[0]);
        }
    }

    /**
     * 监控Lock Task状态
     */
    public void monitorLockTaskState(Context context) {
        ActivityManager am = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);

        int state = am.getLockTaskModeState();

        // 上报状态到MDM服务器
        reportStateToServer(state);

        // 如果意外退出,重新启动
        if (state == ActivityManager.LOCK_TASK_MODE_NONE &&
            shouldBeInLockTask()) {
            restartLockTask(context);
        }
    }
}

使用场景:

  • 企业办公设备管理
  • 员工移动设备控制
  • 合规性管理
  • 数据保护

7.3 教育场景(考试模式)

场景描述: 在考试期间,限制学生设备只能访问考试应用,防止作弊。

考试模式实现:

java 复制代码
public class ExamModeManager {
    /**
     * 启动考试模式
     */
    public void startExamMode(Context context, String examAppPackage) {
        DevicePolicyManager dpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName admin = getAdminComponent(context);

        // 1. 设置考试应用为白名单
        String[] packages = {examAppPackage};
        dpm.setLockTaskPackages(admin, packages);

        // 2. 配置受限功能(允许查看时间,禁止通知)
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
                     | DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
        dpm.setLockTaskFeatures(admin, features);

        // 3. 禁用网络(可选)
        if (shouldDisableNetwork()) {
            dpm.addUserRestriction(admin, UserManager.DISALLOW_CONFIG_WIFI);
            dpm.addUserRestriction(admin, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
        }

        // 4. 启动考试应用
        Intent intent = context.getPackageManager()
            .getLaunchIntentForPackage(examAppPackage);
        if (intent != null) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }

        // 5. 记录考试开始时间
        saveExamStartTime(System.currentTimeMillis());
    }

    /**
     * 结束考试模式
     */
    public void endExamMode(Context context) {
        DevicePolicyManager dpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);
        ComponentName admin = getAdminComponent(context);

        // 1. 清除白名单
        dpm.setLockTaskPackages(admin, new String[]{});

        // 2. 恢复网络
        dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_WIFI);
        dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);

        // 3. 停止Lock Task
        ActivityManager am = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);
        if (am.isInLockTaskMode()) {
            // 需要在考试应用中调用stopLockTask()
            broadcastStopExam(context);
        }

        // 4. 记录考试结束时间
        saveExamEndTime(System.currentTimeMillis());
    }

    /**
     * 监控考试状态
     */
    public void monitorExamStatus(Context context) {
        // 检查考试时长
        long duration = System.currentTimeMillis() - getExamStartTime();
        long maxDuration = getMaxExamDuration();

        if (duration >= maxDuration) {
            // 自动结束考试
            endExamMode(context);
            notifyExamTimeout(context);
        }

        // 检查是否意外退出
        ActivityManager am = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);
        if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE &&
            isExamInProgress()) {
            // 记录违规并重新启动
            logViolation("Exam mode exited unexpectedly");
            restartExamMode(context);
        }
    }
}

考试应用实现:

java 复制代码
public class ExamActivity extends Activity {
    private CountDownTimer mTimer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exam);

        // 隐藏系统UI
        hideSystemUI();

        // 启动倒计时
        startCountdown();
    }

    @Override
    protected void onResume() {
        super.onResume();

        // 自动进入Lock Task
        startLockTask();

        // 注册监听器
        registerExamBroadcastReceiver();
    }

    private void startCountdown() {
        long examDuration = getIntent().getLongExtra("duration", 3600000); // 默认1小时

        mTimer = new CountDownTimer(examDuration, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                updateTimeDisplay(millisUntilFinished);
            }

            @Override
            public void onFinish() {
                finishExam();
            }
        }.start();
    }

    private void finishExam() {
        // 1. 收集答案
        submitAnswers();

        // 2. 停止Lock Task
        stopLockTask();

        // 3. 关闭应用
        finish();
    }

    @Override
    public void onBackPressed() {
        // 考试期间禁用返回键
        showWarning("考试进行中,不能退出");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mTimer != null) {
            mTimer.cancel();
        }
    }
}

使用场景:

  • 在线考试
  • 技能测试
  • 认证考核
  • 课堂测验

7.4 公共展示设备

场景描述: 商场、展览会等公共场所的信息展示设备。

实现特点:

java 复制代码
public class DisplayKioskActivity extends Activity {
    private Handler mIdleHandler = new Handler();
    private Runnable mIdleRunnable;
    private static final long IDLE_TIMEOUT = 60000; // 1分钟无操作

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_display);

        // 启动Lock Task
        startLockTask();

        // 设置全屏
        setupFullScreen();

        // 启动空闲计时器
        resetIdleTimer();

        // 设置交互监听
        setupInteractionListeners();
    }

    private void setupFullScreen() {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        hideSystemUI();
    }

    private void setupInteractionListeners() {
        View rootView = findViewById(android.R.id.content);
        rootView.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
            return false;
        });
    }

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        resetIdleTimer();
    }

    private void resetIdleTimer() {
        mIdleHandler.removeCallbacks(mIdleRunnable);

        mIdleRunnable = () -> {
            // 空闲超时 - 返回主页
            returnToHomePage();
        };

        mIdleHandler.postDelayed(mIdleRunnable, IDLE_TIMEOUT);
    }

    private void returnToHomePage() {
        // 重置到初始页面
        Intent intent = new Intent(this, DisplayKioskActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
    }

    private void hideSystemUI() {
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_FULLSCREEN
        );
    }
}

使用场景:

  • 商场导航屏
  • 展览信息亭
  • 酒店大堂信息屏
  • 机场指引屏

7.5 其他实际应用场景

7.5.1 医疗设备
java 复制代码
// 医疗设备专用模式
public class MedicalDeviceManager {
    public void setupMedicalMode(Context context) {
        // 配置高安全性的Lock Task
        // - 禁止所有系统功能
        // - 仅允许医疗应用
        // - 启用数据加密
        // - 合规性审计日志
    }
}

特点:

  • HIPAA合规
  • 患者隐私保护
  • 数据完整性
  • 审计追踪
7.5.2 车载系统
java 复制代码
// 车载信息娱乐系统
public class VehicleKioskManager {
    public void setupDrivingMode(Context context) {
        // 驾驶模式
        // - 限制分散注意力的应用
        // - 允许导航和音乐
        // - 语音控制优先
    }
}

特点:

  • 驾驶安全
  • 限制交互
  • 语音控制
  • 车辆集成
7.5.3 工业控制
java 复制代码
// 工业控制终端
public class IndustrialControlManager {
    public void setupControlMode(Context context) {
        // 工业控制模式
        // - 仅控制应用
        // - 防误操作
        // - 实时监控
        // - 日志记录
    }
}

特点:

  • 操作安全
  • 实时性
  • 稳定性
  • 防护等级

8. AOSP其他窗口模式对比

8.1 模式概览

Android系统提供了多种窗口和任务模式,各有不同的用途和特性。

模式 引入版本 主要用途 限制级别
Lock Task 5.0 设备锁定、Kiosk
Screen Pinning 5.0 临时固定
Multi-Window 7.0 分屏多任务
Freeform 7.0 自由窗口
Picture-in-Picture 8.0 画中画
Home Mode All 启动器
Recents Mode All 最近任务

8.2 详细对比

8.2.1 Lock Task vs Screen Pinning
特性 Lock Task (LOCKED) Screen Pinning (PINNED)
权限要求 DeviceOwner/ProfileOwner 无(用户确认)
启动方式 API或自动(白名单) API或最近任务菜单
退出方式 代码或管理员 Back+Recents手势
多任务 支持白名单多任务 仅单任务
UI限制 完全可配置 固定限制
状态栏 可完全隐藏 部分限制
导航栏 可配置 受限但可见
应用场景 企业、Kiosk 演示、临时锁定
8.2.2 Multi-Window模式(分屏)

特点:

  • 同时显示两个应用
  • 可调整分割比例
  • 支持拖拽共享数据

启动方式:

java 复制代码
// 在分屏模式中启动Activity
Intent intent = new Intent(this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT |
               Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Manifest配置:

xml 复制代码
<activity
    android:name=".MainActivity"
    android:resizeableActivity="true"
    android:supportsPictureInPicture="false" />

与Lock Task对比:

  • Multi-Window允许用户自由切换,Lock Task限制切换
  • Multi-Window是生产力工具,Lock Task是安全工具
  • Multi-Window可以与Lock Task共存(白名单应用可以分屏)
8.2.3 Freeform模式(自由窗口)

特点:

  • 类似桌面系统的窗口模式
  • 可移动、调整大小
  • 支持多窗口重叠

启动方式:

java 复制代码
// 以Freeform模式启动
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(
    WindowConfiguration.WINDOWING_MODE_FREEFORM);

Intent intent = new Intent(this, FreeformActivity.class);
startActivity(intent, options.toBundle());

开启Freeform (需要开发者选项):

bash 复制代码
adb shell settings put global enable_freeform_support 1
adb shell settings put global force_resizable_activities 1

与Lock Task对比:

  • Freeform提供最大自由度,Lock Task提供最大限制
  • Freeform适合生产环境,Lock Task适合专用环境
  • 二者互斥(Lock Task启动时会退出Freeform)
8.2.4 Picture-in-Picture模式(画中画)

特点:

  • 小窗口悬浮显示
  • 主要用于视频播放
  • 可以在其他应用上层显示

实现示例:

java 复制代码
public class VideoActivity extends Activity {
    @Override
    protected void onUserLeaveHint() {
        super.onUserLeaveHint();

        // 进入PIP模式
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            PictureInPictureParams params =
                new PictureInPictureParams.Builder()
                    .setAspectRatio(new Rational(16, 9))
                    .build();
            enterPictureInPictureMode(params);
        }
    }

    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
                                              Configuration newConfig) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);

        if (isInPictureInPictureMode) {
            // 隐藏UI控件
            hideControls();
        } else {
            // 显示完整UI
            showControls();
        }
    }
}

Manifest配置:

xml 复制代码
<activity
    android:name=".VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
    android:resizeableActivity="true" />

与Lock Task交互:

  • Lock Task启动时会移除PIP窗口(源码: LockTaskController.java: Line 678
  • PIP不能在LOCKED模式中使用
  • PINNED模式可能允许PIP(取决于配置)
8.2.5 模式共存矩阵
当前模式 可启动Lock Task 可启动Multi-Window 可启动Freeform 可启动PIP
Normal
Lock Task (LOCKED) ✓(白名单) ✓(白名单)
Screen Pinning
Multi-Window
Freeform
PIP -

9. 版本演进历史

9.1 Android 5.0 (Lollipop) - 首次引入

引入功能:

  • Lock Task Mode基础框架
  • Screen Pinning用户功能
  • DevicePolicyManager支持

API变更:

java 复制代码
// Activity新增方法
public void startLockTask()
public void stopLockTask()

// DevicePolicyManager新增方法
public void setLockTaskPackages(ComponentName admin, String[] packages)
public String[] getLockTaskPackages(ComponentName admin)
public boolean isLockTaskPermitted(String pkg)

// ActivityManager新增方法
public boolean isInLockTaskMode()

Manifest属性:

xml 复制代码
android:lockTaskMode="normal|never|always|if_whitelisted"

9.2 Android 6.0 (Marshmallow) - 功能增强

新增功能:

  • Lock Task Feature标志
  • 更细粒度的UI控制

API变更:

java 复制代码
// DevicePolicyManager新增
public void setLockTaskFeatures(ComponentName admin, int flags)
public int getLockTaskFeatures(ComponentName admin)

新增Feature标志:

  • LOCK_TASK_FEATURE_SYSTEM_INFO
  • LOCK_TASK_FEATURE_NOTIFICATIONS
  • LOCK_TASK_FEATURE_HOME
  • LOCK_TASK_FEATURE_OVERVIEW
  • LOCK_TASK_FEATURE_GLOBAL_ACTIONS

9.3 Android 7.0 (Nougat) - 多窗口支持

变更:

  • Lock Task与Multi-Window的兼容性改进
  • 白名单应用可以在Lock Task中分屏

行为变更:

  • Lock Task启动时不再强制全屏
  • 支持白名单应用之间的分屏

9.4 Android 8.0 (Oreo) - Treble架构

变更:

  • Lock Task逻辑重构以适配Treble
  • 改进与SystemUI的交互

新增:

  • LOCK_TASK_FEATURE_KEYGUARD支持

9.5 Android 9.0 (Pie) - 手势导航

变更:

  • 支持手势导航系统
  • Screen Pinning手势适配

行为变更:

  • 手势导航模式下的Screen Pinning退出手势调整

9.6 Android 10 (Q) - 作用域存储

变更:

  • Lock Task与作用域存储的兼容性
  • 权限模型更新

9.7 Android 11 ® - One-time权限

变更:

  • 一次性权限与Lock Task的交互
  • 自动重置权限的例外处理

9.8 Android 12 (S) - Material You

变更:

  • SystemUI重构对Lock Task的影响
  • LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK新增

新增Feature:

java 复制代码
public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 1 << 6;

9.9 Android 13 (T) - 隐私增强

变更:

  • 通知权限与Lock Task的交互
  • 照片选择器的Lock Task支持

9.10 Android 14 (U) - 企业功能增强

变更:

  • Device Policy API现代化
  • Lock Task配置简化

9.11 Android 15 (V) - 当前版本

当前状态:

  • 代码位置: /frameworks/base/services/core/java/com/android/server/wm/LockTaskController.java
  • 核心逻辑稳定,主要是性能优化和Bug修复
  • 支持最新的隐私和安全特性

主要改进:

  • 性能优化:减少状态切换开销
  • 安全增强:更严格的权限检查
  • 兼容性:与最新UI特性兼容

9.12 版本兼容性总结

功能 最低支持版本 备注
基础Lock Task 5.0 (API 21)
Screen Pinning 5.0 (API 21)
Lock Task Features 6.0 (API 23)
Multi-Window Lock Task 7.0 (API 24)
Keyguard Feature 8.0 (API 26)
Block Activity Start 12.0 (API 31)

向后兼容性注意事项:

java 复制代码
public class LockTaskCompat {
    public static void startLockTask(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.startLockTask();
        } else {
            // 不支持Lock Task
            Toast.makeText(activity,
                "Lock Task需要Android 5.0+",
                Toast.LENGTH_SHORT).show();
        }
    }

    public static void setLockTaskFeatures(DevicePolicyManager dpm,
                                          ComponentName admin,
                                          int features) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            dpm.setLockTaskFeatures(admin, features);
        } else {
            // Android 6.0以下不支持Feature配置
        }
    }
}

10. 最佳实践与注意事项

10.1 设计原则

10.1.1 用户体验

DO (推荐):

  • 提供清晰的进入/退出提示
  • 在合适的时机自动启动Lock Task
  • 保留必要的系统功能(如时钟)
  • 提供紧急退出机制

DON'T (不推荐):

  • 不要突然进入Lock Task而不通知用户
  • 不要完全隐藏所有系统信息
  • 不要阻止合法的退出操作
  • 不要在不合适的场景使用
10.1.2 安全性

最佳实践:

java 复制代码
public class SecureLockTaskManager {
    /**
     * 安全地启动Lock Task
     */
    public boolean startLockTaskSecurely(Context context, Activity activity) {
        // 1. 验证权限
        if (!hasRequiredPermissions(context)) {
            Log.e(TAG, "Missing required permissions");
            return false;
        }

        // 2. 检查设备状态
        if (!isDeviceSecure(context)) {
            Log.w(TAG, "Device is not secure");
            // 可选:要求用户设置锁屏
            return false;
        }

        // 3. 验证应用签名(防止重打包)
        if (!verifyAppSignature(context)) {
            Log.e(TAG, "Invalid app signature");
            return false;
        }

        // 4. 记录审计日志
        logAuditEvent("LOCK_TASK_STARTED", activity.getClass().getName());

        // 5. 启动Lock Task
        try {
            activity.startLockTask();
            return true;
        } catch (SecurityException e) {
            Log.e(TAG, "Failed to start lock task", e);
            return false;
        }
    }

    /**
     * 检查设备是否安全
     */
    private boolean isDeviceSecure(Context context) {
        KeyguardManager km = (KeyguardManager)
            context.getSystemService(Context.KEYGUARD_SERVICE);
        return km != null && km.isDeviceSecure();
    }
}
10.1.3 性能优化

注意事项:

  1. 避免频繁切换:
java 复制代码
// BAD: 频繁切换
for (int i = 0; i < 10; i++) {
    startLockTask();
    Thread.sleep(100);
    stopLockTask();
}

// GOOD: 一次性配置
startLockTask();
// 保持Lock Task状态
  1. 预加载资源:
java 复制代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // 在启动Lock Task前预加载资源
    preloadResources();

    // 然后启动Lock Task
    startLockTask();
}
  1. 监听生命周期:
java 复制代码
@Override
protected void onStop() {
    super.onStop();

    // 如果Activity停止但Lock Task还在
    ActivityManager am = (ActivityManager)
        getSystemService(Context.ACTIVITY_SERVICE);
    if (am.isInLockTaskMode() && !isFinishing()) {
        // 确保Activity回到前台
        moveTaskToFront();
    }
}

10.2 常见问题与解决方案

10.2.1 问题: 无法启动Lock Task

症状:

复制代码
SecurityException: Calling package does not have permission

解决方案:

  1. 检查Manifest配置:
xml 复制代码
<activity
    android:name=".MainActivity"
    android:lockTaskMode="normal"  <!-- 确保配置正确 -->
    android:exported="true" />
  1. 检查是否在白名单中:
java 复制代码
DevicePolicyManager dpm = (DevicePolicyManager)
    getSystemService(Context.DEVICE_POLICY_SERVICE);
String packageName = getPackageName();
boolean isAllowed = dpm.isLockTaskPermitted(packageName);

if (!isAllowed) {
    // 添加到白名单
    ComponentName admin = getAdminComponent();
    dpm.setLockTaskPackages(admin, new String[]{packageName});
}
  1. 检查DeviceOwner状态:
bash 复制代码
# 查看DeviceOwner
adb shell dpm list-owners

# 如果没有,设置DeviceOwner (需要未激活设备)
adb shell dpm set-device-owner com.example.app/.AdminReceiver
10.2.2 问题: Lock Task意外退出

症状 :

Lock Task模式自动退出,返回到主屏幕

原因分析:

  1. 任务被清除
  2. 应用崩溃
  3. 系统资源不足
  4. 白名单配置变更

解决方案:

java 复制代码
public class LockTaskMonitor extends Service {
    private Handler mHandler = new Handler();
    private Runnable mCheckRunnable;

    @Override
    public void onCreate() {
        super.onCreate();
        startMonitoring();
    }

    private void startMonitoring() {
        mCheckRunnable = new Runnable() {
            @Override
            public void run() {
                ActivityManager am = (ActivityManager)
                    getSystemService(Context.ACTIVITY_SERVICE);

                // 检查Lock Task状态
                int state = am.getLockTaskModeState();
                if (state == ActivityManager.LOCK_TASK_MODE_NONE) {
                    // Lock Task已退出,重新启动
                    Log.w(TAG, "Lock Task exited unexpectedly, restarting");
                    restartLockTask();
                }

                // 继续监控
                mHandler.postDelayed(this, 5000); // 每5秒检查一次
            }
        };

        mHandler.post(mCheckRunnable);
    }

    private void restartLockTask() {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                       Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mCheckRunnable);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

在Manifest中配置持久服务:

xml 复制代码
<service
    android:name=".LockTaskMonitor"
    android:enabled="true"
    android:exported="false"
    android:persistent="true" />
10.2.3 问题: 状态栏未正确隐藏

症状 :

进入Lock Task后状态栏仍然可见

解决方案:

  1. 配置Lock Task Features:
java 复制代码
// 确保未启用SYSTEM_INFO
DevicePolicyManager dpm = (DevicePolicyManager)
    getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName admin = getAdminComponent();

int features = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
dpm.setLockTaskFeatures(admin, features);
  1. 使用沉浸式模式:
java 复制代码
@Override
protected void onResume() {
    super.onResume();

    // 启动Lock Task
    startLockTask();

    // 隐藏系统UI
    hideSystemUI();
}

private void hideSystemUI() {
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        | View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
  1. 监听UI可见性变化:
java 复制代码
decorView.setOnSystemUiVisibilityChangeListener(visibility -> {
    if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
        // 系统UI重新出现,再次隐藏
        decorView.postDelayed(this::hideSystemUI, 100);
    }
});
10.2.4 问题: 无法退出Lock Task

症状 :

调用stopLockTask()后没有效果

解决方案:

  1. 检查调用者UID:
java 复制代码
// 只有启动Lock Task的Activity才能停止
// 确保在正确的Activity中调用stopLockTask()

public class MainActivity extends Activity {
    private boolean mIsLockTaskStarter = false;

    @Override
    protected void onResume() {
        super.onResume();
        startLockTask();
        mIsLockTaskStarter = true; // 标记为启动者
    }

    public void exitLockTask() {
        if (mIsLockTaskStarter) {
            stopLockTask();
        } else {
            // 不是启动者,需要finish Activity
            finish();
        }
    }
}
  1. 检查是否为根任务:
java 复制代码
// 如果是根任务且是LOCKED模式,需要特殊处理
ActivityManager am = (ActivityManager)
    getSystemService(Context.ACTIVITY_SERVICE);

int mode = am.getLockTaskModeState();
if (mode == ActivityManager.LOCK_TASK_MODE_LOCKED) {
    // LOCKED模式可能需要DeviceOwner权限才能退出
    // 或者需要先移除白名单
}
  1. 使用ADB强制退出 (调试用):
bash 复制代码
# 查看当前Lock Task状态
adb shell dumpsys activity | grep "mLockTaskMode"

# 强制停止Lock Task
adb shell am stop-lock-task-mode

# 或者结束应用
adb shell am force-stop com.example.app

10.3 测试指南

10.3.1 单元测试
java 复制代码
@RunWith(AndroidJUnit4.class)
public class LockTaskControllerTest {
    private Context mContext;
    private ActivityManager mActivityManager;
    private DevicePolicyManager mDevicePolicyManager;

    @Before
    public void setUp() {
        mContext = InstrumentationRegistry.getInstrumentation()
            .getTargetContext();
        mActivityManager = (ActivityManager)
            mContext.getSystemService(Context.ACTIVITY_SERVICE);
        mDevicePolicyManager = (DevicePolicyManager)
            mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
    }

    @Test
    public void testLockTaskMode() {
        // 测试Lock Task状态
        int initialState = mActivityManager.getLockTaskModeState();
        assertEquals(ActivityManager.LOCK_TASK_MODE_NONE, initialState);

        // 启动Lock Task (需要合适的权限)
        // ...

        // 验证状态变更
        // ...
    }

    @Test
    public void testLockTaskPackages() {
        ComponentName admin = getAdminComponent();
        String testPackage = "com.example.test";

        // 设置白名单
        mDevicePolicyManager.setLockTaskPackages(
            admin, new String[]{testPackage});

        // 验证
        String[] packages = mDevicePolicyManager.getLockTaskPackages(admin);
        assertEquals(1, packages.length);
        assertEquals(testPackage, packages[0]);

        // 检查权限
        boolean isPermitted = mDevicePolicyManager
            .isLockTaskPermitted(testPackage);
        assertTrue(isPermitted);
    }

    @Test
    public void testLockTaskFeatures() {
        ComponentName admin = getAdminComponent();
        int features = DevicePolicyManager.LOCK_TASK_FEATURE_HOME
                     | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;

        // 设置功能
        mDevicePolicyManager.setLockTaskFeatures(admin, features);

        // 验证
        int retrievedFeatures = mDevicePolicyManager
            .getLockTaskFeatures(admin);
        assertEquals(features, retrievedFeatures);
    }
}
10.3.2 集成测试
java 复制代码
@RunWith(AndroidJUnit4.class)
@LargeTest
public class LockTaskIntegrationTest {
    @Rule
    public ActivityScenarioRule<MainActivity> mActivityRule =
        new ActivityScenarioRule<>(MainActivity.class);

    @Test
    public void testLockTaskLifecycle() {
        mActivityRule.getScenario().onActivity(activity -> {
            // 1. 启动Lock Task
            activity.startLockTask();

            // 2. 验证状态
            ActivityManager am = (ActivityManager)
                activity.getSystemService(Context.ACTIVITY_SERVICE);
            int mode = am.getLockTaskModeState();
            assertNotEquals(ActivityManager.LOCK_TASK_MODE_NONE, mode);

            // 3. 模拟用户操作
            // ...

            // 4. 停止Lock Task
            activity.stopLockTask();

            // 5. 验证退出
            mode = am.getLockTaskModeState();
            assertEquals(ActivityManager.LOCK_TASK_MODE_NONE, mode);
        });
    }

    @Test
    public void testLockTaskWithMultipleActivities() {
        mActivityRule.getScenario().onActivity(mainActivity -> {
            mainActivity.startLockTask();

            // 启动第二个Activity
            Intent intent = new Intent(mainActivity, SecondActivity.class);
            mainActivity.startActivity(intent);

            // 验证Lock Task仍然激活
            // ...
        });
    }

    @Test
    public void testLockTaskRestrictions() {
        mActivityRule.getScenario().onActivity(activity -> {
            activity.startLockTask();

            // 尝试启动系统设置 (应该被阻止)
            Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
            try {
                activity.startActivity(settingsIntent);
                fail("Should not be able to start Settings in Lock Task");
            } catch (SecurityException e) {
                // Expected
            }
        });
    }
}
10.3.3 CTS测试

Android CTS (Compatibility Test Suite) 包含Lock Task相关测试:

测试文件位置:

  • /cts/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.kt
  • /cts/tests/framework/base/windowmanager/src/android/server/wm/other/LockTaskModeTests.java

运行CTS测试:

bash 复制代码
# 运行所有Lock Task测试
adb shell am instrument -w -e class android.server.wm.other.LockTaskModeTests \
  com.android.cts.windowmanager/androidx.test.runner.AndroidJUnitRunner

# 运行特定测试
adb shell am instrument -w \
  -e class android.server.wm.other.LockTaskModeTests#testLockTaskMode \
  com.android.cts.windowmanager/androidx.test.runner.AndroidJUnitRunner

10.4 调试技巧

10.4.1 日志分析
bash 复制代码
# 查看Lock Task相关日志
adb logcat -s ActivityTaskManager:* LockTaskController:* DevicePolicyManager:*

# 使用ProtoLog (需要开启)
adb shell cmd window logging enable-text WM_DEBUG_LOCKTASK
adb logcat -s WindowManager:*

# 查看详细状态
adb shell dumpsys activity | grep -A 20 "LockTask"
10.4.2 实时监控
bash 复制代码
# 监控Lock Task模式变化
watch -n 1 'adb shell dumpsys activity | grep "mLockTaskMode"'

# 监控任务栈
adb shell dumpsys activity activities | grep "TaskRecord"
10.4.3 开发者工具
java 复制代码
public class LockTaskDebugHelper {
    /**
     * 打印Lock Task详细状态
     */
    public static void dumpLockTaskState(Context context) {
        ActivityManager am = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);
        DevicePolicyManager dpm = (DevicePolicyManager)
            context.getSystemService(Context.DEVICE_POLICY_SERVICE);

        Log.d(TAG, "========== Lock Task State ==========");
        Log.d(TAG, "Mode: " + getLockTaskModeString(
            am.getLockTaskModeState()));
        Log.d(TAG, "Is In Lock Task: " + am.isInLockTaskMode());

        ComponentName admin = getAdminComponent(context);
        if (admin != null) {
            String[] packages = dpm.getLockTaskPackages(admin);
            Log.d(TAG, "Whitelist Packages: " + Arrays.toString(packages));

            int features = dpm.getLockTaskFeatures(admin);
            Log.d(TAG, "Features: " + getLockTaskFeaturesString(features));
        }

        Log.d(TAG, "=====================================");
    }

    private static String getLockTaskModeString(int mode) {
        switch (mode) {
            case ActivityManager.LOCK_TASK_MODE_NONE:
                return "NONE";
            case ActivityManager.LOCK_TASK_MODE_LOCKED:
                return "LOCKED";
            case ActivityManager.LOCK_TASK_MODE_PINNED:
                return "PINNED";
            default:
                return "UNKNOWN(" + mode + ")";
        }
    }

    private static String getLockTaskFeaturesString(int features) {
        if (features == DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
            return "NONE";
        }

        List<String> featureList = new ArrayList<>();
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO) != 0) {
            featureList.add("SYSTEM_INFO");
        }
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS) != 0) {
            featureList.add("NOTIFICATIONS");
        }
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_HOME) != 0) {
            featureList.add("HOME");
        }
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW) != 0) {
            featureList.add("OVERVIEW");
        }
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS) != 0) {
            featureList.add("GLOBAL_ACTIONS");
        }
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0) {
            featureList.add("KEYGUARD");
        }
        if ((features & DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) != 0) {
            featureList.add("BLOCK_ACTIVITY_START");
        }

        return TextUtils.join(" | ", featureList);
    }
}

10.5 性能考虑

  1. 启动时间: Lock Task启动会有轻微延迟(通常<100ms)
  2. 内存占用: Lock Task本身内存开销很小
  3. 电池影响 : 如果使用FLAG_KEEP_SCREEN_ON,注意电池消耗
  4. 系统资源: LOCKED模式会禁用某些系统服务,可能提升性能

10.6 安全建议

  1. 永远验证调用者: 不要假设调用者是可信的
  2. 审计日志: 记录所有Lock Task操作
  3. 紧急退出: 提供管理员级别的紧急退出机制
  4. 数据保护: 在Lock Task中处理敏感数据时加密
  5. 证书pinning: 如果Lock Task应用需要网络通信,使用证书pinning

总结

Lock Task模式是Android提供的强大企业和专用设备管理功能。通过合理使用,可以实现:

  1. Kiosk模式: 将设备限制为单一或多个特定应用
  2. 企业管理: 通过MDM远程管理设备
  3. 安全隔离: 防止用户访问未授权功能
  4. 临时锁定: Screen Pinning提供灵活的临时锁定

关键要点:

  • Lock Task有三种模式:NONE、LOCKED、PINNED
  • LOCKED模式需要DeviceOwner/ProfileOwner权限
  • 通过Lock Task Features可以细粒度控制系统功能
  • 合理配置可以平衡安全性和用户体验

参考资源:


致敬前辈,砥砺前行!

相关推荐
huohuopro2 小时前
Vue3 Webview 转 Android 虚拟导航栏遮挡问题记录
android·vue
zh_xuan2 小时前
kotlin 挂起函数
android·开发语言·kotlin
贤泽3 小时前
Android 15 Lock Task 模式深度分析(第一部分)
android
zh_xuan3 小时前
kotlin launch函数
android·kotlin·协程·launch
贤泽3 小时前
android 15 AOSP Broadcast 广播机制源码分析
android·aosp
啥都想学点3 小时前
第1天:搭建 flutter 和 Android 环境
android·flutter
huohuopro4 小时前
Android WebView 输入法同步问题解决方案
android
草莓熊Lotso4 小时前
Ext 系列文件系统核心:块、分区、inode 与块组结构详解
android·linux·c语言·开发语言·c++·人工智能·文件
桂花很香,旭很美4 小时前
ADB 安卓实战手册
android·adb