Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ea7c4f5 authored by Brad Lassey's avatar Brad Lassey
Browse files

Implement device lock state listener

Bug: 296195355
Test: new CTS test
Flag: android.app.device_unlock_listener
Change-Id: I672954ac7bf5d428612c4d6f11c0ee3895b02ab0
parent 13d32366
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -6221,6 +6221,7 @@ package android.app {
  }
  public class KeyguardManager {
    method @FlaggedApi("android.app.device_unlock_listener") @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addDeviceLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.DeviceLockedStateListener);
    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);
@@ -6230,10 +6231,15 @@ package android.app {
    method public boolean isKeyguardLocked();
    method public boolean isKeyguardSecure();
    method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String);
    method @FlaggedApi("android.app.device_unlock_listener") @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeDeviceLockedStateListener(@NonNull android.app.KeyguardManager.DeviceLockedStateListener);
    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);
  }
  @FlaggedApi("android.app.device_unlock_listener") @java.lang.FunctionalInterface public static interface KeyguardManager.DeviceLockedStateListener {
    method public void onDeviceLockedStateChanged(boolean);
  }
  public abstract static class KeyguardManager.KeyguardDismissCallback {
    ctor public KeyguardManager.KeyguardDismissCallback();
    method public void onDismissCancelled();
+96 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.app;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,7 +53,9 @@ import android.view.IOnKeyguardExitResult;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IDeviceLockedStateListener;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.util.Preconditions;
@@ -253,6 +256,26 @@ public class KeyguardManager {
    private final ArrayMap<KeyguardLockedStateListener, Executor>
            mKeyguardLockedStateListeners = new ArrayMap<>();

    private final IDeviceLockedStateListener mIDeviceLockedStateListener =
            new IDeviceLockedStateListener.Stub() {
                @Override
                public void onDeviceLockedStateChanged(boolean isDeviceLocked) {
                    if (!Flags.deviceUnlockListener()) {
                        return;
                    }
                    synchronized (mDeviceLockedStateListeners) {
                        mDeviceLockedStateListeners.forEach((listener, executor) -> {
                            executor.execute(
                                    () -> listener.onDeviceLockedStateChanged(isDeviceLocked));
                        });
                    }
                }
            };

    @GuardedBy("mDeviceLockedStateListeners")
    private final ArrayMap<DeviceLockedStateListener, Executor>
            mDeviceLockedStateListeners = 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
@@ -1370,4 +1393,77 @@ public class KeyguardManager {
            }
        }
    }


    /**
     * Listener for device locked state changes.
     */
    @FunctionalInterface
    @FlaggedApi(Flags.FLAG_DEVICE_UNLOCK_LISTENER)
    public interface DeviceLockedStateListener {
        /**
         * Callback function that executes when the device locked state changes.
         */
        void onDeviceLockedStateChanged(boolean isDeviceLocked);
    }


    /**
     * Registers a listener to execute when the device locked state changes.
     *
     * @param executor The {@link Executor} where the {@code listener} will be invoked
     * @param listener The listener to add to receive device locked state changes.
     *
     * @see #isDeviceLocked()
     * @see #removeDeviceLockedStateListener(DeviceLockedStateListener)
     */
    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
    @FlaggedApi(Flags.FLAG_DEVICE_UNLOCK_LISTENER)
    public void addDeviceLockedStateListener(@NonNull @CallbackExecutor Executor executor,
            @NonNull DeviceLockedStateListener listener) {
        if (!Flags.deviceUnlockListener()) {
            return;
        }

        synchronized (mDeviceLockedStateListeners) {
            mDeviceLockedStateListeners.put(listener, executor);
            if (mDeviceLockedStateListeners.size() > 1) {
                return;
            }
            try {
                mTrustManager.registerDeviceLockedStateListener(mIDeviceLockedStateListener,
                        mContext.getDeviceId());
            } catch (RemoteException re) {
                Log.d(TAG, "TrustManager service died", re);
            }
        }
    }

    /**
     * Unregisters a listener that executes when the device locked state changes.
     *
     * @param listener The listener to remove.
     *
     * @see #isDeviceLocked()
     * @see #addDeviceLockedStateListener(Executor, DeviceLockedStateListener)
     */
    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
    @FlaggedApi(Flags.FLAG_DEVICE_UNLOCK_LISTENER)
    public void removeDeviceLockedStateListener(@NonNull DeviceLockedStateListener listener) {
        if (!Flags.deviceUnlockListener()) {
            return;
        }

        synchronized (mDeviceLockedStateListeners) {
            mDeviceLockedStateListeners.remove(listener);
            if (!mDeviceLockedStateListeners.isEmpty()) {
                return;
            }
            try {
                mTrustManager.unregisterDeviceLockedStateListener(mIDeviceLockedStateListener);
            } catch (RemoteException re) {
                Log.d(TAG, "TrustManager service died", re);
            }
        }
    }
}
+10 −0
Original line number Diff line number Diff line
package: "android.app"
container: "system"

flag {
     namespace: "wallet_integration"
     name: "device_unlock_listener"
     is_exported: true
     description: "Enable listener API for device unlock."
     bug: "296195355"
}
 No newline at end of file
+5 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.app.trust;

import android.app.trust.ITrustListener;
import android.hardware.biometrics.BiometricSourceType;
import com.android.internal.policy.IDeviceLockedStateListener;

/**
 * System private API to comunicate with trust service.
@@ -43,4 +44,8 @@ interface ITrustManager {
    boolean isActiveUnlockRunning(int userId);
    @EnforcePermission("ACCESS_FINE_LOCATION")
    boolean isInSignificantPlace();
    @EnforcePermission("SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE")
    void registerDeviceLockedStateListener(in IDeviceLockedStateListener listener, int deviceId);
    @EnforcePermission("SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE")
    void unregisterDeviceLockedStateListener(in IDeviceLockedStateListener listener);
}
+31 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.ArrayMap;

import com.android.internal.policy.IDeviceLockedStateListener;

import java.util.ArrayList;
import java.util.List;

@@ -258,6 +260,35 @@ public class TrustManager {
        }
    }

    /**
     * Registers a listener for device lock state events.
     *
     * Requires the {@link android.Manifest.permission#SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE}
     * permission.
     */
    public void registerDeviceLockedStateListener(final IDeviceLockedStateListener listener,
            int deviceId) {
        try {
            mService.registerDeviceLockedStateListener(listener, deviceId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Unregisters a listener for device lock state events.
     *
     * Requires the {@link android.Manifest.permission#SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE}
     * permission.
     */
    public void unregisterDeviceLockedStateListener(final IDeviceLockedStateListener listener) {
        try {
            mService.unregisterDeviceLockedStateListener(listener);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @return whether {@param userId} has enabled and configured trust agents. Ignores short-term
     * unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
Loading