Android6.0 屏幕固定功能详解
可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能。
屏幕固定开启后,屏幕只能固定在设定的Task上的Activity切换。
一、设置固定屏幕
我们先来看SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java的代码,这段代码就是长按home键出现几个Activity,然后按了图钉的那个按钮。在这里直接调用了AMS的startLockTaskModeOnCurrent函数。
@Override
publicvoidonClick(Viewv){
if(v.getId()==R.id.screen_pinning_ok_button||mRequestWindow==v){
try{
ActivityManagerNative.getDefault().startLockTaskModeOnCurrent();
}catch(RemoteExceptione){}
}
clearPrompt();
}
我们来看AMS的startLockTaskModeOnCurrent函数,先调用ActivityStackSupervisor的topRunningActivityLocked获取最前面的Activity,然后调用startLockTaskModeLocked函数,参数是TaskRecord。
publicvoidstartLockTaskModeOnCurrent()throwsRemoteException{
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"startLockTaskModeOnCurrent");
longident=Binder.clearCallingIdentity();
try{
synchronized(this){
ActivityRecordr=mStackSupervisor.topRunningActivityLocked();
if(r!=null){
startLockTaskModeLocked(r.task);
}
}
}finally{
Binder.restoreCallingIdentity(ident);
}
}
我们再来看topRunningActivityLocked函数,先从mFocusedStack中获取最前面的Activity。如果没有再遍历所有的mStacks获取。
ActivityRecordtopRunningActivityLocked(){
finalActivityStackfocusedStack=mFocusedStack;
ActivityRecordr=focusedStack.topRunningActivityLocked(null);
if(r!=null){
returnr;
}
//Returntothehomestack.
finalArrayList<ActivityStack>stacks=mHomeStack.mStacks;
for(intstackNdx=stacks.size()-1;stackNdx>=0;--stackNdx){
finalActivityStackstack=stacks.get(stackNdx);
if(stack!=focusedStack&&isFrontStack(stack)){
r=stack.topRunningActivityLocked(null);
if(r!=null){
returnr;
}
}
}
returnnull;
}
在startLockTaskModeLocked函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,下面我们来看这个函数,我们的task不为null,第一次mLockTaskModeTasks为空,会发送一个LOCK_TASK_START_MSG消息
voidsetLockTaskModeLocked(TaskRecordtask,intlockTaskModeState,Stringreason,
booleanandResume){
if(task==null){
//Takeoutoflocktaskmodeifnecessary
finalTaskRecordlockedTask=getLockedTaskLocked();
if(lockedTask!=null){
removeLockedTaskLocked(lockedTask);
if(!mLockTaskModeTasks.isEmpty()){
//Therearelockedtasksremaining,canonlyfinishthistask,notunlockit.
if(DEBUG_LOCKTASK)Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked:Tasksremaining,can'tunlock");
lockedTask.performClearTaskLocked();
resumeTopActivitiesLocked();
return;
}
}
if(DEBUG_LOCKTASK)Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked:Notaskstounlock.Callers="+Debug.getCallers(4));
return;
}
//Shouldhavealreadybeenchecked,butdoitagain.
if(task.mLockTaskAuth==LOCK_TASK_AUTH_DONT_LOCK){
if(DEBUG_LOCKTASK)Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked:Can'tlockduetoauth");
return;
}
if(isLockTaskModeViolation(task)){
Slog.e(TAG_LOCKTASK,"setLockTaskMode:Attempttostartanunauthorizedlocktask.");
return;
}
if(mLockTaskModeTasks.isEmpty()){
//Firstlocktask.
finalMessagelockTaskMsg=Message.obtain();
lockTaskMsg.obj=task.intent.getComponent().getPackageName();
lockTaskMsg.arg1=task.userId;
lockTaskMsg.what=LOCK_TASK_START_MSG;//发送消息
lockTaskMsg.arg2=lockTaskModeState;
mHandler.sendMessage(lockTaskMsg);
}
//Additormoveittothetop.
if(DEBUG_LOCKTASK)Slog.w(TAG_LOCKTASK,"setLockTaskModeLocked:Lockingto"+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函数。
caseLOCK_TASK_START_MSG:{
//Whenlocktaskstarts,wedisablethestatusbars.
try{
if(mLockTaskNotify==null){
mLockTaskNotify=newLockTaskNotify(mService.mContext);
}
mLockTaskNotify.show(true);
mLockTaskModeState=msg.arg2;
if(getStatusBarService()!=null){
intflags=0;
if(mLockTaskModeState==LOCK_TASK_MODE_LOCKED){
flags=StatusBarManager.DISABLE_MASK
&(~StatusBarManager.DISABLE_BACK);
}elseif(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(RemoteExceptionex){
thrownewRuntimeException(ex);
}
}break;
二、固定屏幕后Activity启动流程
在固定屏幕后,如果我们启动其他TaskRecord的Activity是不能启动的,我们来看下这个原理。在startActivityUncheckedLocked函数中会调用isLockTaskModeViolation函数来判断是否进一步的Activity的启动流程,我们来看下这个函数,调用getLockedTaskLocked来看mLockTaskModeTasks(就是锁定屏幕的那些Task),如果当前的task就是当前正在固定屏幕的task,直接returnfalse就是可以继续启动Activity的流程,而如果不是,我们需要看task的mLockTaskAuth变量。
booleanisLockTaskModeViolation(TaskRecordtask,booleanisNewClearTask){
if(getLockedTaskLocked()==task&&!isNewClearTask){
returnfalse;
}
finalintlockTaskAuth=task.mLockTaskAuth;
switch(lockTaskAuth){
caseLOCK_TASK_AUTH_DONT_LOCK:
return!mLockTaskModeTasks.isEmpty();
caseLOCK_TASK_AUTH_LAUNCHABLE_PRIV:
caseLOCK_TASK_AUTH_LAUNCHABLE:
caseLOCK_TASK_AUTH_WHITELISTED:
returnfalse;
caseLOCK_TASK_AUTH_PINNABLE:
//Pinnabletaskscan'tbelaunchedontopoflocktasktasks.
return!mLockTaskModeTasks.isEmpty();
default:
Slog.w(TAG,"isLockTaskModeViolation:invalidlockTaskAuthvalue="+lockTaskAuth);
returntrue;
}
}
我们再来看TaskRecord的setLockedTaskAuth函数,在新建一个TaskRecord的时候会调用setIntent函数,而setIntent函数又是在TaskRecord的构造函数中调用的。我们来看这个函数mLockTaskAuth的值是根据mLockTaskMode来定的,而mLockTaskMode又是ActivityInfo传入的,这个值是在PKMS解析AndroidManifest.xml的时候构造的,默认就是LOCK_TASK_LAUNCH_MODE_DEFAULT,而当没有白名单mLockTaskAuth最后就是LOCK_TASK_AUTH_PINNABLE。
voidsetLockTaskAuth(){
if(!mPrivileged&&
(mLockTaskMode==LOCK_TASK_LAUNCH_MODE_ALWAYS||
mLockTaskMode==LOCK_TASK_LAUNCH_MODE_NEVER)){
//Non-privappsarenotallowedtousealwaysornever,fallbacktodefault
mLockTaskMode=LOCK_TASK_LAUNCH_MODE_DEFAULT;
}
switch(mLockTaskMode){
caseLOCK_TASK_LAUNCH_MODE_DEFAULT:
mLockTaskAuth=isLockTaskWhitelistedLocked()?
LOCK_TASK_AUTH_WHITELISTED:LOCK_TASK_AUTH_PINNABLE;
break;
caseLOCK_TASK_LAUNCH_MODE_NEVER:
mLockTaskAuth=LOCK_TASK_AUTH_DONT_LOCK;
break;
caseLOCK_TASK_LAUNCH_MODE_ALWAYS:
mLockTaskAuth=LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
break;
caseLOCK_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会被阻止。
caseLOCK_TASK_AUTH_PINNABLE: //Pinnabletaskscan'tbelaunchedontopoflocktasktasks. return!mLockTaskModeTasks.isEmpty();
三、取消固定屏幕
最后我们再来看看取消固定屏幕,取消屏幕会在PhoneStatusBar中取消,但是一定是要有虚拟键,原生就是这么设定的。最后调用了AMS的stopLockTaskModeOnCurrent函数。这个函数主要是调用了stopLockTaskMode函数,这个函数中主要是调用了ActivityStackSupervisor的setLockTaskModeLocked函数,之前在固定屏幕时也是调用了这个函数,但是这里我们仔细看,其第一个参数为null。
publicvoidstopLockTaskMode(){
finalTaskRecordlockTask=mStackSupervisor.getLockedTaskLocked();
if(lockTask==null){
//Ourworkhereisdone.
return;
}
finalintcallingUid=Binder.getCallingUid();
finalintlockTaskUid=lockTask.mLockTaskUid;
//EnsurethesamecallerforstartLockTaskModeandstopLockTaskMode.
//ItispossiblelockTaskModewasstartedbythesystemprocessbecause
//android:lockTaskModeissettoalockingvalueintheapplicationmanifestinsteadof
//theappcallingstartLockTaskMode.Inthiscase{@linkTaskRecord.mLockTaskUid}will
//be0,sowecomparethecallingUidtothe{@linkTaskRecord.effectiveUid}instead.
if(getLockTaskModeState()==ActivityManager.LOCK_TASK_MODE_LOCKED&&
callingUid!=lockTaskUid
&&(lockTaskUid!=0
||(lockTaskUid==0&&callingUid!=lockTask.effectiveUid))){
thrownewSecurityException("Invaliduid,expected"+lockTaskUid
+"callingUid="+callingUid+"effectiveUid="+lockTask.effectiveUid);
}
longident=Binder.clearCallingIdentity();
try{
Log.d(TAG,"stopLockTaskMode");
//Stoplocktask
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已经为空了。
voidsetLockTaskModeLocked(TaskRecordtask,intlockTaskModeState,Stringreason,
booleanandResume){
if(task==null){
//Takeoutoflocktaskmodeifnecessary
finalTaskRecordlockedTask=getLockedTaskLocked();
if(lockedTask!=null){
removeLockedTaskLocked(lockedTask);
if(!mLockTaskModeTasks.isEmpty()){
//Therearelockedtasksremaining,canonlyfinishthistask,notunlockit.
if(DEBUG_LOCKTASK)Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked:Tasksremaining,can'tunlock");
lockedTask.performClearTaskLocked();
resumeTopActivitiesLocked();
return;
}
}
if(DEBUG_LOCKTASK)Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked:Notaskstounlock.Callers="+Debug.getCallers(4));
return;
}
四、没有虚拟键如何取消屏幕固定
前面说过如果没有虚拟键就不能取消屏幕固定了,我们说下几种方式
1.使用am命令amtasklockstop可以调用am的stopLockTaskMode函数
2.另一种我们可以在Activity.java中修改代码,比较长按返回键调用AMS的stopLockTaskMode方法,下面就是实现,Activity本身提供了stopLockTask就是调用了AMS的stopLockTaskMode方法
publicbooleanonKeyLongPress(intkeyCode,KeyEventevent){
if(keyCode==KeyEvent.KEYCODE_BACK){
stopLockTask();
}
returnfalse;
}
以上所述是小编给大家介绍的Android6.0屏幕固定功能详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!