Loading core/api/module-lib-current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -318,6 +318,14 @@ package android.net.netstats { } package android.net.wifi { public final class WifiMigration { method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration") public static void migrateLegacyKeystoreToWifiBlobstore(); } } package android.nfc { public class NfcServiceManager { Loading wifi/java/src/android/net/wifi/WifiMigration.java +58 −0 Original line number Diff line number Diff line Loading @@ -19,16 +19,23 @@ package android.net.wifi; import static android.os.Environment.getDataMiscCeDirectory; import static android.os.Environment.getDataMiscDirectory; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.net.wifi.flags.Flags; import android.os.Binder; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.provider.Settings; import android.security.legacykeystore.ILegacyKeystore; import android.util.AtomicFile; import android.util.Log; import android.util.SparseArray; import java.io.File; Loading @@ -36,7 +43,11 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * Class used to provide one time hooks for existing OEM devices to migrate their config store Loading @@ -45,6 +56,8 @@ import java.util.Objects; */ @SystemApi public final class WifiMigration { private static final String TAG = "WifiMigration"; /** * Directory to read the wifi config store files from under. */ Loading Loading @@ -555,4 +568,49 @@ public final class WifiMigration { return data; } /** * Migrate any certificates in Legacy Keystore to the newer WifiBlobstore database. * * @hide */ @FlaggedApi(Flags.FLAG_LEGACY_KEYSTORE_TO_WIFI_BLOBSTORE_MIGRATION) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static void migrateLegacyKeystoreToWifiBlobstore() { final long identity = Binder.clearCallingIdentity(); try { ILegacyKeystore legacyKeystore = WifiBlobStore.getLegacyKeystore(); String[] legacyAliases = legacyKeystore.list("", Process.WIFI_UID); if (legacyAliases == null || legacyAliases.length == 0) { Log.i(TAG, "No aliases need to be migrated"); return; } WifiBlobStore wifiBlobStore = WifiBlobStore.getInstance(); List<String> blobstoreAliasList = Arrays.asList(wifiBlobStore.list("")); Set<String> blobstoreAliases = new HashSet<>(); blobstoreAliases.addAll(blobstoreAliasList); for (String legacyAlias : legacyAliases) { // Only migrate if the alias is not already in WifiBlobstore, // since WifiBlobstore should already contain the latest value. if (!blobstoreAliases.contains(legacyAlias)) { byte[] value = legacyKeystore.get(legacyAlias, Process.WIFI_UID); wifiBlobStore.put(legacyAlias, value); } legacyKeystore.remove(legacyAlias, Process.WIFI_UID); } Log.i(TAG, "Successfully migrated aliases from Legacy Keystore"); } catch (ServiceSpecificException e) { if (e.errorCode == ILegacyKeystore.ERROR_SYSTEM_ERROR) { Log.i(TAG, "Legacy Keystore service has been deprecated"); } else { Log.e(TAG, "Encountered an exception while migrating aliases. " + e); } } catch (Exception e) { Log.e(TAG, "Encountered an exception while migrating aliases. " + e); } finally { Binder.restoreCallingIdentity(identity); } } } wifi/tests/src/android/net/wifi/WifiMigrationTest.java 0 → 100644 +118 −0 Original line number 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; 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; /** * Unit tests for {@link WifiMigration}. */ public class WifiMigrationTest { 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); when(mLegacyKeystore.get(anyString(), anyInt())).thenReturn(TEST_VALUE); } @After public void cleanup() { validateMockitoUsage(); if (mSession != null) { mSession.finishMocking(); } } /** * Verify that the Keystore migration method returns immediately if no aliases * are found in Legacy Keystore. */ @Test public void testKeystoreMigrationNoLegacyAliases() throws Exception { when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(new String[0]); WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); verify(mLegacyKeystore).list(anyString(), anyInt()); verifyNoMoreInteractions(mLegacyKeystore, mWifiBlobStore); } /** * Verify that if all aliases in Legacy Keystore are unique to that database, * all aliases are migrated to WifiBlobstore. */ @Test public void testKeystoreMigrationUniqueLegacyAliases() throws Exception { String[] legacyAliases = new String[]{TEST_ALIAS + "1", TEST_ALIAS + "2"}; String[] blobstoreAliases = new String[0]; when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(legacyAliases); when(mWifiBlobStore.list(anyString())).thenReturn(blobstoreAliases); WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); verify(mWifiBlobStore, times(legacyAliases.length)).put(anyString(), any(byte[].class)); } /** * Verify that if some aliases are shared between Legacy Keystore and WifiBlobstore, * only the ones unique to Legacy Keystore are migrated. */ @Test public void testKeystoreMigrationDuplicateLegacyAliases() throws Exception { String uniqueLegacyAlias = TEST_ALIAS + "1"; String[] blobstoreAliases = new String[]{TEST_ALIAS + "2", TEST_ALIAS + "3"}; String[] legacyAliases = new String[]{blobstoreAliases[0], blobstoreAliases[1], uniqueLegacyAlias}; when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(legacyAliases); when(mWifiBlobStore.list(anyString())).thenReturn(blobstoreAliases); // Expect that only the unique legacy alias is migrated to the blobstore WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); verify(mWifiBlobStore).list(anyString()); verify(mWifiBlobStore).put(eq(uniqueLegacyAlias), any(byte[].class)); verifyNoMoreInteractions(mWifiBlobStore); } } wifi/wifi.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -17,3 +17,11 @@ flag { description: "Control the API that allows setting / reading the NetworkProviderInfo's battery charging status" bug: "305067231" } flag { name: "legacy_keystore_to_wifi_blobstore_migration" is_exported: true namespace: "wifi" description: "Add API to migrate all values from Legacy Keystore to the new Wifi Blobstore database" bug: "332560152" } Loading
core/api/module-lib-current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -318,6 +318,14 @@ package android.net.netstats { } package android.net.wifi { public final class WifiMigration { method @FlaggedApi("android.net.wifi.flags.legacy_keystore_to_wifi_blobstore_migration") public static void migrateLegacyKeystoreToWifiBlobstore(); } } package android.nfc { public class NfcServiceManager { Loading
wifi/java/src/android/net/wifi/WifiMigration.java +58 −0 Original line number Diff line number Diff line Loading @@ -19,16 +19,23 @@ package android.net.wifi; import static android.os.Environment.getDataMiscCeDirectory; import static android.os.Environment.getDataMiscDirectory; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.net.wifi.flags.Flags; import android.os.Binder; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.provider.Settings; import android.security.legacykeystore.ILegacyKeystore; import android.util.AtomicFile; import android.util.Log; import android.util.SparseArray; import java.io.File; Loading @@ -36,7 +43,11 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * Class used to provide one time hooks for existing OEM devices to migrate their config store Loading @@ -45,6 +56,8 @@ import java.util.Objects; */ @SystemApi public final class WifiMigration { private static final String TAG = "WifiMigration"; /** * Directory to read the wifi config store files from under. */ Loading Loading @@ -555,4 +568,49 @@ public final class WifiMigration { return data; } /** * Migrate any certificates in Legacy Keystore to the newer WifiBlobstore database. * * @hide */ @FlaggedApi(Flags.FLAG_LEGACY_KEYSTORE_TO_WIFI_BLOBSTORE_MIGRATION) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static void migrateLegacyKeystoreToWifiBlobstore() { final long identity = Binder.clearCallingIdentity(); try { ILegacyKeystore legacyKeystore = WifiBlobStore.getLegacyKeystore(); String[] legacyAliases = legacyKeystore.list("", Process.WIFI_UID); if (legacyAliases == null || legacyAliases.length == 0) { Log.i(TAG, "No aliases need to be migrated"); return; } WifiBlobStore wifiBlobStore = WifiBlobStore.getInstance(); List<String> blobstoreAliasList = Arrays.asList(wifiBlobStore.list("")); Set<String> blobstoreAliases = new HashSet<>(); blobstoreAliases.addAll(blobstoreAliasList); for (String legacyAlias : legacyAliases) { // Only migrate if the alias is not already in WifiBlobstore, // since WifiBlobstore should already contain the latest value. if (!blobstoreAliases.contains(legacyAlias)) { byte[] value = legacyKeystore.get(legacyAlias, Process.WIFI_UID); wifiBlobStore.put(legacyAlias, value); } legacyKeystore.remove(legacyAlias, Process.WIFI_UID); } Log.i(TAG, "Successfully migrated aliases from Legacy Keystore"); } catch (ServiceSpecificException e) { if (e.errorCode == ILegacyKeystore.ERROR_SYSTEM_ERROR) { Log.i(TAG, "Legacy Keystore service has been deprecated"); } else { Log.e(TAG, "Encountered an exception while migrating aliases. " + e); } } catch (Exception e) { Log.e(TAG, "Encountered an exception while migrating aliases. " + e); } finally { Binder.restoreCallingIdentity(identity); } } }
wifi/tests/src/android/net/wifi/WifiMigrationTest.java 0 → 100644 +118 −0 Original line number 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; 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; /** * Unit tests for {@link WifiMigration}. */ public class WifiMigrationTest { 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); when(mLegacyKeystore.get(anyString(), anyInt())).thenReturn(TEST_VALUE); } @After public void cleanup() { validateMockitoUsage(); if (mSession != null) { mSession.finishMocking(); } } /** * Verify that the Keystore migration method returns immediately if no aliases * are found in Legacy Keystore. */ @Test public void testKeystoreMigrationNoLegacyAliases() throws Exception { when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(new String[0]); WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); verify(mLegacyKeystore).list(anyString(), anyInt()); verifyNoMoreInteractions(mLegacyKeystore, mWifiBlobStore); } /** * Verify that if all aliases in Legacy Keystore are unique to that database, * all aliases are migrated to WifiBlobstore. */ @Test public void testKeystoreMigrationUniqueLegacyAliases() throws Exception { String[] legacyAliases = new String[]{TEST_ALIAS + "1", TEST_ALIAS + "2"}; String[] blobstoreAliases = new String[0]; when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(legacyAliases); when(mWifiBlobStore.list(anyString())).thenReturn(blobstoreAliases); WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); verify(mWifiBlobStore, times(legacyAliases.length)).put(anyString(), any(byte[].class)); } /** * Verify that if some aliases are shared between Legacy Keystore and WifiBlobstore, * only the ones unique to Legacy Keystore are migrated. */ @Test public void testKeystoreMigrationDuplicateLegacyAliases() throws Exception { String uniqueLegacyAlias = TEST_ALIAS + "1"; String[] blobstoreAliases = new String[]{TEST_ALIAS + "2", TEST_ALIAS + "3"}; String[] legacyAliases = new String[]{blobstoreAliases[0], blobstoreAliases[1], uniqueLegacyAlias}; when(mLegacyKeystore.list(anyString(), anyInt())).thenReturn(legacyAliases); when(mWifiBlobStore.list(anyString())).thenReturn(blobstoreAliases); // Expect that only the unique legacy alias is migrated to the blobstore WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); verify(mWifiBlobStore).list(anyString()); verify(mWifiBlobStore).put(eq(uniqueLegacyAlias), any(byte[].class)); verifyNoMoreInteractions(mWifiBlobStore); } }
wifi/wifi.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -17,3 +17,11 @@ flag { description: "Control the API that allows setting / reading the NetworkProviderInfo's battery charging status" bug: "305067231" } flag { name: "legacy_keystore_to_wifi_blobstore_migration" is_exported: true namespace: "wifi" description: "Add API to migrate all values from Legacy Keystore to the new Wifi Blobstore database" bug: "332560152" }