diff options
12 files changed, 603 insertions, 178 deletions
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index f8309bcd3267..1ae0f52ac74e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -270,14 +270,14 @@ public class KeyguardManager { } /** + * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows + * you to disable / reenable the keyguard. + * * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD} * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} * instead; this allows you to seamlessly hide the keyguard as your application * moves in and out of the foreground and does not require that any special * permissions be requested. - * - * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows - * you to disable / reenable the keyguard. */ @Deprecated public class KeyguardLock { @@ -303,7 +303,7 @@ public class KeyguardManager { @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void disableKeyguard() { try { - mWM.disableKeyguard(mToken, mTag); + mWM.disableKeyguard(mToken, mTag, mContext.getUserId()); } catch (RemoteException ex) { } } @@ -322,16 +322,17 @@ public class KeyguardManager { @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard() { try { - mWM.reenableKeyguard(mToken); + mWM.reenableKeyguard(mToken, mContext.getUserId()); } catch (RemoteException ex) { } } } /** - * @deprecated Use {@link KeyguardDismissCallback} * Callback passed to {@link KeyguardManager#exitKeyguardSecurely} to notify * caller of result. + * + * @deprecated Use {@link KeyguardDismissCallback} */ @Deprecated public interface OnKeyguardExitResult { @@ -380,12 +381,6 @@ public class KeyguardManager { } /** - * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD} - * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} - * instead; this allows you to seamlessly hide the keyguard as your application - * moves in and out of the foreground and does not require that any special - * permissions be requested. - * * Enables you to lock or unlock the keyguard. Get an instance of this class by * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}. @@ -394,6 +389,12 @@ public class KeyguardManager { * * @return A {@link KeyguardLock} handle to use to disable and reenable the * keyguard. + * + * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD} + * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} + * instead; this allows you to seamlessly hide the keyguard as your application + * moves in and out of the foreground and does not require that any special + * permissions be requested. */ @Deprecated public KeyguardLock newKeyguardLock(String tag) { @@ -430,13 +431,12 @@ public class KeyguardManager { } /** - * @deprecated Use {@link #isKeyguardLocked()} instead. - * * If keyguard screen is showing or in restricted key input mode (i.e. in * keyguard password emergency screen). When in such mode, certain keys, * such as the Home key and the right soft keys, don't work. * * @return true if in keyguard restricted input mode. + * @deprecated Use {@link #isKeyguardLocked()} instead. */ public boolean inKeyguardRestrictedInputMode() { return isKeyguardLocked(); @@ -588,12 +588,6 @@ public class KeyguardManager { } /** - * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD} - * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} - * instead; this allows you to seamlessly hide the keyguard as your application - * moves in and out of the foreground and does not require that any special - * permissions be requested. - * * Exit the keyguard securely. The use case for this api is that, after * disabling the keyguard, your app, which was granted permission to * disable the keyguard and show a limited amount of information deemed @@ -606,6 +600,12 @@ public class KeyguardManager { * @param callback Lets you know whether the operation was successful and * it is safe to launch anything that would normally be considered safe * once the user has gotten past the keyguard. + + * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD} + * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} + * instead; this allows you to seamlessly hide the keyguard as your application + * moves in and out of the foreground and does not require that any special + * permissions be requested. */ @Deprecated @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD) diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 308a00020db9..85a6faab0749 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -116,8 +116,10 @@ interface IWindowManager void stopFreezingScreen(); // these require DISABLE_KEYGUARD permission - void disableKeyguard(IBinder token, String tag); - void reenableKeyguard(IBinder token); + /** @deprecated use Activity.setShowWhenLocked instead. */ + void disableKeyguard(IBinder token, String tag, int userId); + /** @deprecated use Activity.setShowWhenLocked instead. */ + void reenableKeyguard(IBinder token, int userId); void exitKeyguardSecurely(IOnKeyguardExitResult callback); boolean isKeyguardLocked(); boolean isKeyguardSecure(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 3b9110d31c6f..880197836392 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -406,24 +406,6 @@ public class KeyguardViewMediator extends SystemUI { } @Override - public void onPhoneStateChanged(int phoneState) { - synchronized (KeyguardViewMediator.this) { - if (TelephonyManager.CALL_STATE_IDLE == phoneState // call ending - && !mDeviceInteractive // screen off - && mExternallyEnabled) { // not disabled by any app - - // note: this is a way to gracefully reenable the keyguard when the call - // ends and the screen is off without always reenabling the keyguard - // each time the screen turns off while in call (and having an occasional ugly - // flicker while turning back on the screen and disabling the keyguard again). - if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the " - + "keyguard is showing"); - doKeyguardLocked(null); - } - } - } - - @Override public void onClockVisibilityChanged() { adjustStatusBarLocked(); } @@ -1313,15 +1295,7 @@ public class KeyguardViewMediator extends SystemUI { if (!mExternallyEnabled) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); - // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes - // for an occasional ugly flicker in this situation: - // 1) receive a call with the screen on (no keyguard) or make a call - // 2) screen times out - // 3) user hits key to turn screen back on - // instead, we reenable the keyguard when we know the screen is off and the call - // ends (see the broadcast receiver below) - // TODO: clean this up when we have better support at the window manager level - // for apps that wish to be on top of the keyguard + mNeedToReshowWhenReenabled = true; return; } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index adfa8d546166..ea8c7922d831 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -112,6 +112,7 @@ import com.android.server.locksettings.LockSettingsStorage.PersistentData; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken; +import com.android.server.wm.WindowManagerInternal; import libcore.util.HexEncoding; @@ -1870,6 +1871,7 @@ public class LockSettingsService extends ILockSettings.Stub { DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); dpm.reportPasswordChanged(userId); + LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId); }); } diff --git a/services/core/java/com/android/server/utils/UserTokenWatcher.java b/services/core/java/com/android/server/utils/UserTokenWatcher.java new file mode 100644 index 000000000000..a3e58f802845 --- /dev/null +++ b/services/core/java/com/android/server/utils/UserTokenWatcher.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import android.annotation.UserIdInt; +import android.os.Handler; +import android.os.IBinder; +import android.os.TokenWatcher; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.IndentingPrintWriter; + +import java.io.PrintWriter; + +/** + * Multi-user aware {@link TokenWatcher}. + * + * {@link UserTokenWatcher} is thread-safe. + */ +public final class UserTokenWatcher { + + private final Callback mCallback; + private final Handler mHandler; + private final String mTag; + + @GuardedBy("mWatchers") + private final SparseArray<TokenWatcher> mWatchers = new SparseArray<>(1); + + public UserTokenWatcher(Callback callback, Handler handler, String tag) { + mCallback = callback; + mHandler = handler; + mTag = tag; + } + + /** + * Record that this token has been acquired for the given user. When acquire is called, and + * the user's count goes from 0 to 1, the acquired callback is called on the given + * handler. + * + * Note that the same {@code token} can only be acquired once per user. If this + * {@code token} has already been acquired for the given user, no action is taken. The first + * subsequent call to {@link #release} will release this {@code token} + * immediately. + * + * @param token An IBinder object. + * @param tag A string used by the {@link #dump} method for debugging, + * to see who has references. + * @param userId A user id + */ + public void acquire(IBinder token, String tag, @UserIdInt int userId) { + synchronized (mWatchers) { + TokenWatcher watcher = mWatchers.get(userId); + if (watcher == null) { + watcher = new InnerTokenWatcher(userId, mHandler, mTag); + mWatchers.put(userId, watcher); + } + watcher.acquire(token, tag); + } + } + + /** + * Record that this token has been released for the given user. When release is called, and + * the user's count goes from 1 to 0, the released callback is called on the given + * handler. + * + * @param token An IBinder object. + * @param userId A user id + */ + public void release(IBinder token, @UserIdInt int userId) { + synchronized (mWatchers) { + TokenWatcher watcher = mWatchers.get(userId); + if (watcher != null) { + watcher.release(token); + } + } + } + + /** + * Returns whether the given user has any registered tokens that have not been cleaned up. + * + * @return true, if the given user has registered tokens. + */ + public boolean isAcquired(@UserIdInt int userId) { + synchronized (mWatchers) { + TokenWatcher watcher = mWatchers.get(userId); + return watcher != null && watcher.isAcquired(); + } + } + + /** + * Dumps the current state. + */ + public void dump(PrintWriter pw) { + synchronized (mWatchers) { + for (int i = 0; i < mWatchers.size(); i++) { + int userId = mWatchers.keyAt(i); + TokenWatcher watcher = mWatchers.valueAt(i); + if (watcher.isAcquired()) { + pw.print("User "); + pw.print(userId); + pw.println(":"); + watcher.dump(new IndentingPrintWriter(pw, " ")); + } + } + } + } + + /** + * Callback for {@link UserTokenWatcher}. + */ + public interface Callback { + + /** + * Reports that the first token has been acquired for the given user. + */ + void acquired(@UserIdInt int userId); + + /** + * Reports that the last token has been release for the given user. + */ + void released(@UserIdInt int userId); + } + + private final class InnerTokenWatcher extends TokenWatcher { + private final int mUserId; + + private InnerTokenWatcher(int userId, Handler handler, String tag) { + super(handler, tag); + this.mUserId = userId; + } + + @Override + public void acquired() { + // We MUST NOT hold any locks while invoking the callbacks. + mCallback.acquired(mUserId); + } + + @Override + public void released() { + // We MUST NOT hold any locks while invoking the callbacks. + mCallback.released(mUserId); + + synchronized (mWatchers) { + final TokenWatcher watcher = mWatchers.get(mUserId); + if (watcher != null && !watcher.isAcquired()) { + mWatchers.remove(mUserId); + } + } + } + } +} diff --git a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java index 4a20f1a039ef..c9173a6430cc 100644 --- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java +++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java @@ -19,113 +19,142 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.Handler; import android.os.IBinder; -import android.os.Message; -import android.os.RemoteException; -import android.os.TokenWatcher; -import android.util.Log; -import android.util.Pair; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManagerInternal; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.utils.UserTokenWatcher; +import com.android.server.wm.LockTaskController.LockTaskToken; -public class KeyguardDisableHandler extends Handler { +class KeyguardDisableHandler { private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardDisableHandler" : TAG_WM; - private static final int ALLOW_DISABLE_YES = 1; - private static final int ALLOW_DISABLE_NO = 0; - private static final int ALLOW_DISABLE_UNKNOWN = -1; // check with DevicePolicyManager - private int mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; // sync'd by mKeyguardTokenWatcher + private final UserTokenWatcher mAppTokenWatcher; + private final UserTokenWatcher mSystemTokenWatcher; - // Message.what constants - static final int KEYGUARD_DISABLE = 1; - static final int KEYGUARD_REENABLE = 2; - static final int KEYGUARD_POLICY_CHANGED = 3; + private int mCurrentUser = UserHandle.USER_SYSTEM; + private Injector mInjector; - final Context mContext; - final WindowManagerPolicy mPolicy; - KeyguardTokenWatcher mKeyguardTokenWatcher; + @VisibleForTesting + KeyguardDisableHandler(Injector injector, Handler handler) { + mInjector = injector; + mAppTokenWatcher = new UserTokenWatcher(mCallback, handler, TAG); + mSystemTokenWatcher = new UserTokenWatcher(mCallback, handler, TAG); + } - public KeyguardDisableHandler(final Context context, final WindowManagerPolicy policy) { - mContext = context; - mPolicy = policy; + public void setCurrentUser(int user) { + synchronized (this) { + mCurrentUser = user; + updateKeyguardEnabledLocked(UserHandle.USER_ALL); + } } - @SuppressWarnings("unchecked") - @Override - public void handleMessage(Message msg) { - if (mKeyguardTokenWatcher == null) { - mKeyguardTokenWatcher = new KeyguardTokenWatcher(this); + void updateKeyguardEnabled(int userId) { + synchronized (this) { + updateKeyguardEnabledLocked(userId); } + } - switch (msg.what) { - case KEYGUARD_DISABLE: - final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj; - mKeyguardTokenWatcher.acquire(pair.first, pair.second); - break; - - case KEYGUARD_REENABLE: - mKeyguardTokenWatcher.release((IBinder)msg.obj); - break; - - case KEYGUARD_POLICY_CHANGED: - mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN; - if (mKeyguardTokenWatcher.isAcquired()) { - // If we are currently disabled we need to know if the keyguard - // should be re-enabled, so determine the allow state immediately. - mKeyguardTokenWatcher.updateAllowState(); - if (mAllowDisableKeyguard != ALLOW_DISABLE_YES) { - mPolicy.enableKeyguard(true); - } - } else { - // lazily evaluate this next time we're asked to disable keyguard - mPolicy.enableKeyguard(true); - } - break; + private void updateKeyguardEnabledLocked(int userId) { + if (mCurrentUser == userId || userId == UserHandle.USER_ALL) { + mInjector.enableKeyguard(shouldKeyguardBeEnabled(mCurrentUser)); } } - class KeyguardTokenWatcher extends TokenWatcher { + void disableKeyguard(IBinder token, String tag, int callingUid, int userId) { + UserTokenWatcher watcherForCaller = watcherForCallingUid(token, callingUid); + watcherForCaller.acquire(token, tag, mInjector.getProfileParentId(userId)); + } - public KeyguardTokenWatcher(final Handler handler) { - super(handler, TAG); - } + void reenableKeyguard(IBinder token, int callingUid, int userId) { + UserTokenWatcher watcherForCaller = watcherForCallingUid(token, callingUid); + watcherForCaller.release(token, mInjector.getProfileParentId(userId)); + } - public void updateAllowState() { - // We fail safe and prevent disabling keyguard in the unlikely event this gets - // called before DevicePolicyManagerService has started. - DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( - Context.DEVICE_POLICY_SERVICE); - if (dpm != null) { - try { - mAllowDisableKeyguard = dpm.getPasswordQuality(null, - ActivityManager.getService().getCurrentUser().id) - == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ? - ALLOW_DISABLE_YES : ALLOW_DISABLE_NO; - } catch (RemoteException re) { - // Nothing much we can do - } - } + private UserTokenWatcher watcherForCallingUid(IBinder token, int callingUid) { + if (Process.isApplicationUid(callingUid)) { + return mAppTokenWatcher; + } else if (callingUid == Process.SYSTEM_UID && token instanceof LockTaskToken) { + // We allow the lock task token here as a legacy case, because it enforces its own + // security guarantees. + // NOTE: DO NOT add new usages of this API in system server. It is deprecated and + // easily misused. + return mSystemTokenWatcher; + } else { + throw new UnsupportedOperationException("Only apps can use the KeyguardLock API"); } + } + + private boolean shouldKeyguardBeEnabled(int userId) { + final boolean dpmRequiresPassword = mInjector.dpmRequiresPassword(mCurrentUser); + final boolean keyguardSecure = mInjector.isKeyguardSecure(mCurrentUser); + + final boolean allowedFromApps = !dpmRequiresPassword && !keyguardSecure; + // The system can disable the keyguard for lock task mode even if the keyguard is secure, + // because it enforces its own security guarantees. + final boolean allowedFromSystem = !dpmRequiresPassword; + final boolean shouldBeDisabled = allowedFromApps && mAppTokenWatcher.isAcquired(userId) + || allowedFromSystem && mSystemTokenWatcher.isAcquired(userId); + return !shouldBeDisabled; + } + + // Callback happens on mHandler thread. + private final UserTokenWatcher.Callback mCallback = new UserTokenWatcher.Callback() { @Override - public void acquired() { - if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) { - updateAllowState(); - } - if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) { - mPolicy.enableKeyguard(false); - } else { - Log.v(TAG, "Not disabling keyguard since device policy is enforced"); - } + public void acquired(int userId) { + updateKeyguardEnabled(userId); } @Override - public void released() { - mPolicy.enableKeyguard(true); + public void released(int userId) { + updateKeyguardEnabled(userId); } + }; + + static KeyguardDisableHandler create(Context context, WindowManagerPolicy policy, + Handler handler) { + final UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class); + return new KeyguardDisableHandler(new Injector() { + @Override + public boolean dpmRequiresPassword(int userId) { + DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + return dpm == null || dpm.getPasswordQuality(null, userId) + != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + } + + @Override + public boolean isKeyguardSecure(int userId) { + return policy.isKeyguardSecure(userId); + } + + @Override + public int getProfileParentId(int userId) { + return userManager.getProfileParentId(userId); + } + + @Override + public void enableKeyguard(boolean enabled) { + policy.enableKeyguard(enabled); + } + }, handler); + } + + interface Injector { + boolean dpmRequiresPassword(int userId); + + boolean isKeyguardSecure(int userId); + + int getProfileParentId(int userId); + + void enableKeyguard(boolean enabled); } } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 41d0777d1c78..090451f08188 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -54,6 +54,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.provider.Settings; import android.telecom.TelecomManager; import android.util.Pair; @@ -126,7 +127,7 @@ public class LockTaskController { /** Tag used for disabling of keyguard */ private static final String LOCK_TASK_TAG = "Lock-to-App"; - private final IBinder mToken = new Binder(); + private final IBinder mToken = new LockTaskToken(); private final ActivityStackSupervisor mSupervisor; private final Context mContext; @@ -180,6 +181,17 @@ public class LockTaskController { */ private final Handler mHandler; + /** + * Stores the user for which we're trying to dismiss the keyguard and then subsequently + * disable it. + * + * Tracking this ensures we don't mistakenly disable the keyguard if we've stopped trying to + * between the dismiss request and when it succeeds. + * + * Must only be accessed from the Handler thread. + */ + private int mPendingDisableFromDismiss = UserHandle.USER_NULL; + LockTaskController(Context context, ActivityStackSupervisor supervisor, Handler handler) { mContext = context; @@ -739,16 +751,18 @@ public class LockTaskController { * Should only be called on the handler thread to avoid race. */ private void setKeyguardState(int lockTaskModeState, int userId) { + mPendingDisableFromDismiss = UserHandle.USER_NULL; if (lockTaskModeState == LOCK_TASK_MODE_NONE) { - mWindowManager.reenableKeyguard(mToken); + mWindowManager.reenableKeyguard(mToken, userId); } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) { if (isKeyguardAllowed(userId)) { - mWindowManager.reenableKeyguard(mToken); + mWindowManager.reenableKeyguard(mToken, userId); } else { // If keyguard is not secure and it is locked, dismiss the keyguard before // disabling it, which avoids the platform to think the keyguard is still on. if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) { + mPendingDisableFromDismiss = userId; mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() { @Override public void onDismissError() throws RemoteException { @@ -758,7 +772,13 @@ public class LockTaskController { @Override public void onDismissSucceeded() throws RemoteException { mHandler.post( - () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG)); + () -> { + if (mPendingDisableFromDismiss == userId) { + mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, + userId); + mPendingDisableFromDismiss = UserHandle.USER_NULL; + } + }); } @Override @@ -767,12 +787,12 @@ public class LockTaskController { } }, null); } else { - mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG); + mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, userId); } } } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED - mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG); + mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG, userId); } } @@ -897,4 +917,10 @@ public class LockTaskController { default: return "unknown=" + mLockTaskModeState; } } + + /** Marker class for the token used to disable keyguard. */ + static class LockTaskToken extends Binder { + private LockTaskToken() { + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index e83b8634925e..cf722a21b1ba 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -335,6 +335,11 @@ public abstract class WindowManagerInternal { public abstract void registerAppTransitionListener(AppTransitionListener listener); /** + * Reports that the password for the given user has changed. + */ + public abstract void reportPasswordChanged(int userId); + + /** * Retrieves a height of input method window for given display. */ public abstract int getInputMethodWindowVisibleHeight(int displayId); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 39a8465a31b3..a119a2337af7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; +import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.StatusBarManager.DISABLE_MASK; @@ -71,7 +72,6 @@ import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN; import static com.android.server.LockGuard.INDEX_WINDOW; import static com.android.server.LockGuard.installLock; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static com.android.server.wm.KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; @@ -180,7 +180,6 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.MergedConfiguration; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -233,6 +232,7 @@ import com.android.internal.policy.IShortcutService; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.LatencyTracker; +import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.AnimationThread; @@ -394,7 +394,7 @@ public class WindowManagerService extends IWindowManager.Stub public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED: - mKeyguardDisableHandler.sendEmptyMessage(KEYGUARD_POLICY_CHANGED); + mKeyguardDisableHandler.updateKeyguardEnabled(getSendingUserId()); break; } } @@ -959,7 +959,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); - mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); + mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH); mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); @@ -1038,7 +1038,7 @@ public class WindowManagerService extends IWindowManager.Stub IntentFilter filter = new IntentFilter(); // Track changes to DevicePolicyManager state so we can enable/disable keyguard. filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - mContext.registerReceiver(mBroadcastReceiver, filter); + mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); mLatencyTracker = LatencyTracker.getInstance(context); @@ -2800,45 +2800,38 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void disableKeyguard(IBinder token, String tag) { + public void disableKeyguard(IBinder token, String tag, int userId) { + userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false /* allowAll */, ALLOW_FULL_ONLY, "disableKeyguard", null); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } - // If this isn't coming from the system then don't allow disabling the lockscreen - // to bypass security. - if (Binder.getCallingUid() != SYSTEM_UID && isKeyguardSecure()) { - Log.d(TAG_WM, "current mode is SecurityMode, ignore disableKeyguard"); - return; - } - - // If this isn't coming from the current profiles, ignore it. - if (!isCurrentProfileLocked(UserHandle.getCallingUserId())) { - Log.d(TAG_WM, "non-current profiles, ignore disableKeyguard"); - return; - } - - if (token == null) { - throw new IllegalArgumentException("token == null"); + final int callingUid = Binder.getCallingUid(); + final long origIdentity = Binder.clearCallingIdentity(); + try { + mKeyguardDisableHandler.disableKeyguard(token, tag, callingUid, userId); + } finally { + Binder.restoreCallingIdentity(origIdentity); } - - mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( - KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag))); } @Override - public void reenableKeyguard(IBinder token) { + public void reenableKeyguard(IBinder token, int userId) { + userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false /* allowAll */, ALLOW_FULL_ONLY, "reenableKeyguard", null); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } - - if (token == null) { - throw new IllegalArgumentException("token == null"); + Preconditions.checkNotNull(token, "token is null"); + final int callingUid = Binder.getCallingUid(); + final long origIdentity = Binder.clearCallingIdentity(); + try { + mKeyguardDisableHandler.reenableKeyguard(token, callingUid, userId); + } finally { + Binder.restoreCallingIdentity(origIdentity); } - - mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage( - KeyguardDisableHandler.KEYGUARD_REENABLE, token)); } /** @@ -3122,11 +3115,7 @@ public class WindowManagerService extends IWindowManager.Stub mCurrentUserId = newUserId; mCurrentProfileIds = currentProfileIds; mPolicy.setCurrentUserLw(newUserId); - - // If keyguard was disabled, re-enable it - // TODO: Keep track of keyguardEnabled state per user and use here... - // e.g. enabled = mKeyguardDisableHandler.getEnabledStateForUser(newUserId); - mPolicy.enableKeyguard(true); + mKeyguardDisableHandler.setCurrentUser(newUserId); // Hide windows that should not be seen by the new user. mRoot.switchUser(); @@ -7099,6 +7088,11 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void reportPasswordChanged(int userId) { + mKeyguardDisableHandler.updateKeyguardEnabled(userId); + } + + @Override public int getInputMethodWindowVisibleHeight(int displayId) { synchronized (mGlobalLock) { final DisplayContent dc = mRoot.getDisplayContent(displayId); diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java new file mode 100644 index 000000000000..ce22788f7cb9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.Process.NFC_UID; +import static android.os.Process.SYSTEM_UID; +import static android.os.UserHandle.USER_ALL; +import static android.os.UserHandle.USER_SYSTEM; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.UserHandle; +import android.util.SparseBooleanArray; + +import com.android.server.wm.LockTaskController.LockTaskToken; + +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.Constructor; + +public class KeyguardDisableHandlerTest { + + private KeyguardDisableHandler mKeyguardDisable; + + private boolean mKeyguardEnabled; + private SparseBooleanArray mKeyguardSecure = new SparseBooleanArray(); + private SparseBooleanArray mDpmRequiresPassword = new SparseBooleanArray(); + + @Before + public void setUp() throws Exception { + mKeyguardEnabled = true; + + mKeyguardDisable = new KeyguardDisableHandler(new KeyguardDisableHandler.Injector() { + @Override + public boolean dpmRequiresPassword(int userId) { + return mDpmRequiresPassword.get(userId); + } + + @Override + public boolean isKeyguardSecure(int userId) { + return mKeyguardSecure.get(userId); + } + + @Override + public int getProfileParentId(int userId) { + return userId; + } + + @Override + public void enableKeyguard(boolean enabled) { + mKeyguardEnabled = enabled; + } + }, mock(Handler.class)) { + @Override + public void disableKeyguard(IBinder token, String tag, int callingUid, int userId) { + super.disableKeyguard(token, tag, callingUid, userId); + // In the actual code, the update is posted to the handler thread. Eagerly update + // here to simplify the test. + updateKeyguardEnabled(userId); + } + + @Override + public void reenableKeyguard(IBinder token, int callingUid, int userId) { + super.reenableKeyguard(token, callingUid, userId); + // In the actual code, the update is posted to the handler thread. Eagerly update + // here to simplify the test. + updateKeyguardEnabled(userId); + } + }; + } + + @Test + public void starts_enabled() { + assertTrue(mKeyguardEnabled); + mKeyguardDisable.updateKeyguardEnabled(USER_ALL); + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromApp_disables() { + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM); + assertFalse(mKeyguardEnabled); + } + + @Test + public void disable_fromApp_secondaryUser_disables() { + mKeyguardDisable.setCurrentUser(1); + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", + UserHandle.getUid(1, FIRST_APPLICATION_UID), 1); + assertFalse(mKeyguardEnabled); + } + + @Test + public void disable_fromSystem_LockTask_disables() { + mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM); + assertFalse(mKeyguardEnabled); + } + + @Test + public void disable_fromSystem_genericToken_fails() { + try { + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", SYSTEM_UID, USER_SYSTEM); + fail("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + assertThat(e.getMessage(), containsString("Only apps can use the KeyguardLock API")); + } + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromNonApp_genericToken_fails() { + try { + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", NFC_UID, USER_SYSTEM); + fail("Expected exception not thrown"); + } catch (UnsupportedOperationException e) { + assertThat(e.getMessage(), containsString("Only apps can use the KeyguardLock API")); + } + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromApp_secure_staysEnabled() { + configureIsSecure(true, USER_SYSTEM); + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM); + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromApp_dpmRequiresPassword_staysEnabled() { + configureDpmRequiresPassword(true, USER_SYSTEM); + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM); + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromSystem_LockTask_secure_disables() { + configureIsSecure(true, USER_SYSTEM); + mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM); + assertFalse(mKeyguardEnabled); + } + + @Test + public void disable_fromSystem_LockTask_requiresDpm_staysEnabled() { + configureDpmRequiresPassword(true, USER_SYSTEM); + mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM); + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromApp_thenSecure_reenables() { + mKeyguardDisable.disableKeyguard(new Binder(), "Tag", FIRST_APPLICATION_UID, USER_SYSTEM); + configureIsSecure(true, USER_SYSTEM); + assertTrue(mKeyguardEnabled); + } + + @Test + public void disable_fromSystem_LockTask_thenRequiresDpm_reenables() { + mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM); + configureDpmRequiresPassword(true, USER_SYSTEM); + assertTrue(mKeyguardEnabled); + } + + @Test + public void user_switch_to_enabledUser_applies_enabled() { + mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", SYSTEM_UID, USER_SYSTEM); + assertFalse("test setup failed", mKeyguardEnabled); + mKeyguardDisable.setCurrentUser(1); + assertTrue(mKeyguardEnabled); + } + + @Test + public void user_switch_to_disabledUser_applies_disabled() { + mKeyguardDisable.disableKeyguard(createLockTaskToken(), "Tag", + SYSTEM_UID, 1); + assertTrue("test setup failed", mKeyguardEnabled); + mKeyguardDisable.setCurrentUser(1); + assertFalse(mKeyguardEnabled); + } + + private void configureIsSecure(boolean secure, int userId) { + mKeyguardSecure.put(userId, secure); + mKeyguardDisable.updateKeyguardEnabled(userId); + } + + private void configureDpmRequiresPassword(boolean requiresPassword, int userId) { + mDpmRequiresPassword.put(userId, requiresPassword); + mKeyguardDisable.updateKeyguardEnabled(userId); + } + + private LockTaskToken createLockTaskToken() { + try { + final Constructor<LockTaskToken> constructor = + LockTaskToken.class.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index 33e6063bbb97..9d061e1084d6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -558,19 +558,20 @@ public class LockTaskControllerTest { mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN keyguard should be disabled - verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString()); + verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID)); // WHEN keyguard is enabled for lock task mode mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD); // THEN keyguard should be enabled - verify(mWindowManager).reenableKeyguard(any(IBinder.class)); + verify(mWindowManager).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID)); // WHEN keyguard is disabled again for lock task mode mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE); // THEN keyguard should be disabled - verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString()); + verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString(), + eq(TEST_USER_ID)); } @Test @@ -651,7 +652,7 @@ public class LockTaskControllerTest { private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception { // THEN the keyguard should have been disabled - verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString()); + verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID)); // THEN the status bar should have been disabled verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class), eq(mContext.getPackageName())); @@ -666,7 +667,7 @@ public class LockTaskControllerTest { private void verifyLockTaskStopped(VerificationMode mode) throws Exception { // THEN the keyguard should have been disabled - verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class)); + verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID)); // THEN the status bar should have been disabled verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE), any(IBinder.class), eq(mContext.getPackageName())); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 14312cf84693..369a002fa273 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -20,6 +20,7 @@ import android.content.res.Configuration; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.test.suitebuilder.annotation.SmallTest; import android.view.IWindowManager; import junit.framework.TestCase; @@ -107,7 +108,7 @@ public class WindowManagerPermissionTests extends TestCase { public void testDISABLE_KEYGUARD() { Binder token = new Binder(); try { - mWm.disableKeyguard(token, "foo"); + mWm.disableKeyguard(token, "foo", UserHandle.myUserId()); fail("IWindowManager.disableKeyguard did not throw SecurityException as" + " expected"); } catch (SecurityException e) { @@ -117,7 +118,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.reenableKeyguard(token); + mWm.reenableKeyguard(token, UserHandle.myUserId()); fail("IWindowManager.reenableKeyguard did not throw SecurityException as" + " expected"); } catch (SecurityException e) { |