Android6.0 屏幕固定功能详解

可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能。

屏幕固定开启后,屏幕只能固定在设定的Task上的Activity切换。

一、设置固定屏幕

我们先来看SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java的代码,这段代码就是长按home键出现几个Activity,然后按了图钉的那个按钮。在这里直接调用了AMS的startLockTaskModeOnCurrent函数。

@Override
public void onClick(View v) {
if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
try {
ActivityManagerNative.getDefault().startLockTaskModeOnCurrent();
} catch (RemoteException e) {}
}
clearPrompt();
}

我们来看AMS的startLockTaskModeOnCurrent函数,先调用ActivityStackSupervisor的topRunningActivityLocked获取最前面的Activity,然后调用startLockTaskModeLocked函数,参数是TaskRecord。

public void startLockTaskModeOnCurrent() throws RemoteException {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"startLockTaskModeOnCurrent");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
ActivityRecord r = mStackSupervisor.topRunningActivityLocked();
if (r != null) {
startLockTaskModeLocked(r.task);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}

我们再来看topRunningActivityLocked函数,先从mFocusedStack中获取最前面的Activity。如果没有再遍历所有的mStacks获取。

ActivityRecord topRunningActivityLocked() {
final ActivityStack focusedStack = mFocusedStack;
ActivityRecord r = focusedStack.topRunningActivityLocked(null);
if (r != null) {
return r;
}
// Return to the home stack.
final ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack != focusedStack && isFrontStack(stack)) {
r = stack.topRunningActivityLocked(null);
if (r != null) {
return r;
}
}
}
return null;
}

在startLockTaskModeLocked函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,下面我们来看这个函数,我们的task不为null,第一次mLockTaskModeTasks为空,会发送一个LOCK_TASK_START_MSG消息

void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason,
boolean andResume) {
if (task == null) {
// Take out of lock task mode if necessary
final TaskRecord lockedTask = getLockedTaskLocked();
if (lockedTask != null) {
removeLockedTaskLocked(lockedTask);
if (!mLockTaskModeTasks.isEmpty()) {
// There are locked tasks remaining, can only finish this task, not unlock it.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Tasks remaining, can't unlock");
lockedTask.performClearTaskLocked();
resumeTopActivitiesLocked();
return;
}
}
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: No tasks to unlock. Callers=" + Debug.getCallers(4));
return;
}
// Should have already been checked, but do it again.
if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Can't lock due to auth");
return;
}
if (isLockTaskModeViolation(task)) {
Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
return;
}
if (mLockTaskModeTasks.isEmpty()) {
// First locktask.
final Message lockTaskMsg = Message.obtain();
lockTaskMsg.obj = task.intent.getComponent().getPackageName();
lockTaskMsg.arg1 = task.userId;
lockTaskMsg.what = LOCK_TASK_START_MSG;//发送消息
lockTaskMsg.arg2 = lockTaskModeState;
mHandler.sendMessage(lockTaskMsg);
}
// Add it or move it to the top.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskModeLocked: Locking to " + task +
" Callers=" + Debug.getCallers(4));
mLockTaskModeTasks.remove(task);
mLockTaskModeTasks.add(task);//加入到mLockModeTasks中
if (task.mLockTaskUid == -1) {
task.mLockTaskUid = task.effectiveUid;
}
if (andResume) {
findTaskToMoveToFrontLocked(task, 0, null, reason);//把task放最前面
resumeTopActivitiesLocked();//显示新的Activity
}
}

我们再来看消息处理,在消息处理中主要调用了WMS的disableKeyguard函数。

case LOCK_TASK_START_MSG: {
// When lock task starts, we disable the status bars.
try {
if (mLockTaskNotify == null) {
mLockTaskNotify = new LockTaskNotify(mService.mContext);
}
mLockTaskNotify.show(true);
mLockTaskModeState = msg.arg2;
if (getStatusBarService() != null) {
int flags = 0;
if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK);
} else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK)
& (~StatusBarManager.DISABLE_HOME)
& (~StatusBarManager.DISABLE_RECENT);
}
getStatusBarService().disable(flags, mToken,
mService.mContext.getPackageName());
}
mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(true,
(String)msg.obj, msg.arg1);
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
} break;

二、固定屏幕后Activity启动流程

在固定屏幕后,如果我们启动其他TaskRecord的Activity是不能启动的,我们来看下这个原理。在startActivityUncheckedLocked函数中会调用isLockTaskModeViolation函数来判断是否进一步的Activity的启动流程,我们来看下这个函数,调用getLockedTaskLocked来看mLockTaskModeTasks(就是锁定屏幕的那些Task),如果当前的task就是当前正在固定屏幕的task,直接return false就是可以继续启动Activity的流程,而如果不是,我们需要看task的mLockTaskAuth变量。

boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
if (getLockedTaskLocked() == task && !isNewClearTask) {
return false;
}
final int lockTaskAuth = task.mLockTaskAuth;
switch (lockTaskAuth) {
case LOCK_TASK_AUTH_DONT_LOCK:
return !mLockTaskModeTasks.isEmpty();
case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
case LOCK_TASK_AUTH_LAUNCHABLE:
case LOCK_TASK_AUTH_WHITELISTED:
return false;
case LOCK_TASK_AUTH_PINNABLE:
// Pinnable tasks can't be launched on top of locktask tasks.
return !mLockTaskModeTasks.isEmpty();
default:
Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
return true;
}
}

我们再来看TaskRecord的setLockedTaskAuth函数,在新建一个TaskRecord的时候会调用setIntent函数,而setIntent函数又是在TaskRecord的构造函数中调用的。我们来看这个函数mLockTaskAuth的值是根据mLockTaskMode来定的,而mLockTaskMode又是ActivityInfo传入的,这个值是在PKMS解析AndroidManifest.xml的时候构造的,默认就是LOCK_TASK_LAUNCH_MODE_DEFAULT,而当没有白名单mLockTaskAuth最后就是LOCK_TASK_AUTH_PINNABLE。

void setLockTaskAuth() {
if (!mPrivileged &&
(mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS ||
mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
// Non-priv apps are not allowed to use always or never, fall back to default
mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
}
switch (mLockTaskMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
mLockTaskAuth = isLockTaskWhitelistedLocked() ?
LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
break;
case LOCK_TASK_LAUNCH_MODE_NEVER:
mLockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
break;
case LOCK_TASK_LAUNCH_MODE_ALWAYS:
mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
break;
case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
mLockTaskAuth = isLockTaskWhitelistedLocked() ?
LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
break;
}
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this +
" mLockTaskAuth=" + lockTaskAuthToString());
}

我们再来看isLockTaskModeViolation函数如下代码,现在是task的mLockTaskAuth 是LOCK_TASK_AUTH_PINNABLE,而当前处于固定屏幕,所以mLockTaskModeTasks不为null,最后返回true。那Activity启动流程就不能走下去了,那就是代表启动普通的Activity会被阻止。

case LOCK_TASK_AUTH_PINNABLE:
// Pinnable tasks can't be launched on top of locktask tasks.
return !mLockTaskModeTasks.isEmpty();

三、取消固定屏幕

最后我们再来看看取消固定屏幕,取消屏幕会在PhoneStatusBar中取消,但是一定是要有虚拟键,原生就是这么设定的。最后调用了AMS的stopLockTaskModeOnCurrent函数。这个函数主要是调用了stopLockTaskMode函数,这个函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,之前在固定屏幕时也是调用了这个函数,但是这里我们仔细看,其第一个参数为null。

public void stopLockTaskMode() {
final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked();
if (lockTask == null) {
// Our work here is done.
return;
}
final int callingUid = Binder.getCallingUid();
final int lockTaskUid = lockTask.mLockTaskUid;
// Ensure the same caller for startLockTaskMode and stopLockTaskMode.
// It is possible lockTaskMode was started by the system process because
// android:lockTaskMode is set to a locking value in the application manifest instead of
// the app calling startLockTaskMode. In this case {@link TaskRecord.mLockTaskUid} will
// be 0, so we compare the callingUid to the {@link TaskRecord.effectiveUid} instead.
if (getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED &&
callingUid != lockTaskUid
&& (lockTaskUid != 0
|| (lockTaskUid == 0 && callingUid != lockTask.effectiveUid))) {
throw new SecurityException("Invalid uid, expected " + lockTaskUid
+ " callingUid=" + callingUid + " effectiveUid=" + lockTask.effectiveUid);
}
long ident = Binder.clearCallingIdentity();
try {
Log.d(TAG, "stopLockTaskMode");
// Stop lock task
synchronized (this) {
mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
"stopLockTask", true);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}

我们来看下这个函数,如果为空,现在调用getLockedTaskLocked获取当前固定屏幕的TaskRecord,然后调用removeLockedTaskLocked去除这个TaskRecord,如果还不为null,调用resumeTopActivitiesLocked启动下个Activity(一般也就是下个屏幕锁定的TaskRecord的Activity)。

如果为空了,直接返回。但是在我们下次启动普通的Activity的时候就恢复正常了,因为mLockTaskModeTasks已经为空了。

void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason,
boolean andResume) {
if (task == null) {
// Take out of lock task mode if necessary
final TaskRecord lockedTask = getLockedTaskLocked();
if (lockedTask != null) {
removeLockedTaskLocked(lockedTask);
if (!mLockTaskModeTasks.isEmpty()) {
// There are locked tasks remaining, can only finish this task, not unlock it.
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Tasks remaining, can't unlock");
lockedTask.performClearTaskLocked();
resumeTopActivitiesLocked();
return;
}
}
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: No tasks to unlock. Callers=" + Debug.getCallers(4));
return;
}

四、没有虚拟键如何取消屏幕固定

前面说过如果没有虚拟键就不能取消屏幕固定了,我们说下几种方式

1.使用am命令 am task lock stop可以调用am的stopLockTaskMode函数

2.另一种我们可以在Activity.java中修改代码,比较长按返回键调用AMS的stopLockTaskMode方法,下面就是实现,Activity本身提供了stopLockTask就是调用了AMS的stopLockTaskMode方法

public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
stopLockTask();
}
return false;
}

以上所述是小编给大家介绍的Android6.0 屏幕固定功能详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android手机获取root权限并实现关机重启功能的方法

    本文实例讲述了Android手机获取root权限并实现关机重启功能的方法,是Android程序设计中非常常见的重要功能.现分享给大家,供大家在Android程序开发中参考之用. 具体功能代码如下: /* * 执行命令 * @param command * 1.获取root权限 "chmod 777 "+getPackageCodePath() * 2.关机 reboot -p * 3.重启 reboot */ public static boolean execCmd(String c

  • Android开发实现长按返回键弹出关机框功能

    本文实例讲述了Android开发实现长按返回键弹出关机框功能.分享给大家供大家参考,具体如下: 今天刚好在PhoneWindowManager.java下看,当看到长按Home键的功能时,突然想到是不是可以长按back键来弹出关机框. 有想法就试试呗.当然想法是根据长按home键来的,那么我们应该可以模仿长按Home键来做.经过一番实验,貌似好像可以,拿出来给大家分享一下!!! 先找到PhoneWindowManager.java文件,在framework/base/policy/src/com

  • Android获取设备隐私 忽略6.0权限管理

    一.前言 (1).由于MIUI等部分国产定制系统也有权限管理,没有相关api,故无法判断用户是否允许获取联系人等隐私.在Android 6.0之后,新增权限管理可以通过官方api判断用户的运行状态: (2).我们指定targetSdkVersion为23或者之后我们还需要在运行时请求这些所需的权限.这很重要,因为已经出现了很多开发者把targetSdkVersion飙到了最新,然后发现自己的app疯狂的崩溃,这是由于他们没有实现执行运行时权限请求的代码.当你已经把一个targeting API

  • Android编程实现系统重启与关机的方法

    本文实例讲述了Android编程实现系统重启与关机的方法.分享给大家供大家参考,具体如下: 最近在做个东西,巧合碰到了sharedUserId 的问题,所以收集了一些资料,存存档备份. 安装在设备中的每一个apk文件,Android 给每个 APK 进程分配一个单独的用户空间,其 manifest 中的 userid 就是对应一个 Linux 用户都会被分配到一个属于自己的统一的 Linux 用户 ID,并且为它创建一个沙箱,以防止影响其他应用程序(或者其他应用程序影响它). 用户 ID 在应用

  • Android 6.0开发实现关机菜单添加重启按钮的方法

    本文实例讲述了Android 6.0开发实现关机菜单添加重启按钮的方法.分享给大家供大家参考,具体如下: 修改: /trunk/LINUX/android/frameworks/base/core/res/res/values/config.xml 添加数组name为config_globalActionsList的值 修改: /LINUX/android/frameworks/base/services/core/java/com/android/server/policy/GlobalAct

  • Android实现关机重启的方法分享

    实现系统重启的APK需要system的权限,在AndroidManifest.xml中增加android:sharedUserId="android.uid.system",再修改签名即可: 具体方法参考: 点击打开链接 1.使用PowerManager来实现:代码: 复制代码 代码如下: private void rebootSystem(){      PowerManager pManager=(PowerManager) getSystemService(Context.POW

  • Android系统关机的全流程解析

    在PowerManager的API文档中,给出了一个关机/重启接口: public void reboot (String reason) 对于这个接口的描述很简单,就是几句话. 接口的作用就是重启设备,而且,就算重启成功了也没有返回值. 需要包含REBOOT权限,也就是android.permission.REBOOT 唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null. 一.上层空间 1.frameworks/base/core/java/android/

  • Android实现关机与重启的几种方式(推荐)

    下面我们来探究Android如何实现关机,重启:在Android中这种操作往往需要管理员级别,或者root Android实现的方式如下几种: 默认的SDK并没有提供应用开发者直接的Android系统关机或重启的API接口,一般来讲,实现Android系统的关机或重启,需要较高的权限(系统权限甚至Root权限).所以,在一般的APP中,如果想要实现关机或重启功能,要么是在App中声明系统权限,要么是通过某种"间接"的方式,比如广播或反射,来间接实现系统关机或重启.再者,就是放在源码环境

  • 谈谈Android6.0运行时的权限处理

    运行时权限介绍 Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上, 又新增了运行时权限动态检测,以下权限都需要在运行时判断: 1.身体传感器 2.日历    3.摄像头 4.通讯录 5.地理位置 6.麦克风 7.电话 8.短信 9.存储空间 在 Android 6.0 中,app 如果想要获得某些权限,会在应用中弹出一个对话框,让用户确认是否授予该权限. 具体的截图如下: 这要做的好处就是运行一个 app 时可以拒绝其中的某些权限,防止 app 触及到你的

  • Android6.0动态申请权限所遇到的问题小结

    白天在做SDK23版本的适配,遇到了不少坑,现在抽空记下来,以此为戒. 首先要知道哪些坑,就得先了解一些定义和基本使用方式. 那么先介绍一下动态申请的权限分组情况. 下面的权限组是由谷歌官方定义的,目的是在申请权限时,只要用户允许同一权限组的任意一条权限,那么该组的其他权限也就默认是允许的.不过据高人介绍,在使用时最好是用到哪个权限就具体的请求该权限,因为保不齐哪天谷歌一高兴就把权限组换了甚至删了 group:android.permission-group.CONTACTS permissio

  • Android 关机弹出选择菜单的深入解析

    在Android系统中,长按Power键默认会弹出对话框让你选择"飞行模式","静音","关机"等功能.这些功能对于手机非常适用,但是对于机顶盒产品就没有什么必要了.本文简单介绍一下怎样定制关机界面.我的目标是长按Power键,将会关机,弹出"设备将要关机"选择对话框.如果可以选择"是"关机,和"否"返回系统.弹出对话框的代码位于:frameworks\policies\base\pho

随机推荐