Add role guarded APIs for subscribing to keyguard locked state in KeyguardManager.

Bug: 216630470
CTS-Coverage-Bug: 220371902
Test: manual
Test: atest CtsAppTestCases:KeyguardLockedStateApiTest
Ignore-AOSP-First: (for packages/Shell/AndroidManifest.xml) Permission
not yet publicly announced.

Change-Id: I1f44ec6799b0f74791613389ce28aadf84c9fe5c
This commit is contained in:
Xiaowen Lei
2022-02-14 21:26:31 +00:00
committed by Bryce Lee
parent c80e1dabc1
commit a650c2c4b7
7 changed files with 166 additions and 11 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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 -->

View File

@@ -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,