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

Commit 4afe8576 authored by Jason Macnak's avatar Jason Macnak
Browse files

Make display unique id stable across x86 and x86_64 builds

... by using a minimal version of CityHash64 for the hash function.
std::hash differs between x86 and x86_64 and also:

"Hash functions are only required to produce the same result for the
same input within a single execution of a program;"

This is problematic for input device configuration files which
reference the display unique id.

Bug: b/186150820
Test: libsurfaceflinger_unittest
Change-Id: If15b66775fa48f14cc56bbd23536b61844b7ae37
parent 620fd684
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -146,6 +146,7 @@ filegroup {
        "DisplayHardware/ComposerHal.cpp",
        "DisplayHardware/DisplayIdentification.cpp",
        "DisplayHardware/FramebufferSurface.cpp",
        "DisplayHardware/Hash.cpp",
        "DisplayHardware/HWC2.cpp",
        "DisplayHardware/HWComposer.cpp",
        "DisplayHardware/PowerAdvisor.cpp",
+4 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <log/log.h>

#include "DisplayIdentification.h"
#include "Hash.h"

namespace android {
namespace {
@@ -262,8 +263,9 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
    }

    // Hash model string instead of using product code or (integer) serial number, since the latter
    // have been observed to change on some displays with multiple inputs.
    const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
    // have been observed to change on some displays with multiple inputs. Use a stable hash instead
    // of std::hash which is only required to be same within a single execution of a program.
    const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString));

    // Parse extension blocks.
    std::optional<Cea861ExtensionBlock> cea861Block;
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 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.
 */

#undef LOG_TAG
#define LOG_TAG "DisplayIdentification"

#include <cstring>
#include <type_traits>

#include <log/log.h>

#include "Hash.h"

namespace android {
namespace {

template <class T>
inline T load(const void* p) {
    static_assert(std::is_integral<T>::value, "T must be integral");

    T r;
    std::memcpy(&r, p, sizeof(r));
    return r;
}

uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
    return (val >> shift) | (val << (64 - shift));
}

uint64_t shiftMix(uint64_t val) {
    return val ^ (val >> 47);
}

uint64_t hash64Len16(uint64_t u, uint64_t v) {
    constexpr uint64_t kMul = 0x9ddfea08eb382d69;
    uint64_t a = (u ^ v) * kMul;
    a ^= (a >> 47);
    uint64_t b = (v ^ a) * kMul;
    b ^= (b >> 47);
    b *= kMul;
    return b;
}

uint64_t hash64Len0To16(const char* s, uint64_t len) {
    constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
    constexpr uint64_t k3 = 0xc949d7c7509e6557;

    if (len > 8) {
        const uint64_t a = load<uint64_t>(s);
        const uint64_t b = load<uint64_t>(s + len - 8);
        return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
    }
    if (len >= 4) {
        const uint32_t a = load<uint32_t>(s);
        const uint32_t b = load<uint32_t>(s + len - 4);
        return hash64Len16(len + (a << 3), b);
    }
    if (len > 0) {
        const unsigned char a = static_cast<unsigned char>(s[0]);
        const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
        const unsigned char c = static_cast<unsigned char>(s[len - 1]);
        const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
        const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
        return shiftMix(y * k2 ^ z * k3) * k2;
    }
    return k2;
}

} // namespace

uint64_t cityHash64Len0To16(std::string_view sv) {
    auto len = sv.length();
    if (len > 16) {
        ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
        len = 16;
    }
    return hash64Len0To16(sv.data(), len);
}

} // namespace android
 No newline at end of file
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 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.
 */

#pragma once

#include <cstdint>
#include <string_view>

namespace android {

// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
uint64_t cityHash64Len0To16(std::string_view sv);

} // namespace android
 No newline at end of file
+5 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <gtest/gtest.h>

#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/Hash.h"

using ::testing::ElementsAre;

@@ -134,7 +135,7 @@ DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&byte
}

uint32_t hash(const char* str) {
    return static_cast<uint32_t>(std::hash<std::string_view>()(str));
    return static_cast<uint32_t>(cityHash64Len0To16(str));
}

} // namespace
@@ -309,9 +310,9 @@ TEST(DisplayIdentificationTest, parseDisplayIdentificationData) {
    ASSERT_TRUE(tertiaryInfo);

    // Display IDs should be unique.
    EXPECT_NE(primaryInfo->id, secondaryInfo->id);
    EXPECT_NE(primaryInfo->id, tertiaryInfo->id);
    EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
    EXPECT_EQ(4633257497453176576, primaryInfo->id.value);
    EXPECT_EQ(4621520285560261121, secondaryInfo->id.value);
    EXPECT_EQ(4633127902230889474, tertiaryInfo->id.value);
}

TEST(DisplayIdentificationTest, deviceProductInfo) {