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屏幕固定功能详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!