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

Commit faea9ff3 authored by Eric Biggers's avatar Eric Biggers Committed by Android (Google) Code Review
Browse files

Merge changes I203018d7,I099c8315,I21e958d3

* changes:
  locksettings: use ArrayUtils.concat(byte[]...)
  ArrayUtils: add concat() for byte arrays
  ArrayUtils: rename concatElements() to concat()
parents 8db173d6 838d33b4
Loading
Loading
Loading
Loading
+28 −5
Original line number Diff line number Diff line
@@ -39,8 +39,7 @@ import java.util.Set;
import java.util.function.IntFunction;

/**
 * ArrayUtils contains some methods that you can call to find out
 * the most efficient increments by which to grow arrays.
 * Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
 */
public class ArrayUtils {
    private static final int CACHE_SIZE = 73;
@@ -351,15 +350,16 @@ public class ArrayUtils {
    }

    /**
     * Combine multiple arrays into a single array.
     * Returns the concatenation of the given arrays.  Only works for object arrays, not for
     * primitive arrays.  See {@link #concat(byte[]...)} for a variant that works on byte arrays.
     *
     * @param kind The class of the array elements
     * @param arrays The arrays to combine
     * @param arrays The arrays to concatenate.  Null arrays are treated as empty.
     * @param <T> The class of the array elements (inferred from kind).
     * @return A single array containing all the elements of the parameter arrays.
     */
    @SuppressWarnings("unchecked")
    public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
    public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[]... arrays) {
        if (arrays == null || arrays.length == 0) {
            return createEmptyArray(kind);
        }
@@ -400,6 +400,29 @@ public class ArrayUtils {
        return (T[]) Array.newInstance(kind, 0);
    }

    /**
     * Returns the concatenation of the given byte arrays.  Null arrays are treated as empty.
     */
    public static @NonNull byte[] concat(@Nullable byte[]... arrays) {
        if (arrays == null) {
            return new byte[0];
        }
        int totalLength = 0;
        for (byte[] a : arrays) {
            if (a != null) {
                totalLength += a.length;
            }
        }
        final byte[] result = new byte[totalLength];
        int pos = 0;
        for (byte[] a : arrays) {
            if (a != null) {
                System.arraycopy(a, 0, result, pos, a.length);
                pos += a.length;
            }
        }
        return result;
    }

    /**
     * Adds value to given array if not already present, providing set-like
+4 −12
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.os.Parcelable;
import android.os.storage.StorageManager;
import android.text.TextUtils;

import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;

import libcore.util.HexEncoding;
@@ -280,7 +281,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
            sha256.update(hashFactor);
            sha256.update(passwordToHash);
            sha256.update(salt);
            return new String(HexEncoding.encode(sha256.digest()));
            return HexEncoding.encodeToString(sha256.digest());
        } catch (NoSuchAlgorithmException e) {
            throw new AssertionError("Missing digest algorithm: ", e);
        }
@@ -302,21 +303,12 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
        }

        try {
            // Previously the password was passed as a String with the following code:
            // byte[] saltedPassword = (password + salt).getBytes();
            // The code below creates the identical digest preimage using byte arrays:
            byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length);
            System.arraycopy(salt, 0, saltedPassword, password.length, salt.length);
            byte[] saltedPassword = ArrayUtils.concat(password, salt);
            byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
            byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);

            byte[] combined = new byte[sha1.length + md5.length];
            System.arraycopy(sha1, 0, combined, 0, sha1.length);
            System.arraycopy(md5, 0, combined, sha1.length, md5.length);

            final char[] hexEncoded = HexEncoding.encode(combined);
            Arrays.fill(saltedPassword, (byte) 0);
            return new String(hexEncoded);
            return HexEncoding.encodeToString(ArrayUtils.concat(sha1, md5));
        } catch (NoSuchAlgorithmException e) {
            throw new AssertionError("Missing digest algorithm: ", e);
        }
+77 −33
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.internal.util;

import static com.android.internal.util.ArrayUtils.concatElements;

import static org.junit.Assert.assertArrayEquals;

import junit.framework.TestCase;
@@ -156,61 +154,107 @@ public class ArrayUtilsTest extends TestCase {
                ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1));
    }

    public void testConcatEmpty() throws Exception {
        assertArrayEquals(new Long[] {},
                concatElements(Long.class, null, null));
        assertArrayEquals(new Long[] {},
                concatElements(Long.class, new Long[] {}, null));
        assertArrayEquals(new Long[] {},
                concatElements(Long.class, null, new Long[] {}));
        assertArrayEquals(new Long[] {},
                concatElements(Long.class, new Long[] {}, new Long[] {}));
    public void testConcat_zeroObjectArrays() {
        // empty varargs array
        assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class));
        // null varargs array
        assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class, (String[][]) null));
    }

    public void testconcatElements() throws Exception {
    public void testConcat_oneObjectArray() {
        assertArrayEquals(new String[] { "1", "2" },
                ArrayUtils.concat(String.class, new String[] { "1", "2" }));
    }

    public void testConcat_oneEmptyObjectArray() {
        assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class, (String[]) null));
        assertArrayEquals(new String[] {}, ArrayUtils.concat(String.class, new String[] {}));
    }

    public void testConcat_twoObjectArrays() {
        assertArrayEquals(new Long[] { 1L },
                concatElements(Long.class, new Long[] { 1L }, new Long[] {}));
                ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] {}));
        assertArrayEquals(new Long[] { 1L },
                concatElements(Long.class, new Long[] {}, new Long[] { 1L }));
                ArrayUtils.concat(Long.class, new Long[] {}, new Long[] { 1L }));
        assertArrayEquals(new Long[] { 1L, 2L },
                concatElements(Long.class, new Long[] { 1L }, new Long[] { 2L }));
                ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] { 2L }));
        assertArrayEquals(new Long[] { 1L, 2L, 3L, 4L },
                concatElements(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
                ArrayUtils.concat(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
    }

    public void testConcat_twoEmptyObjectArrays() {
        assertArrayEquals(new Long[] {}, ArrayUtils.concat(Long.class, null, null));
        assertArrayEquals(new Long[] {}, ArrayUtils.concat(Long.class, new Long[] {}, null));
        assertArrayEquals(new Long[] {}, ArrayUtils.concat(Long.class, null, new Long[] {}));
        assertArrayEquals(new Long[] {},
                ArrayUtils.concat(Long.class, new Long[] {}, new Long[] {}));
    }

    public void testConcatElements_threeWay() {
    public void testConcat_threeObjectArrays() {
        String[] array1 = { "1", "2" };
        String[] array2 = { "3", "4" };
        String[] array3 = { "5", "6" };
        String[] expectation = { "1", "2", "3", "4", "5", "6" };

        String[] concatResult = ArrayUtils.concatElements(String.class, array1, array2, array3);
        assertArrayEquals(expectation, concatResult);
        assertArrayEquals(expectation, ArrayUtils.concat(String.class, array1, array2, array3));
    }


    public void testConcatElements_threeWayWithNull() {
    public void testConcat_threeObjectArraysWithNull() {
        String[] array1 = { "1", "2" };
        String[] array2 = null;
        String[] array3 = { "5", "6" };
        String[] expectation = { "1", "2", "5", "6" };

        String[] concatResult = ArrayUtils.concatElements(String.class, array1, array2, array3);
        assertArrayEquals(expectation, concatResult);
        assertArrayEquals(expectation, ArrayUtils.concat(String.class, array1, array2, array3));
    }

    public void testConcat_zeroByteArrays() {
        // empty varargs array
        assertArrayEquals(new byte[] {}, ArrayUtils.concat());
        // null varargs array
        assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[][]) null));
    }

    public void testConcatElements_zeroElements() {
        String[] expectation = new String[0];
    public void testConcat_oneByteArray() {
        assertArrayEquals(new byte[] { 1, 2 }, ArrayUtils.concat(new byte[] { 1, 2 }));
    }

        String[] concatResult = ArrayUtils.concatElements(String.class);
        assertArrayEquals(expectation, concatResult);
    public void testConcat_oneEmptyByteArray() {
        assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[]) null));
        assertArrayEquals(new byte[] {}, ArrayUtils.concat(new byte[] {}));
    }

    public void testConcatElements_oneNullElement() {
        String[] expectation = new String[0];
    public void testConcat_twoByteArrays() {
        assertArrayEquals(new byte[] { 1 }, ArrayUtils.concat(new byte[] { 1 }, new byte[] {}));
        assertArrayEquals(new byte[] { 1 }, ArrayUtils.concat(new byte[] {}, new byte[] { 1 }));
        assertArrayEquals(new byte[] { 1, 2 },
                ArrayUtils.concat(new byte[] { 1 }, new byte[] { 2 }));
        assertArrayEquals(new byte[] { 1, 2, 3, 4 },
                ArrayUtils.concat(new byte[] { 1, 2 }, new byte[] { 3, 4 }));
    }

        String[] concatResult = ArrayUtils.concatElements(String.class, null);
        assertArrayEquals(expectation, concatResult);
    public void testConcat_twoEmptyByteArrays() {
        assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[]) null, null));
        assertArrayEquals(new byte[] {}, ArrayUtils.concat(new byte[] {}, null));
        assertArrayEquals(new byte[] {}, ArrayUtils.concat((byte[]) null, new byte[] {}));
        assertArrayEquals(new byte[] {}, ArrayUtils.concat(new byte[] {}, new byte[] {}));
    }

    public void testConcat_threeByteArrays() {
        byte[] array1 = { 1, 2 };
        byte[] array2 = { 3, 4 };
        byte[] array3 = { 5, 6 };
        byte[] expectation = { 1, 2, 3, 4, 5, 6 };

        assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3));
    }

    public void testConcat_threeByteArraysWithNull() {
        byte[] array1 = { 1, 2 };
        byte[] array2 = null;
        byte[] array3 = { 5, 6 };
        byte[] expectation = { 1, 2, 5, 6 };

        assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3));
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -875,16 +875,16 @@ public class SettingsBackupAgent extends BackupAgentHelper {
        String[] whitelist;
        Map<String, Validator> validators = null;
        if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
            whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
            whitelist = ArrayUtils.concat(String.class, SecureSettings.SETTINGS_TO_BACKUP,
                    Settings.Secure.LEGACY_RESTORE_SETTINGS,
                    DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
            validators = SecureSettingsValidators.VALIDATORS;
        } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
            whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
            whitelist = ArrayUtils.concat(String.class, SystemSettings.SETTINGS_TO_BACKUP,
                    Settings.System.LEGACY_RESTORE_SETTINGS);
            validators = SystemSettingsValidators.VALIDATORS;
        } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
            whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
            whitelist = ArrayUtils.concat(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
                    Settings.Global.LEGACY_RESTORE_SETTINGS);
            validators = GlobalSettingsValidators.VALIDATORS;
        } else {
+7 −14
Original line number Diff line number Diff line
@@ -122,6 +122,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -147,7 +148,6 @@ import com.android.server.wm.WindowManagerInternal;

import libcore.util.HexEncoding;

import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -1847,8 +1847,8 @@ public class LockSettingsService extends ILockSettings.Stub {
    @VisibleForTesting /** Note: this method is overridden in unit tests */
    protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
        if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
        byte[] encryptionResult;
        byte[] iv;
        final byte[] iv;
        final byte[] ciphertext;
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
            keyGenerator.init(new SecureRandom());
@@ -1877,7 +1877,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                        KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
                                + KeyProperties.ENCRYPTION_PADDING_NONE);
                cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
                encryptionResult = cipher.doFinal(password.getCredential());
                ciphertext = cipher.doFinal(password.getCredential());
                iv = cipher.getIV();
            } finally {
                // The original key can now be discarded.
@@ -1888,17 +1888,10 @@ public class LockSettingsService extends ILockSettings.Stub {
                | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
            throw new IllegalStateException("Failed to encrypt key", e);
        }
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
        if (iv.length != PROFILE_KEY_IV_SIZE) {
            throw new IllegalArgumentException("Invalid iv length: " + iv.length);
        }
            outputStream.write(iv);
            outputStream.write(encryptionResult);
        } catch (IOException e) {
            throw new IllegalStateException("Failed to concatenate byte arrays", e);
        }
        mStorage.writeChildProfileLock(userId, outputStream.toByteArray());
        mStorage.writeChildProfileLock(userId, ArrayUtils.concat(iv, ciphertext));
    }

    private void setUserKeyProtection(int userId, byte[] key) {
Loading