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

Commit ddbcdfef authored by Gabriel Biren's avatar Gabriel Biren Committed by Android (Google) Code Review
Browse files

Merge changes Idb3905ce,I5111a20a into main

* changes:
  Add unit tests for WifiKeystore.
  Move getLegacyKeystore to the WifiBlobStore class.
parents f8bd1ec6 e2229449
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package android.net.wifi;
package android.net.wifi;


import android.os.ServiceManager;
import android.security.legacykeystore.ILegacyKeystore;

import com.android.internal.net.ConnectivityBlobStore;
import com.android.internal.net.ConnectivityBlobStore;


/**
/**
@@ -24,6 +27,7 @@ import com.android.internal.net.ConnectivityBlobStore;
 */
 */
public class WifiBlobStore extends ConnectivityBlobStore {
public class WifiBlobStore extends ConnectivityBlobStore {
    private static final String DB_NAME = "WifiBlobStore.db";
    private static final String DB_NAME = "WifiBlobStore.db";
    private static final String LEGACY_KEYSTORE_SERVICE_NAME = "android.security.legacykeystore";
    private static WifiBlobStore sInstance;
    private static WifiBlobStore sInstance;
    private WifiBlobStore() {
    private WifiBlobStore() {
        super(DB_NAME);
        super(DB_NAME);
@@ -36,4 +40,10 @@ public class WifiBlobStore extends ConnectivityBlobStore {
        }
        }
        return sInstance;
        return sInstance;
    }
    }

    /** Returns an interface to access the Legacy Keystore service. */
    public static ILegacyKeystore getLegacyKeystore() {
        return ILegacyKeystore.Stub.asInterface(
                ServiceManager.checkService(LEGACY_KEYSTORE_SERVICE_NAME));
    }
}
}
+4 −10
Original line number Original line Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.os.Binder;
import android.os.Binder;
import android.os.Process;
import android.os.Process;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.ServiceSpecificException;
import android.security.legacykeystore.ILegacyKeystore;
import android.security.legacykeystore.ILegacyKeystore;
import android.util.Log;
import android.util.Log;
@@ -37,12 +36,6 @@ import java.util.Set;
@SuppressLint("UnflaggedApi") // Promoting from @SystemApi(MODULE_LIBRARIES)
@SuppressLint("UnflaggedApi") // Promoting from @SystemApi(MODULE_LIBRARIES)
public final class WifiKeystore {
public final class WifiKeystore {
    private static final String TAG = "WifiKeystore";
    private static final String TAG = "WifiKeystore";
    private static final String LEGACY_KEYSTORE_SERVICE_NAME = "android.security.legacykeystore";

    private static ILegacyKeystore getLegacyKeystore() {
        return ILegacyKeystore.Stub.asInterface(
                ServiceManager.checkService(LEGACY_KEYSTORE_SERVICE_NAME));
    }


    /** @hide */
    /** @hide */
    WifiKeystore() {
    WifiKeystore() {
@@ -93,7 +86,7 @@ public final class WifiKeystore {
                return blob;
                return blob;
            }
            }
            Log.i(TAG, "Searching for blob in Legacy Keystore");
            Log.i(TAG, "Searching for blob in Legacy Keystore");
            return getLegacyKeystore().get(alias, Process.WIFI_UID);
            return WifiBlobStore.getLegacyKeystore().get(alias, Process.WIFI_UID);
        } catch (ServiceSpecificException e) {
        } catch (ServiceSpecificException e) {
            if (e.errorCode != ILegacyKeystore.ERROR_ENTRY_NOT_FOUND) {
            if (e.errorCode != ILegacyKeystore.ERROR_ENTRY_NOT_FOUND) {
                Log.e(TAG, "Failed to get blob.", e);
                Log.e(TAG, "Failed to get blob.", e);
@@ -122,7 +115,7 @@ public final class WifiKeystore {
            Log.i(TAG, "remove blob. alias " + alias);
            Log.i(TAG, "remove blob. alias " + alias);
            blobStoreSuccess = WifiBlobStore.getInstance().remove(alias);
            blobStoreSuccess = WifiBlobStore.getInstance().remove(alias);
            // Legacy Keystore will throw an exception if the alias is not found.
            // Legacy Keystore will throw an exception if the alias is not found.
            getLegacyKeystore().remove(alias, Process.WIFI_UID);
            WifiBlobStore.getLegacyKeystore().remove(alias, Process.WIFI_UID);
            legacyKsSuccess = true;
            legacyKsSuccess = true;
        } catch (ServiceSpecificException e) {
        } catch (ServiceSpecificException e) {
            if (e.errorCode != ILegacyKeystore.ERROR_ENTRY_NOT_FOUND) {
            if (e.errorCode != ILegacyKeystore.ERROR_ENTRY_NOT_FOUND) {
@@ -151,7 +144,8 @@ public final class WifiKeystore {
        try {
        try {
            // Aliases from WifiBlobStore will be pre-trimmed.
            // Aliases from WifiBlobStore will be pre-trimmed.
            final String[] blobStoreAliases = WifiBlobStore.getInstance().list(prefix);
            final String[] blobStoreAliases = WifiBlobStore.getInstance().list(prefix);
            final String[] legacyAliases = getLegacyKeystore().list(prefix, Process.WIFI_UID);
            final String[] legacyAliases =
                    WifiBlobStore.getLegacyKeystore().list(prefix, Process.WIFI_UID);
            for (int i = 0; i < legacyAliases.length; ++i) {
            for (int i = 0; i < legacyAliases.length; ++i) {
                legacyAliases[i] = legacyAliases[i].substring(prefix.length());
                legacyAliases[i] = legacyAliases[i].substring(prefix.length());
            }
            }
+178 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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 android.net.wifi;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.validateMockitoUsage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;

import android.os.ServiceSpecificException;
import android.security.legacykeystore.ILegacyKeystore;

import com.android.dx.mockito.inline.extended.ExtendedMockito;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;

import java.util.Arrays;

/** Unit tests for {@link WifiKeystore} */
public class WifiKeystoreTest {
    public static final String TEST_ALIAS = "someAliasString";
    public static final byte[] TEST_VALUE = new byte[]{10, 11, 12};

    @Mock private ILegacyKeystore mLegacyKeystore;
    @Mock private WifiBlobStore mWifiBlobStore;

    private MockitoSession mSession;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mSession = ExtendedMockito.mockitoSession()
                .mockStatic(WifiBlobStore.class, withSettings().lenient())
                .startMocking();
        when(WifiBlobStore.getLegacyKeystore()).thenReturn(mLegacyKeystore);
        when(WifiBlobStore.getInstance()).thenReturn(mWifiBlobStore);
    }

    @After
    public void cleanup() {
        validateMockitoUsage();
        if (mSession != null) {
            mSession.finishMocking();
        }
    }

    /**
     * Test that put() only writes to the WifiBlobStore database.
     */
    @Test
    public void testPut() throws Exception {
        WifiKeystore.put(TEST_ALIAS, TEST_VALUE);
        verify(mWifiBlobStore).put(anyString(), any());
        verify(mLegacyKeystore, never()).put(anyString(), anyInt(), any());
    }

    /**
     * Test that if the alias is found in the WifiBlobStore database,
     * then the legacy database is not searched.
     */
    @Test
    public void testGet_wifiBlobStoreDb() throws Exception {
        when(mWifiBlobStore.get(anyString())).thenReturn(TEST_VALUE);
        assertArrayEquals(TEST_VALUE, WifiKeystore.get(TEST_ALIAS));

        verify(mWifiBlobStore).get(anyString());
        verify(mLegacyKeystore, never()).get(anyString(), anyInt());
    }

    /**
     * Test that if the alias is not found in the WifiBlobStore database,
     * then the legacy database is searched.
     */
    @Test
    public void testGet_legacyDb() throws Exception {
        when(mWifiBlobStore.get(anyString())).thenReturn(null);
        when(mLegacyKeystore.get(anyString(), anyInt())).thenReturn(TEST_VALUE);
        assertArrayEquals(TEST_VALUE, WifiKeystore.get(TEST_ALIAS));

        verify(mWifiBlobStore).get(anyString());
        verify(mLegacyKeystore).get(anyString(), anyInt());
    }

    /**
     * Test that get() returns a non-null value if the alias is
     * not found in either database.
     */
    @Test
    public void testGet_notFound() throws Exception {
        when(mWifiBlobStore.get(anyString())).thenReturn(null);
        when(mLegacyKeystore.get(anyString(), anyInt()))
                .thenThrow(new ServiceSpecificException(ILegacyKeystore.ERROR_ENTRY_NOT_FOUND));
        assertNotNull(WifiKeystore.get(TEST_ALIAS));
    }

    /**
     * Test that remove() returns true if the alias is removed
     * from at least one database.
     */
    @Test
    public void testRemove_success() throws Exception {
        // Only removed from WifiBlobStore
        when(mWifiBlobStore.remove(anyString())).thenReturn(true);
        doThrow(new ServiceSpecificException(ILegacyKeystore.ERROR_ENTRY_NOT_FOUND))
                .when(mLegacyKeystore).remove(anyString(), anyInt());
        assertTrue(WifiKeystore.remove(TEST_ALIAS));

        // Only removed from Legacy Keystore
        when(mWifiBlobStore.remove(anyString())).thenReturn(false);
        doNothing().when(mLegacyKeystore).remove(anyString(), anyInt());
        assertTrue(WifiKeystore.remove(TEST_ALIAS));

        // Removed from both WifiBlobStore and Legacy Keystore
        when(mWifiBlobStore.remove(anyString())).thenReturn(true);
        doNothing().when(mLegacyKeystore).remove(anyString(), anyInt());
        assertTrue(WifiKeystore.remove(TEST_ALIAS));
    }

    /**
     * Test that remove() returns false if the alias is not removed
     * from any database.
     */
    @Test
    public void testRemove_notFound() throws Exception {
        when(mWifiBlobStore.remove(anyString())).thenReturn(false);
        doThrow(new ServiceSpecificException(ILegacyKeystore.ERROR_ENTRY_NOT_FOUND))
                .when(mLegacyKeystore).remove(anyString(), anyInt());
        assertFalse(WifiKeystore.remove(TEST_ALIAS));
    }

    /**
     * Test that list() retrieves aliases from both the WifiBlobStore
     * and Legacy Keystore databases. The results should be de-duplicated.
     */
    @Test
    public void testList() throws Exception {
        // Aliases retrieved from WifiBlobStore will be pre-trimmed.
        String[] blobStoreAliases = new String[]{"1", "2"};
        String[] legacyDbAliases = new String[]{TEST_ALIAS + "2", TEST_ALIAS + "3"};
        when(mWifiBlobStore.list(anyString())).thenReturn(blobStoreAliases);
        when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(legacyDbAliases);

        // Alias 2 exists in both DBs and should be de-duplicated.
        String[] expected = new String[]{"1", "2", "3"};
        String[] retrieved = WifiKeystore.list(TEST_ALIAS);
        Arrays.sort(retrieved);
        assertArrayEquals(expected, retrieved);
    }
}