diff options
| -rw-r--r-- | core/api/current.txt | 7 | ||||
| -rw-r--r-- | core/java/android/app/KeyguardManager.java | 84 | ||||
| -rw-r--r-- | core/java/android/view/IWindowManager.aidl | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl | 21 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 7 | ||||
| -rw-r--r-- | packages/Shell/AndroidManifest.xml | 3 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/WindowManagerService.java | 51 |
7 files changed, 166 insertions, 11 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 56415ec14564..753e04666247 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -189,6 +189,7 @@ package android { field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES"; field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE"; field public static final String STATUS_BAR = "android.permission.STATUS_BAR"; + field public static final String SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE = "android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"; field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW"; field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR"; field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT"; @@ -5677,6 +5678,7 @@ package android.app { } public class KeyguardManager { + method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener); method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence); method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult); method @Deprecated public boolean inKeyguardRestrictedInputMode(); @@ -5685,6 +5687,7 @@ package android.app { method public boolean isKeyguardLocked(); method public boolean isKeyguardSecure(); method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String); + method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener); method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback); } @@ -5700,6 +5703,10 @@ package android.app { method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard(); } + @java.lang.FunctionalInterface public static interface KeyguardManager.KeyguardLockedStateListener { + method public void onKeyguardLockedStateChanged(boolean); + } + @Deprecated public static interface KeyguardManager.OnKeyguardExitResult { method @Deprecated public void onKeyguardExitResult(boolean); } diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 99100003e5b3..87ba197d8052 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -52,6 +52,7 @@ import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.util.Preconditions; import com.android.internal.widget.IWeakEscrowTokenActivatedListener; import com.android.internal.widget.IWeakEscrowTokenRemovedListener; @@ -183,6 +184,10 @@ public class KeyguardManager { }) @interface LockTypes {} + // TODO(b/220379118): register only one binder listener and keep a map of listener to executor. + private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener> + mKeyguardLockedStateListeners = new ArrayMap<>(); + /** * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics * if enrolled) for the current user of the device. The caller is expected to launch this @@ -534,7 +539,7 @@ public class KeyguardManager { /** * Return whether the keyguard is currently locked. * - * @return true if keyguard is locked. + * @return {@code true} if the keyguard is locked. */ public boolean isKeyguardLocked() { try { @@ -550,7 +555,7 @@ public class KeyguardManager { * * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states. * - * @return true if a PIN, pattern or password is set or a SIM card is locked. + * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked. */ public boolean isKeyguardSecure() { try { @@ -565,7 +570,7 @@ public class KeyguardManager { * 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. + * @return {@code true} if in keyguard restricted input mode. * @deprecated Use {@link #isKeyguardLocked()} instead. */ public boolean inKeyguardRestrictedInputMode() { @@ -576,7 +581,7 @@ public class KeyguardManager { * Returns whether the device is currently locked and requires a PIN, pattern or * password to unlock. * - * @return true if unlocking the device currently requires a PIN, pattern or + * @return {@code true} if unlocking the device currently requires a PIN, pattern or * password. */ public boolean isDeviceLocked() { @@ -603,7 +608,7 @@ public class KeyguardManager { * * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure. * - * @return true if a PIN, pattern or password was set. + * @return {@code true} if a PIN, pattern or password was set. */ public boolean isDeviceSecure() { return isDeviceSecure(mContext.getUserId()); @@ -762,7 +767,7 @@ public class KeyguardManager { * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} - * @return true if the password is valid, false otherwise + * @return {@code true} if the password is valid, false otherwise * @hide */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @@ -821,7 +826,7 @@ public class KeyguardManager { * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} - * @return true if the lock is successfully set, false otherwise + * @return {@code true} if the lock is successfully set, false otherwise * @hide */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @@ -903,8 +908,8 @@ public class KeyguardManager { /** * Remove a weak escrow token. * - * @return true if the given handle refers to a valid weak token previously returned from - * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise. + * @return {@code true} if the given handle refers to a valid weak token previously returned + * from {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -944,7 +949,7 @@ public class KeyguardManager { /** * Register the given WeakEscrowTokenRemovedListener. * - * @return true if the listener is registered successfully, return false otherwise. + * @return {@code true} if the listener is registered successfully, return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -982,7 +987,7 @@ public class KeyguardManager { /** * Unregister the given WeakEscrowTokenRemovedListener. * - * @return true if the listener is unregistered successfully, return false otherwise. + * @return {@code true} if the listener is unregistered successfully, return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -1076,4 +1081,61 @@ public class KeyguardManager { throw new IllegalArgumentException("Unknown lock type " + lockType); } } + + /** + * Listener for keyguard locked state changes. + */ + @FunctionalInterface + public interface KeyguardLockedStateListener { + /** + * Callback function that executes when the keyguard locked state changes. + */ + void onKeyguardLockedStateChanged(boolean isKeyguardLocked); + } + + /** + * Registers a listener to execute when the keyguard visibility changes. + * + * @param listener The listener to add to receive keyguard visibility changes. + */ + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull KeyguardLockedStateListener listener) { + synchronized (mKeyguardLockedStateListeners) { + try { + final IKeyguardLockedStateListener innerListener = + new IKeyguardLockedStateListener.Stub() { + @Override + public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) { + executor.execute( + () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked)); + } + }; + mWM.addKeyguardLockedStateListener(innerListener); + mKeyguardLockedStateListeners.put(listener, innerListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregisters a listener that executes when the keyguard visibility changes. + */ + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) { + synchronized (mKeyguardLockedStateListeners) { + IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get( + listener); + if (innerListener == null) { + return; + } + try { + mWM.removeKeyguardLockedStateListener(innerListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mKeyguardLockedStateListeners.remove(listener); + } + } } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ce21086931da..53b842a0f3a2 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -18,6 +18,7 @@ package android.view; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import android.app.IAssistDataReceiver; @@ -199,6 +200,9 @@ interface IWindowManager boolean isKeyguardSecure(int userId); void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message); + void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener); + void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener); + // Requires INTERACT_ACROSS_USERS_FULL permission void setSwitchingUser(boolean switching); diff --git a/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl new file mode 100644 index 000000000000..ee5021918879 --- /dev/null +++ b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 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.internal.policy; + +oneway interface IKeyguardLockedStateListener { + void onKeyguardLockedStateChanged(boolean isKeyguardLocked); +}
\ No newline at end of file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d4c03e412fcb..659b1b0c5bb1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4119,6 +4119,13 @@ <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" android:protectionLevel="internal|preinstalled" /> + <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state. + <p>Protection level: internal|role + <p>Intended for use by ROLE_ASSISTANT only. + --> + <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" + android:protectionLevel="internal|role"/> + <!-- Must be required by a {@link android.service.autofill.AutofillService}, to ensure that only the system can bind to it. <p>Protection level: signature diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index f0b180e5cab6..121f9e58a62a 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -588,6 +588,9 @@ <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" /> <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" /> + <!-- Permission required for CTS test - KeyguardLockedStateApiTest --> + <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" /> + <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/> <!-- Permission required for CTS test - ResourceObserverNativeTest --> diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5b1021eefc0a..709f885db776 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -200,6 +200,7 @@ import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteCallback; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -288,6 +289,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import com.android.internal.policy.KeyInterceptionInfo; import com.android.internal.protolog.ProtoLogImpl; @@ -460,6 +462,10 @@ public class WindowManagerService extends IWindowManager.Stub final private KeyguardDisableHandler mKeyguardDisableHandler; + private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners = + new RemoteCallbackList<>(); + private boolean mDispatchedKeyguardLockedState = false; + // VR Vr2d Display Id. int mVr2dDisplayId = INVALID_DISPLAY; boolean mVrModeEnabled = false; @@ -3029,6 +3035,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void onKeyguardShowingAndNotOccludedChanged() { mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); + dispatchKeyguardLockedStateState(); } @Override @@ -3218,6 +3225,50 @@ public class WindowManagerService extends IWindowManager.Stub } } + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + boolean registered = mKeyguardLockedStateListeners.register(listener); + if (!registered) { + Slog.w(TAG, "Failed to register listener: " + listener); + } + } + + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + mKeyguardLockedStateListeners.unregister(listener); + } + + private void enforceSubscribeToKeyguardLockedStatePermission() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE, + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE + + " permission required to read keyguard visibility"); + } + + private void dispatchKeyguardLockedStateState() { + mH.post(() -> { + final boolean isKeyguardLocked = mPolicy.isKeyguardShowing(); + if (mDispatchedKeyguardLockedState == isKeyguardLocked) { + return; + } + final int n = mKeyguardLockedStateListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged( + isKeyguardLocked); + } catch (RemoteException e) { + // Handled by the RemoteCallbackList. + } + } + mKeyguardLockedStateListeners.finishBroadcast(); + mDispatchedKeyguardLockedState = isKeyguardLocked; + }); + } + @Override public void setSwitchingUser(boolean switching) { if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, |