Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #pragma once |
| 18 | |
| 19 | #include <stdint.h> |
| 20 | #include <stdlib.h> |
| 21 | |
| 22 | // Set TRACE_CHECKSUMHELPER to 1 to debug creation/destruction of GLprotocol |
| 23 | // instances. |
| 24 | #define TRACE_CHECKSUMHELPER 0 |
| 25 | |
| 26 | #if TRACE_CHECKSUMHELPER |
| 27 | #define LOG_CHECKSUMHELPER(x...) fprintf(stderr, x) |
| 28 | #else |
| 29 | #define LOG_CHECKSUMHELPER(x...) |
| 30 | #endif |
| 31 | |
| 32 | // ChecksumCalculator adds checksum as an array of bytes to GL pipe communication, which |
| 33 | // size depends on the protocol version. Each pipe should use one ChecksumCalculator. |
| 34 | // It can: |
| 35 | // (1) take a list of buffers one by one and compute their checksum string, |
| 36 | // in this case the checksum should be as the data in those buffers are |
| 37 | // concatenated; |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 38 | // (2) compute the checksum of the buffer list, then either write them into |
| 39 | // a buffer provided by user, or compare it against a checksum provided |
| 40 | // by user |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 41 | // (3) support different checksum version in future. |
| 42 | // |
| 43 | // For backward compatibility, checksum version 0 behaves the same as there is |
| 44 | // no checksum (i.e., checksumByteSize returns 0, validate always returns true, |
| 45 | // addBuffer and writeCheckSum does nothing). |
| 46 | // |
| 47 | // Notice that to detect package lost, ChecksumCalculator also keeps track of how |
| 48 | // many times it generates/validates checksums, and might use it as part of the |
| 49 | // checksum. |
| 50 | // |
| 51 | // To evaluate checksums from a list of data buffers buf1, buf2... Please call |
| 52 | // addBuffer(buf1, buf1len), addBuffer(buf2, buf2len) ... in order. |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 53 | // Then if the checksum needs to be encoded into a buffer, one needs to allocate |
| 54 | // a checksum buffer with size checksumByteSize(), and call |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 55 | // writeChecksum(checksumBuffer) to write the checksum to the buffer. |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 56 | // If the checksum needs to be validated against an existing one, one needs to |
| 57 | // call validate(existChecksum, existChecksumLen). |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 58 | // |
| 59 | // The checksum generator and validator must be set to the same version, and |
| 60 | // the validator must check ALL checksums in the order they are generated, |
| 61 | // otherwise the validation function will return false. |
| 62 | // |
| 63 | // It is allowed to change the checksum version between calculating two |
| 64 | // checksums. This is designed for backward compatibility reason. |
| 65 | // |
| 66 | // Example 1, encoding and decoding: |
| 67 | // |
| 68 | // bool testChecksum(void* buf, size_t bufLen) { |
| 69 | // // encoding message |
| 70 | // ChecksumCalculator encoder; |
| 71 | // encoder.setVersion(1); |
| 72 | // encoder.addBuffer(buf, bufLen); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 73 | // std::vector<unsigned char> message(bufLen + encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 74 | // memcpy(&message[0], buf, bufLen); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 75 | // encoder.writeChecksum(&message[0] + bufLen, encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 76 | // |
| 77 | // // decoding message |
| 78 | // ChecksumCalculator decoder; |
| 79 | // decoder.setVersion(1); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 80 | // decoder.addBuffer(&message[0], bufLen); |
| 81 | // return decoder.validate(&message[0] + bufLen, decoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 82 | // } |
| 83 | // The return value is true. |
| 84 | // |
| 85 | // Example 2, decoding will fail if the order of messages is wrong: |
| 86 | // |
| 87 | // bool testChecksumOrder(void* buf1, size_t bufLen1, |
| 88 | // void* buf2, size_t bufLen2) { |
| 89 | // // encoding messages |
| 90 | // ChecksumCalculator encoder; |
| 91 | // encoder.setVersion(1); |
| 92 | // |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 93 | // std::vector<unsigned char> message1(bufLen1 + encoder.checksumByteSize()); |
| 94 | // std::vector<unsigned char> message2(bufLen2 + encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 95 | // |
| 96 | // encoder.addBuffer(buf1, bufLen1); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 97 | // std::vector<unsigned char> message1(bufLen1 + encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 98 | // memcpy(&message1[0], buf1, bufLen1); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 99 | // encoder.writeChecksum(&message1[0] + bufLen1, encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 100 | // |
| 101 | // encoder.addBuffer(buf2, bufLen2); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 102 | // std::vector<unsigned char> message2(bufLen2 + encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 103 | // memcpy(&message2[0], buf2, bufLen2); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 104 | // encoder.writeChecksum(&message2[0] + bufLen2, encoder.checksumByteSize()); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 105 | // |
| 106 | // // decoding messages |
| 107 | // ChecksumCalculator decoder; |
| 108 | // decoder.setVersion(1); |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 109 | // decoder.addBuffer(&message2[0], bufLen2); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 110 | // // returns false because the decoding order is not consistent with |
| 111 | // // encoding order |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 112 | // if (!decoder.validate(&message2[0]+bufLen2, decoder.checksumByteSize())) { |
| 113 | // return false; |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 114 | // } |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 115 | // |
| 116 | // decoder.addBuffer(&message1[0], bufLen1); |
| 117 | // if (!decoder.validate(&message1[0]+bufLen1, decoder.checksumByteSize())) { |
| 118 | // return false; |
| 119 | // } |
| 120 | // |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 121 | // return false; |
| 122 | // } |
| 123 | |
| 124 | class ChecksumCalculator { |
| 125 | public: |
Yurii Zubrytskyi | ac0e8ca | 2016-11-04 11:57:02 -0700 | [diff] [blame] | 126 | enum Sizes { |
| 127 | kVersion1ChecksumSize = 8, |
| 128 | kMaxChecksumSize = kVersion1ChecksumSize |
| 129 | }; |
| 130 | |
Yahan Zhou | d906928 | 2016-06-17 17:40:14 -0700 | [diff] [blame] | 131 | ChecksumCalculator(); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 132 | // Get and set current checksum version |
| 133 | uint32_t getVersion() const { return m_version; } |
| 134 | // Call setVersion to set a checksum version. It should be called before |
| 135 | // addBuffer(), writeChecksum() and validate(). And it should be called |
| 136 | // exact once per rendering thread if both host and guest support checksum. |
| 137 | // It won't be called if either host or guest does not support checksum. |
| 138 | bool setVersion(uint32_t version); |
| 139 | |
| 140 | // Maximum supported checksum version |
| 141 | static uint32_t getMaxVersion(); |
| 142 | // A version string that looks like "ANDROID_EMU_CHECKSUM_HELPER_v1" |
| 143 | // Used multiple times when the guest queries the maximum supported version |
| 144 | // from the host. |
| 145 | // The library owns the returned pointer. The returned pointer will be |
| 146 | // deconstructed when unloading library. |
| 147 | static const char* getMaxVersionStr(); |
| 148 | static const char* getMaxVersionStrPrefix(); |
| 149 | |
| 150 | // Size of checksum in the current version |
| 151 | size_t checksumByteSize() const; |
| 152 | |
| 153 | // Update the current checksum value from the data |
| 154 | // at |buf| of |bufLen| bytes. Once all buffers |
| 155 | // have been added, call writeChecksum() to store |
| 156 | // the final checksum value and reset its state. |
| 157 | void addBuffer(const void* buf, size_t bufLen); |
| 158 | // Write the checksum from the list of buffers to outputChecksum |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 159 | // Will reset the list of buffers by calling resetChecksum. |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 160 | // Return false if the buffer is not long enough |
| 161 | // Please query buffer size from checksumByteSize() |
| 162 | bool writeChecksum(void* outputChecksum, size_t outputChecksumLen); |
| 163 | // Restore the states for computing checksums. |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 164 | // Automatically called at the end of writeChecksum and validate. |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 165 | // Can also be used to abandon the current checksum being calculated. |
| 166 | // Notes: it doesn't update the internal read / write counter |
| 167 | void resetChecksum(); |
| 168 | |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 169 | // Calculate the checksum from the list of buffers and |
| 170 | // compare it with the checksum encoded in expectedChecksum |
| 171 | // Will reset the list of buffers by calling resetChecksum. |
| 172 | bool validate(const void* expectedChecksum, size_t expectedChecksumLen); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 173 | protected: |
Yahan Zhou | d906928 | 2016-06-17 17:40:14 -0700 | [diff] [blame] | 174 | uint32_t m_version; |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 175 | // A temporary state used to compute the total length of a list of buffers, |
| 176 | // if addBuffer is called. |
Yahan Zhou | d906928 | 2016-06-17 17:40:14 -0700 | [diff] [blame] | 177 | uint32_t m_numRead; |
| 178 | uint32_t m_numWrite; |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 179 | // m_isEncodingChecksum is true when between addBuffer and writeChecksum |
Yahan Zhou | d906928 | 2016-06-17 17:40:14 -0700 | [diff] [blame] | 180 | bool m_isEncodingChecksum; |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 181 | private: |
| 182 | // Compute a 32bit checksum |
| 183 | // Used in protocol v1 |
Yahan Zhou | e222fd5 | 2016-03-16 12:41:08 -0700 | [diff] [blame] | 184 | uint32_t computeV1Checksum(); |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 185 | // The buffer used in protocol version 1 to compute checksum. |
Yahan Zhou | d906928 | 2016-06-17 17:40:14 -0700 | [diff] [blame] | 186 | uint32_t m_v1BufferTotalLength; |
Yahan Zhou | b7f0908 | 2016-03-10 11:45:02 -0800 | [diff] [blame] | 187 | }; |