locksettings: Add AIDL weaver HAL support

Test: atest FrameworksServicesTests:com.android.server.locksettings.LockSettingsServiceTests
Test: Set PIN, enter incorrect PIN, enter correct PIN
Test: Remove HIDL HAL service and flash new image
Test: enter incorrect PIN, enter correct PIN.
Test: Set a new PIN and retest all without HIDL service
Bug: 182976659
Change-Id: Icf720b5ab4e93675cf72ab441fd4af86a46f90e9
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1e1d610..5c00452 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -161,6 +161,7 @@
         "android.hardware.tv.cec-V1-java",
         "android.hardware.tv.hdmi-V1-java",
         "android.hardware.weaver-V1.0-java",
+        "android.hardware.weaver-V2-java",
         "android.hardware.biometrics.face-V1.0-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
         "android.hardware.oemlock-V1.0-java",
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index acd7cc1..66ce429 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -24,13 +24,14 @@
 import android.app.admin.PasswordMetrics;
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.hardware.weaver.V1_0.IWeaver;
-import android.hardware.weaver.V1_0.WeaverConfig;
-import android.hardware.weaver.V1_0.WeaverReadResponse;
-import android.hardware.weaver.V1_0.WeaverReadStatus;
-import android.hardware.weaver.V1_0.WeaverStatus;
+import android.hardware.weaver.IWeaver;
+import android.hardware.weaver.WeaverConfig;
+import android.hardware.weaver.WeaverReadResponse;
+import android.hardware.weaver.WeaverReadStatus;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.security.GateKeeper;
@@ -60,7 +61,6 @@
 import java.nio.ByteBuffer;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -464,37 +464,67 @@
     }
 
     @VisibleForTesting
-    protected IWeaver getWeaverService() throws RemoteException {
+    protected android.hardware.weaver.V1_0.IWeaver getWeaverHidlService() throws RemoteException {
         try {
-            return IWeaver.getService(/* retry */ true);
+            return android.hardware.weaver.V1_0.IWeaver.getService(/* retry */ true);
         } catch (NoSuchElementException e) {
-            Slog.i(TAG, "Device does not support weaver");
             return null;
         }
     }
 
+    private IWeaver getWeaverService() {
+        // Try to get the AIDL service first
+        try {
+            IWeaver aidlWeaver = IWeaver.Stub.asInterface(
+                    ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
+            if (aidlWeaver != null) {
+                Slog.i(TAG, "Using AIDL weaver service");
+                return aidlWeaver;
+            }
+        } catch (SecurityException e) {
+            Slog.w(TAG, "Does not have permissions to get AIDL weaver service");
+        }
+
+        // If the AIDL service can't be found, look for the HIDL service
+        try {
+            android.hardware.weaver.V1_0.IWeaver hidlWeaver = getWeaverHidlService();
+            if (hidlWeaver != null) {
+                Slog.i(TAG, "Using HIDL weaver service");
+                return new WeaverHidlWrapper(hidlWeaver);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to get HIDL weaver service.", e);
+        }
+        Slog.w(TAG, "Device does not support weaver");
+        return null;
+    }
+
     public synchronized void initWeaverService() {
         if (mWeaver != null) {
             return;
         }
-        try {
-            mWeaverConfig = null;
-            mWeaver = getWeaverService();
-            if (mWeaver != null) {
-                mWeaver.getConfig((int status, WeaverConfig config) -> {
-                    if (status == WeaverStatus.OK && config.slots > 0) {
-                        mWeaverConfig = config;
-                    } else {
-                        Slog.e(TAG, "Failed to get weaver config, status " + status
-                                + " slots: " + config.slots);
-                        mWeaver = null;
-                    }
-                });
-                mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to get weaver service", e);
+
+        IWeaver weaver = getWeaverService();
+        if (weaver == null) {
+            return;
         }
+
+        // Get the config
+        WeaverConfig weaverConfig = null;
+        try {
+            weaverConfig = weaver.getConfig();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to get weaver config", e);
+        }
+        if (weaverConfig == null || weaverConfig.slots <= 0) {
+            Slog.e(TAG, "Failed to initialize weaver config");
+            return;
+        }
+
+        mWeaver = weaver;
+        mWeaverConfig = weaverConfig;
+        mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots());
+        Slog.i(TAG, "Weaver service initialized");
     }
 
     private synchronized boolean isWeaverAvailable() {
@@ -525,19 +555,31 @@
             value = secureRandom(mWeaverConfig.valueSize);
         }
         try {
-            int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
-            if (writeStatus != WeaverStatus.OK) {
-                Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
-                return null;
-            }
+            mWeaver.write(slot, key, value);
         } catch (RemoteException e) {
-            Slog.e(TAG, "weaver write failed", e);
+            Slog.e(TAG, "weaver write binder call failed, slot: " + slot, e);
+            return null;
+        } catch (ServiceSpecificException e) {
+            Slog.e(TAG, "weaver write failed, slot: " + slot, e);
             return null;
         }
         return value;
     }
 
     /**
+     * Create a VerifyCredentialResponse from a timeout base on the WeaverReadResponse.
+     * This checks the received timeout(long) to make sure it sure it fits in an int before
+     * using it. If it doesn't fit, we use Integer.MAX_VALUE.
+     */
+    private static VerifyCredentialResponse responseFromTimeout(WeaverReadResponse response) {
+        int timeout =
+                response.timeout > Integer.MAX_VALUE || response.timeout < 0
+                ? Integer.MAX_VALUE
+                : (int) response.timeout;
+        return VerifyCredentialResponse.fromTimeout(timeout);
+    }
+
+    /**
      * Verify the supplied key against a weaver slot, returning a response indicating whether
      * the verification is successful, throttled or failed. If successful, the bound secret
      * is also returned.
@@ -551,46 +593,39 @@
         } else if (key.length != mWeaverConfig.keySize) {
             throw new IllegalArgumentException("Invalid key size for weaver");
         }
-        final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
+        final WeaverReadResponse readResponse;
         try {
-            mWeaver.read(slot, toByteArrayList(key),
-                    (int status, WeaverReadResponse readResponse) -> {
-                    switch (status) {
-                        case WeaverReadStatus.OK:
-                            response[0] = new VerifyCredentialResponse.Builder().setGatekeeperHAT(
-                                    fromByteArrayList(readResponse.value)).build();
-                            break;
-                        case WeaverReadStatus.THROTTLE:
-                            response[0] = VerifyCredentialResponse
-                                    .fromTimeout(readResponse.timeout);
-                            Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
-                            break;
-                        case WeaverReadStatus.INCORRECT_KEY:
-                            if (readResponse.timeout == 0) {
-                                response[0] = VerifyCredentialResponse.ERROR;
-                                Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
-                            } else {
-                                response[0] = VerifyCredentialResponse
-                                        .fromTimeout(readResponse.timeout);
-                                Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
-                                        + slot);
-                            }
-                            break;
-                        case WeaverReadStatus.FAILED:
-                            response[0] = VerifyCredentialResponse.ERROR;
-                            Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
-                            break;
-                        default:
-                            response[0] = VerifyCredentialResponse.ERROR;
-                            Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
-                            break;
-                    }
-                });
+            readResponse = mWeaver.read(slot, key);
         } catch (RemoteException e) {
-            response[0] = VerifyCredentialResponse.ERROR;
             Slog.e(TAG, "weaver read failed, slot: " + slot, e);
+            return VerifyCredentialResponse.ERROR;
         }
-        return response[0];
+
+        switch (readResponse.status) {
+            case WeaverReadStatus.OK:
+                return new VerifyCredentialResponse.Builder()
+                                      .setGatekeeperHAT(readResponse.value)
+                                      .build();
+            case WeaverReadStatus.THROTTLE:
+                Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
+                return responseFromTimeout(readResponse);
+            case WeaverReadStatus.INCORRECT_KEY:
+                if (readResponse.timeout == 0) {
+                    Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
+                    return VerifyCredentialResponse.ERROR;
+                } else {
+                    Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
+                    return responseFromTimeout(readResponse);
+                }
+            case WeaverReadStatus.FAILED:
+                Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
+                return VerifyCredentialResponse.ERROR;
+            default:
+                Slog.e(TAG,
+                        "weaver read unknown status " + readResponse.status
+                                + ", slot: " + slot);
+                return VerifyCredentialResponse.ERROR;
+        }
     }
 
     public void removeUser(IGateKeeperService gatekeeper, int userId) {
@@ -1660,22 +1695,6 @@
 
     native long nativeSidFromPasswordHandle(byte[] handle);
 
-    protected static ArrayList<Byte> toByteArrayList(byte[] data) {
-        ArrayList<Byte> result = new ArrayList<Byte>(data.length);
-        for (int i = 0; i < data.length; i++) {
-            result.add(data[i]);
-        }
-        return result;
-    }
-
-    protected static byte[] fromByteArrayList(ArrayList<Byte> data) {
-        byte[] result = new byte[data.size()];
-        for (int i = 0; i < data.size(); i++) {
-            result[i] = data.get(i);
-        }
-        return result;
-    }
-
     @VisibleForTesting
     static byte[] bytesToHex(byte[] bytes) {
         return HexEncoding.encodeToString(bytes).getBytes();
diff --git a/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java b/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java
new file mode 100644
index 0000000..9d93c3d
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java
@@ -0,0 +1,143 @@
+/*
+ * 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.server.locksettings;
+
+import android.hardware.weaver.V1_0.IWeaver;
+import android.hardware.weaver.V1_0.WeaverConfig;
+import android.hardware.weaver.V1_0.WeaverReadResponse;
+import android.hardware.weaver.V1_0.WeaverReadStatus;
+import android.hardware.weaver.V1_0.WeaverStatus;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Implement the AIDL IWeaver interface wrapping the HIDL implementation
+ */
+class WeaverHidlWrapper implements android.hardware.weaver.IWeaver {
+    private static final String TAG = "WeaverHidlWrapper";
+    private final IWeaver mImpl;
+
+    WeaverHidlWrapper(IWeaver impl) {
+        mImpl = impl;
+    }
+
+    private static ArrayList<Byte> toByteArrayList(byte[] data) {
+        ArrayList<Byte> result = new ArrayList<Byte>(data.length);
+        for (int i = 0; i < data.length; i++) {
+            result.add(data[i]);
+        }
+        return result;
+    }
+
+    private static byte[] fromByteArrayList(ArrayList<Byte> data) {
+        byte[] result = new byte[data.size()];
+        for (int i = 0; i < data.size(); i++) {
+            result[i] = data.get(i);
+        }
+        return result;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        // We do not require the interface hash as the client.
+        throw new UnsupportedOperationException(
+            "WeaverHidlWrapper does not support getInterfaceHash");
+    }
+    @Override
+    public int getInterfaceVersion() {
+        // Supports only V2 which is at feature parity.
+        return 2;
+    }
+    @Override
+    public android.os.IBinder asBinder() {
+        // There is no IHwBinder to IBinder. Not required as the client.
+        throw new UnsupportedOperationException("WeaverHidlWrapper does not support asBinder");
+    }
+
+    @Override
+    public android.hardware.weaver.WeaverConfig getConfig() throws RemoteException {
+        final WeaverConfig[] res = new WeaverConfig[1];
+        mImpl.getConfig((int status, WeaverConfig config) -> {
+            if (status == WeaverStatus.OK && config.slots > 0) {
+                res[0] = config;
+            } else {
+                res[0] = null;
+                Slog.e(TAG,
+                        "Failed to get HIDL weaver config. status: " + status
+                                + ", slots: " + config.slots);
+            }
+        });
+
+        if (res[0] == null) {
+            return null;
+        }
+        android.hardware.weaver.WeaverConfig config = new android.hardware.weaver.WeaverConfig();
+        config.slots = res[0].slots;
+        config.keySize = res[0].keySize;
+        config.valueSize = res[0].valueSize;
+        return config;
+    }
+
+    @Override
+    public android.hardware.weaver.WeaverReadResponse read(int slotId, byte[] key)
+            throws RemoteException {
+        final WeaverReadResponse[] res = new WeaverReadResponse[1];
+        final int[] status = new int[1];
+        mImpl.read(
+                slotId, toByteArrayList(key), (int inStatus, WeaverReadResponse readResponse) -> {
+                    status[0] = inStatus;
+                    res[0] = readResponse;
+                });
+
+        android.hardware.weaver.WeaverReadResponse aidlRes =
+                new android.hardware.weaver.WeaverReadResponse();
+        switch (status[0]) {
+            case WeaverReadStatus.OK:
+                aidlRes.status = android.hardware.weaver.WeaverReadStatus.OK;
+                break;
+            case WeaverReadStatus.THROTTLE:
+                aidlRes.status = android.hardware.weaver.WeaverReadStatus.THROTTLE;
+                break;
+            case WeaverReadStatus.INCORRECT_KEY:
+                aidlRes.status = android.hardware.weaver.WeaverReadStatus.INCORRECT_KEY;
+                break;
+            case WeaverReadStatus.FAILED:
+                aidlRes.status = android.hardware.weaver.WeaverReadStatus.FAILED;
+                break;
+            default:
+                aidlRes.status = android.hardware.weaver.WeaverReadStatus.FAILED;
+                break;
+        }
+        if (res[0] != null) {
+            aidlRes.timeout = res[0].timeout;
+            aidlRes.value = fromByteArrayList(res[0].value);
+        }
+        return aidlRes;
+    }
+
+    @Override
+    public void write(int slotId, byte[] key, byte[] value) throws RemoteException {
+        int writeStatus = mImpl.write(slotId, toByteArrayList(key), toByteArrayList(value));
+        if (writeStatus != WeaverStatus.OK) {
+            throw new ServiceSpecificException(
+                android.hardware.weaver.IWeaver.STATUS_FAILED, "Failed IWeaver.write call");
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index 8cb18a8..a40cb06 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -113,7 +113,7 @@
     }
 
     @Override
-    protected IWeaver getWeaverService() throws RemoteException {
+    protected IWeaver getWeaverHidlService() throws RemoteException {
         return mWeaverService;
     }