English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
제목을 보고도 무엇인지 모를 사람들이 많을 수 있으므로, 먼저 설명드리겠습니다. android6.0 설정->안전->스크린 고정이 활성화된 후에도 홈 키를 길게 눌러 가장 최근의 몇 가지 Activity를 선택할 수 있으며, 풀 버튼을 누르면 스크린 고정 기능이 시작됩니다.
스크린 고정이 활성화된 후, 스크린은 설정된 Task에만 Activity를 전환할 수 있습니다.
1. 스크린 고정 설정
우리가 SystemUI를 보자./src/com/android/systemui/recents/ScreenPinningRequest.java의 코드, 이 코드는 홈 키를 길게 눌러 몇 가지 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); } } } Binder.restoreCallingIdentity(ident); } }
topRunningActivityLocked 함수를 다시 보겠습니다. 먼저 mFocusedStack에서 가장 앞의 Activity를 가져옵니다. 그런 다음 모든 mStacks를 순회하여 가져옵니다.
ActivityRecord topRunningActivityLocked() { final ActivityStack focusedStack = mFocusedStack; ActivityRecord r = focusedStack.topRunningActivityLocked(null); if (r != null) { return r; } // 홈 스택으로 돌아가십시오. 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) { // 필요한 경우 잠금 작업 모드에서 빼내기 final TaskRecord lockedTask = getLockedTaskLocked(); if (lockedTask != null) { removeLockedTaskLocked(lockedTask); if (!mLockTaskModeTasks.isEmpty()) { // 잠금된 작업이 남아 있으므로, 이 작업을 완료할 수만 있지만 잠금을 해제할 수 없습니다. 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()) { // 최초 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);//Add it to mLockModeTasks if (task.mLockTaskUid == -1) { task.mLockTaskUid = task.effectiveUid; } if (andResume) { findTaskToMoveToFrontLocked(task, 0, null, reason);//Put the task at the front resumeTopActivitiesLocked();//Display a new Activity } }
We look at the message processing, mainly calling the WMS's disableKeyguard function in the message processing.
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); } } throw new RuntimeException(ex); } }
스크린 고정 후 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이며, 블랙리스트가 없으면 마지막으로 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();
3. 고정 화면 해제
마지막으로 고정 화면을 해제하는 것을 살펴보겠습니다. 화면이 해제되면 PhoneStatusBar에서 해제됩니다. 하지만 항상 가상 키가 있어야 합니다. 원래 설정은 이렇게 되어 있습니다. 마지막으로 AMS의 stopLockTaskModeOnCurrent 함수를 호출했습니다. 이 함수는 주로 stopLockTaskMode 함수를 호출하며, 이 함수는 ActivityStackSupervisor의 setLockTaskModeLocked 함수를 호출합니다. 이전 고정 화면에서도 이 함수를 호출했습니다. 하지만 이번에는 첫 번째 매개변수가 null입니다.
public void stopLockTaskMode() { final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked(); if (lockTask == null) { // 우리의 작업은 이제 완료되었습니다. return; } final int callingUid = Binder.getCallingUid(); final int lockTaskUid = lockTask.mLockTaskUid; // startLockTaskMode와 stopLockTaskMode의 동일한 호출자를 보장합니다. // lockTaskMode가 시스템 프로세스에 의해 시작되었을 가능성이 있습니다. // android:lockTaskMode이 애플리케이션 매니페스트에서 락 값으로 설정되어 있습니다. // 이 앱이 startLockTaskMode를 호출했습니다. 이 경우{@link TaskRecord.mLockTaskUid}가 // 비어 있어서, callingUid를{@link TaskRecord.effectiveUid}와 비교합니다. 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"); // 락 태스크 태스크 중지 synchronized (this) { mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE, "stopLockTask", true); } } Binder.restoreCallingIdentity(ident); } }
이 함수를 보자. 비어 있으면, 현재 getLockedTaskLocked를 호출하여 잠금된 화면의 TaskRecord를 가져오고, 이 TaskRecord를 제거하기 위해 removeLockedTaskLocked를 호출합니다. 그럼에도 불구하고 null이 아니면, resumeTopActivitiesLocked를 호출하여 다음 Activity를 시작합니다(일반적으로 다음 화면의 잠금된 TaskRecord의 Activity입니다).
비어 있으면서도, 우리가 다음에 일반 Activity를 시작할 때는 정상적으로 되돌아옵니다. 왜냐하면 mLockTaskModeTasks가 비어 있기 때문입니다.
void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason, boolean andResume) { if (task == null) { // 필요한 경우 잠금 작업 모드에서 빼내기 final TaskRecord lockedTask = getLockedTaskLocked(); if (lockedTask != null) { removeLockedTaskLocked(lockedTask); if (!mLockTaskModeTasks.isEmpty()) { // 잠금된 작업이 남아 있으므로, 이 작업을 완료할 수만 있지만 잠금을 해제할 수 없습니다. 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; }
4. 가상 키가 없으면 화면 고정을 어떻게 취소하나요?
이전에 가상 키가 없으면 화면 고정을 취소할 수 없다고 말했습니다. 몇 가지 방법을 설명하겠습니다.
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; }
위에서 편집자가 여러분에게 소개한 안드로이드6.0 화면 고정 기능 설명, 많은 도움이 되길 바랍니다. 모든 질문이 있으면 댓글을 남겨 주세요. 편집자는 즉시 답변을 드리겠습니다. 또한,呐喊 튜토리얼 웹사이트에 대한 지원에 감사드립니다!
고지사항: 이 문서의 내용은 인터넷에서 가져왔으며, 저작권자는 모두 소유합니다. 내용은 인터넷 사용자가 자발적으로 기여하고 업로드한 것이며, 이 사이트는 소유권을 가지지 않으며, 인공적으로 편집되지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 위반된 내용을 발견하면, 이메일을 notice#w로 보내 주시기 바랍니다.3codebox.com에 이메일을 보내면 (#을 @으로 변경하십시오) 신고를 하고 관련 증거를 제공하십시오. 사실이 확인되면, 이 사이트는 즉시 위반된 내용을 삭제합니다.