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

Commit e31ca56a authored by Eric Biggers's avatar Eric Biggers
Browse files

Test both AIDL and HIDL Weaver interfaces in locksettings unit tests

Since Android 14, the current Weaver HAL interface has been the AIDL
interface.  But the HIDL interface is still supported too, considering
that devices may upgrade Android without upgrading Weaver.  Until now,
MockWeaverService implemented only the old (HIDL) interface.

Change it to instead implement the current (AIDL) interface, and provide
support for the old (HIDL) interface via an adapter.  Then, split
WeaverBasedSyntheticPasswordTests into two test classes, one that runs
with the HIDL interface and one that runs with the AIDL interface.  This
provides unit test coverage for the code that uses each interface.

Also, for read() with an incorrect key, return INCORRECT_KEY instead of
FAILED.  This matches what real Weaver implementations return.  This
does not change the result of any existing test.

Test: atest FrameworksServicesTests:com.android.server.locksettings
Bug: 182976659
Bug: 395976735
Flag: TEST_ONLY
Change-Id: I97fed67dae87ca9f4e1a6d24a790c1387509e8c0
parent 7fac4b7b
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -568,7 +568,9 @@ class SyntheticPasswordManager {
        }
    }

    private @Nullable IWeaver getWeaverAidlService() {
    @VisibleForTesting
    @Nullable
    protected IWeaver getWeaverAidlService() {
        final IWeaver aidlWeaver;
        try {
            aidlWeaver =
+28 −4
Original line number Diff line number Diff line
@@ -15,8 +15,10 @@
 */
package com.android.server.locksettings;

import static org.junit.Assert.assertNull;

import android.content.Context;
import android.hardware.weaver.V1_0.IWeaver;
import android.hardware.weaver.IWeaver;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -35,6 +37,8 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {

    private FakeGateKeeperService mGateKeeper;
    private MockWeaverService mWeaverService;
    private IWeaver mWeaverAidl;
    private android.hardware.weaver.V1_0.IWeaver mWeaverHidl;

    public MockSyntheticPasswordManager(Context context, LockSettingsStorage storage,
            FakeGateKeeperService gatekeeper, UserManager userManager,
@@ -112,13 +116,33 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
        }
    }

    /** Enables MockWeaverService. */
    public void enableWeaver() {
        enableWeaverAidl();
    }

    /** Enables MockWeaverService with the current (AIDL) interface. */
    public void enableWeaverAidl() {
        assertNull(mWeaverService);
        mWeaverService = new MockWeaverService();
        mWeaverAidl = mWeaverService;
    }

    @Override
    protected IWeaver getWeaverHidlService() throws RemoteException {
        return mWeaverService;
    protected IWeaver getWeaverAidlService() {
        return mWeaverAidl;
    }

    public void enableWeaver() {
    /** Enables MockWeaverService with the old (HIDL) interface. */
    public void enableWeaverHidl() {
        assertNull(mWeaverService);
        mWeaverService = new MockWeaverService();
        mWeaverHidl = mWeaverService.asHidl();
    }

    @Override
    protected android.hardware.weaver.V1_0.IWeaver getWeaverHidlService() throws RemoteException {
        return mWeaverHidl;
    }

    public int getSumOfWeaverFailureCounters() {
+103 −21
Original line number Diff line number Diff line
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.WeaverStatus;
import static org.mockito.Mockito.mock;

import android.hardware.weaver.IWeaver;
import android.hardware.weaver.WeaverConfig;
import android.hardware.weaver.WeaverReadResponse;
import android.hardware.weaver.WeaverReadStatus;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.ArrayList;
import java.util.Arrays;

public class MockWeaverService extends IWeaver.Stub {
public class MockWeaverService implements IWeaver {

    private static final int MAX_SLOTS = 8;
    private static final int KEY_LENGTH = 256 / 8;
    private static final int VALUE_LENGTH = 256 / 8;

    private static class WeaverSlot {
        public ArrayList<Byte> key;
        public ArrayList<Byte> value;
        public byte[] key;
        public byte[] value;
        public int failureCounter;
    }

@@ -30,46 +33,125 @@ public class MockWeaverService extends IWeaver.Stub {
    }

    @Override
    public void getConfig(getConfigCallback cb) throws RemoteException {
    public WeaverConfig getConfig() throws RemoteException {
        WeaverConfig config = new WeaverConfig();
        config.keySize = KEY_LENGTH;
        config.valueSize = VALUE_LENGTH;
        config.slots = MAX_SLOTS;
        cb.onValues(WeaverStatus.OK, config);
        return config;
    }

    @Override
    public int write(int slotId, ArrayList<Byte> key, ArrayList<Byte> value)
            throws RemoteException {
    public void write(int slotId, byte[] key, byte[] value) throws RemoteException {
        if (slotId < 0 || slotId >= MAX_SLOTS) {
            throw new RuntimeException("Invalid slot id");
        }
        WeaverSlot slot = mSlots[slotId];
        slot.key = (ArrayList<Byte>) key.clone();
        slot.value = (ArrayList<Byte>) value.clone();
        slot.key = key.clone();
        slot.value = value.clone();
        slot.failureCounter = 0;
        return WeaverStatus.OK;
    }

    @Override
    public void read(int slotId, ArrayList<Byte> key, readCallback cb) throws RemoteException {
    public WeaverReadResponse read(int slotId, byte[] key) throws RemoteException {
        if (slotId < 0 || slotId >= MAX_SLOTS) {
            throw new RuntimeException("Invalid slot id");
        }

        WeaverReadResponse response = new WeaverReadResponse();
        WeaverSlot slot = mSlots[slotId];
        if (key.equals(slot.key)) {
            response.value.addAll(slot.value);
            cb.onValues(WeaverStatus.OK, response);
        WeaverReadResponse response = new WeaverReadResponse();
        if (Arrays.equals(key, slot.key)) {
            response.value = slot.value.clone();
            response.status = WeaverReadStatus.OK;
            slot.failureCounter = 0;
        } else {
            cb.onValues(WeaverStatus.FAILED, response);
            response.status = WeaverReadStatus.INCORRECT_KEY;
            slot.failureCounter++;
        }
        return response;
    }

    @Override
    public String getInterfaceHash() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getInterfaceVersion() {
        return 2;
    }

    @Override
    public IBinder asBinder() {
        return mock(IBinder.class);
    }

    public int getSumOfFailureCounters() {
        return Arrays.stream(mSlots).mapToInt(slot -> slot.failureCounter).sum();
    }

    /** Returns an adapter object that implements the old (HIDL) Weaver interface. */
    public android.hardware.weaver.V1_0.IWeaver.Stub asHidl() {
        return new MockWeaverServiceHidlAdapter();
    }

    private class MockWeaverServiceHidlAdapter extends android.hardware.weaver.V1_0.IWeaver.Stub {

        @Override
        public void getConfig(getConfigCallback cb) throws RemoteException {
            WeaverConfig aidlConfig = MockWeaverService.this.getConfig();
            android.hardware.weaver.V1_0.WeaverConfig hidlConfig =
                    new android.hardware.weaver.V1_0.WeaverConfig();
            hidlConfig.keySize = aidlConfig.keySize;
            hidlConfig.valueSize = aidlConfig.valueSize;
            hidlConfig.slots = aidlConfig.slots;
            cb.onValues(android.hardware.weaver.V1_0.WeaverStatus.OK, hidlConfig);
        }

        @SuppressWarnings("NonApiType")
        @Override
        public int write(int slotId, ArrayList<Byte> key, ArrayList<Byte> value)
                throws RemoteException {
            MockWeaverService.this.write(slotId, fromByteArrayList(key), fromByteArrayList(value));
            return android.hardware.weaver.V1_0.WeaverStatus.OK;
        }

        @SuppressWarnings("NonApiType")
        @Override
        public void read(int slotId, ArrayList<Byte> key, readCallback cb) throws RemoteException {
            WeaverReadResponse aidlResponse =
                    MockWeaverService.this.read(slotId, fromByteArrayList(key));
            android.hardware.weaver.V1_0.WeaverReadResponse hidlResponse =
                    new android.hardware.weaver.V1_0.WeaverReadResponse();
            int hidlStatus = switch (aidlResponse.status) {
                case WeaverReadStatus.OK -> android.hardware.weaver.V1_0.WeaverStatus.OK;
                case WeaverReadStatus.INCORRECT_KEY ->
                        android.hardware.weaver.V1_0.WeaverReadStatus.INCORRECT_KEY;
                case WeaverReadStatus.THROTTLE ->
                        android.hardware.weaver.V1_0.WeaverReadStatus.THROTTLE;
                default -> android.hardware.weaver.V1_0.WeaverStatus.FAILED;
            };
            if (aidlResponse.value != null) {
                hidlResponse.value = toByteArrayList(aidlResponse.value);
            }
            cb.onValues(hidlStatus, hidlResponse);
        }

        @SuppressWarnings("NonApiType")
        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;
        }

        @SuppressWarnings("NonApiType")
        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;
        }
    }
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.runner.RunWith;

@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class WeaverAidlBasedSyntheticPasswordTests extends WeaverBasedSyntheticPasswordTests {

    @Before
    public void setup() {
        mSpManager.enableWeaverAidl();
    }
}
+1 −16
Original line number Diff line number Diff line
@@ -6,28 +6,13 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.server.locksettings.LockSettingsStorage.PersistentData;

import com.google.android.collect.Sets;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class WeaverBasedSyntheticPasswordTests extends SyntheticPasswordTests {

    @Before
    public void enableWeaver() throws Exception {
        mSpManager.enableWeaver();
    }
public abstract class WeaverBasedSyntheticPasswordTests extends SyntheticPasswordTests {

    // Tests that if the device is not yet provisioned and the FRP credential uses Weaver, then the
    // Weaver slot of the FRP credential is not reused.  Assumes that Weaver slots are allocated
Loading