1 /*
2  * Copyright 2020 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ImageHashManager"
19 
20 #include <android/hardware_buffer_jni.h>
21 #include <log/log_main.h>
22 #include <nativehelper/JNIHelp.h>
23 #include <nativehelper/scoped_utf_chars.h>
24 #include <array>
25 #include <string>
26 
27 #include <ImageHashManager.h>
28 
29 namespace android {
30 
31 class BufferWrapper {
32 public:
BufferWrapper(AHardwareBuffer * buffer)33     BufferWrapper(AHardwareBuffer* buffer) : mBuffer(buffer) {}
34 
lock(uint8_t ** buf)35     int lock(uint8_t** buf) {
36         if (mIsLocked) {
37             return -1;
38         }
39 
40         int status = AHardwareBuffer_lock(mBuffer,
41                                           AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
42                                                   AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER,
43                                           -1 /* fence */, nullptr /* rect */,
44                                           reinterpret_cast<void**>(buf));
45         if (!status) {
46             mIsLocked = true;
47         }
48         return status;
49     }
50 
unlock()51     void unlock() {
52         if (!mIsLocked) {
53             return;
54         }
55         mIsLocked = false;
56         AHardwareBuffer_unlock(mBuffer, nullptr);
57     }
58 
~BufferWrapper()59     ~BufferWrapper() { unlock(); }
60 
61 private:
62     AHardwareBuffer* mBuffer = nullptr;
63     bool mIsLocked = false;
64 };
65 
nativeGenerateHash(JNIEnv * env,jobject clazz,jobject hardwareBufferObj,jstring hashAlgorithmString)66 static jbyteArray nativeGenerateHash(JNIEnv* env, jobject clazz, jobject hardwareBufferObj,
67                                      jstring hashAlgorithmString) {
68     ScopedUtfChars hashAlgorithmChars(env, hashAlgorithmString);
69     std::string hashAlgorithm(hashAlgorithmChars.c_str());
70 
71     transform(hashAlgorithm.begin(), hashAlgorithm.end(), hashAlgorithm.begin(), ::tolower);
72 
73     AHardwareBuffer* hardwareBuffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj);
74 
75     AHardwareBuffer_Desc bufferDesc;
76     AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
77 
78     BufferWrapper bufferWrapper(hardwareBuffer);
79     uint8_t* buf = nullptr;
80     int32_t status = bufferWrapper.lock(&buf);
81 
82     if (status) {
83         ALOGE("Failed to lock buffer status=%d", status);
84         return nullptr;
85     }
86 
87     std::array<uint8_t, 8> imageHash;
88     status = ImageHashManager::generateHash(hashAlgorithm, buf, bufferDesc, &imageHash);
89 
90     if (status) {
91         ALOGE("Failed to generate hash status=%d", status);
92         return nullptr;
93     }
94 
95     jbyteArray ret = env->NewByteArray(8);
96     if (ret) {
97         jbyte* bytes = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
98         if (bytes) {
99             memcpy(bytes, imageHash.data(), 8);
100             env->ReleasePrimitiveArrayCritical(ret, bytes, 0);
101         }
102     }
103     return ret;
104 }
105 
106 // clang-format off
107 static const JNINativeMethod gMethods[] = {
108     {"nativeGenerateHash", "(Landroid/hardware/HardwareBuffer;Ljava/lang/String;)[B",
109                 (void *)nativeGenerateHash},
110 };
111 // clang-format on
112 
register_android_ext_services_displayhash_DisplayHashAlgorithm(JNIEnv * env)113 int register_android_ext_services_displayhash_DisplayHashAlgorithm(JNIEnv* env) {
114     int res = jniRegisterNativeMethods(env, "android/ext/services/displayhash/ImageHashManager",
115                                        gMethods, NELEM(gMethods));
116     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
117 
118     return res;
119 }
120 
121 } /* namespace android */
122