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 
18 #define LOG_TAG "FrameRateCtsActivity"
19 
20 #include <android/hardware_buffer.h>
21 #include <android/hardware_buffer_jni.h>
22 #include <android/log.h>
23 #include <android/native_window.h>
24 #include <android/native_window_jni.h>
25 #include <android/rect.h>
26 #include <android/surface_control.h>
27 #include <jni.h>
28 #include <utils/Errors.h>
29 
30 #include <array>
31 #include <string>
32 
33 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
34 
35 using namespace android;
36 
37 namespace {
38 
39 class Buffer {
40 public:
Buffer(int width,int height,int rgbaColor)41     Buffer(int width, int height, int rgbaColor) {
42         AHardwareBuffer_Desc desc;
43         memset(&desc, 0, sizeof(desc));
44         desc.width = width;
45         desc.height = height;
46         desc.layers = 1;
47         desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
48         desc.usage =
49                 AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
50         int rc = AHardwareBuffer_allocate(&desc, &mBuffer);
51         if (rc < 0 || mBuffer == nullptr) {
52             ALOGE("AHardwareBuffer_allocate failed: %s (%d)", strerror(-rc), -rc);
53             return;
54         }
55         int8_t* buf = nullptr;
56         int32_t bytesPerPixel = 0;
57         int32_t bytesPerStride = 0;
58         std::string lockFunctionName = "AHardwareBuffer_lockAndGetInfo";
59         rc = AHardwareBuffer_lockAndGetInfo(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
60                                             /*fence=*/-1,
61                                             /*rect=*/nullptr, reinterpret_cast<void**>(&buf),
62                                             &bytesPerPixel, &bytesPerStride);
63         if (rc == INVALID_OPERATION) {
64             // Older versions of gralloc don't implement AHardwareBuffer_lockAndGetInfo(). Fall back
65             // to AHardwareBuffer_lock().
66             lockFunctionName = "AHardwareBuffer_lock";
67             bytesPerPixel = 4;
68             bytesPerStride = width * bytesPerPixel;
69             rc = AHardwareBuffer_lock(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
70                                       /*fence=*/-1,
71                                       /*rect=*/nullptr, reinterpret_cast<void**>(&buf));
72         }
73         if (rc < 0 || buf == nullptr) {
74             ALOGE("%s failed: %s (%d)", lockFunctionName.c_str(), strerror(-rc), -rc);
75             AHardwareBuffer_release(mBuffer);
76             mBuffer = nullptr;
77             return;
78         }
79 
80         // There's a bug where Qualcomm returns pixels per stride instead of bytes per stride. See
81         // b/149601846.
82         if (bytesPerStride < width * bytesPerPixel) {
83             bytesPerStride *= bytesPerPixel;
84         }
85 
86         int8_t* rgbaBytes = reinterpret_cast<int8_t*>(&rgbaColor);
87         for (int row = 0; row < height; row++) {
88             int8_t* ptr = buf + row * bytesPerStride;
89             for (int col = 0; col < width; col++, ptr += bytesPerPixel) {
90                 ptr[0] = rgbaBytes[0];
91                 ptr[1] = rgbaBytes[1];
92                 ptr[2] = rgbaBytes[2];
93                 ptr[3] = rgbaBytes[3];
94             }
95         }
96 
97         rc = AHardwareBuffer_unlock(mBuffer, /*fence=*/nullptr);
98         if (rc < 0) {
99             ALOGE("AHardwareBuffer_unlock failed: %s (%d)", strerror(-rc), -rc);
100             AHardwareBuffer_release(mBuffer);
101             mBuffer = nullptr;
102             return;
103         }
104     }
105 
~Buffer()106     ~Buffer() {
107         if (mBuffer) {
108             AHardwareBuffer_release(mBuffer);
109         }
110     }
111 
isValid() const112     bool isValid() const { return mBuffer != nullptr; }
getBuffer() const113     AHardwareBuffer* getBuffer() const { return mBuffer; }
114 
115 private:
116     AHardwareBuffer* mBuffer = nullptr;
117 };
118 
119 class Surface {
120 public:
Surface(ANativeWindow * parentWindow,const std::string & name,int left,int top,int right,int bottom)121     Surface(ANativeWindow* parentWindow, const std::string& name, int left, int top, int right,
122             int bottom) {
123         mSurface = ASurfaceControl_createFromWindow(parentWindow, name.c_str());
124         if (mSurface == nullptr) {
125             return;
126         }
127 
128         mWidth = right - left;
129         mHeight = bottom - top;
130         ARect source{0, 0, mWidth, mHeight};
131         ARect dest{left, top, right, bottom};
132         ASurfaceTransaction* transaction = ASurfaceTransaction_create();
133         ASurfaceTransaction_setGeometry(transaction, mSurface, source, dest,
134                                         ANATIVEWINDOW_TRANSFORM_IDENTITY);
135         ASurfaceTransaction_apply(transaction);
136         ASurfaceTransaction_delete(transaction);
137     }
138 
~Surface()139     ~Surface() {
140         ASurfaceTransaction* transaction = ASurfaceTransaction_create();
141         ASurfaceTransaction_reparent(transaction, mSurface, nullptr);
142         ASurfaceTransaction_apply(transaction);
143         ASurfaceTransaction_delete(transaction);
144         ASurfaceControl_release(mSurface);
145     }
146 
isValid() const147     bool isValid() const { return mSurface != nullptr; }
getSurfaceControl() const148     ASurfaceControl* getSurfaceControl() const { return mSurface; }
getWidth() const149     int getWidth() const { return mWidth; }
getHeight() const150     int getHeight() const { return mHeight; }
151 
152 private:
153     ASurfaceControl* mSurface = nullptr;
154     int mWidth = 0;
155     int mHeight = 0;
156 };
157 
158 struct ANativeWindowRAII {
ANativeWindowRAII__anonb7bd83a40111::ANativeWindowRAII159     ANativeWindowRAII(ANativeWindow *anw = nullptr) :
160          mNw(anw) {
161     }
~ANativeWindowRAII__anonb7bd83a40111::ANativeWindowRAII162     ~ANativeWindowRAII() {
163         if (mNw != nullptr) {
164             ANativeWindow_release(mNw);
165         }
166     }
167     ANativeWindow* mNw;
168 };
169 
nativeWindowSetFrameRate(JNIEnv * env,jclass,jobject jSurface,jfloat frameRate,jint compatibility,jint changeFrameRateStrategy)170 jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate,
171                               jint compatibility, jint changeFrameRateStrategy) {
172     ANativeWindowRAII window;
173     if (jSurface) {
174         window.mNw = ANativeWindow_fromSurface(env, jSurface);
175     }
176     if (changeFrameRateStrategy == ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS) {
177         return ANativeWindow_setFrameRate(window.mNw, frameRate, compatibility);
178     }
179 
180     return ANativeWindow_setFrameRateWithChangeStrategy(window.mNw, frameRate, compatibility,
181             changeFrameRateStrategy);
182 }
183 
nativeWindowClearFrameRate(JNIEnv * env,jclass,jobject jSurface)184 jint nativeWindowClearFrameRate(JNIEnv* env, jclass, jobject jSurface) {
185     ANativeWindowRAII window;
186     if (jSurface) {
187         window.mNw = ANativeWindow_fromSurface(env, jSurface);
188     }
189 
190     return ANativeWindow_clearFrameRate(window.mNw);
191 }
192 
surfaceControlCreate(JNIEnv * env,jclass,jobject jParentSurface,jstring jName,jint left,jint top,jint right,jint bottom)193 jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface, jstring jName, jint left,
194                            jint top, jint right, jint bottom) {
195     if (!jParentSurface || !jName) {
196         return 0;
197     }
198     ANativeWindowRAII parentWindow = ANativeWindow_fromSurface(env, jParentSurface);
199     if (!parentWindow.mNw) {
200         return 0;
201     }
202 
203     const char* name = env->GetStringUTFChars(jName, nullptr);
204     std::string strName = name;
205     env->ReleaseStringUTFChars(jName, name);
206 
207     Surface* surface = new Surface(parentWindow.mNw, strName, left, top, right, bottom);
208     if (!surface->isValid()) {
209         delete surface;
210         return 0;
211     }
212 
213     return reinterpret_cast<jlong>(surface);
214 }
215 
surfaceControlDestroy(JNIEnv *,jclass,jlong surfaceControlLong)216 void surfaceControlDestroy(JNIEnv*, jclass, jlong surfaceControlLong) {
217     if (surfaceControlLong == 0) {
218         return;
219     }
220     delete reinterpret_cast<Surface*>(surfaceControlLong);
221 }
222 
surfaceControlSetFrameRate(JNIEnv *,jclass,jlong surfaceControlLong,jfloat frameRate,jint compatibility,jint changeFrameRateStrategy)223 void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate,
224                                 jint compatibility, jint changeFrameRateStrategy) {
225     ASurfaceControl* surfaceControl =
226             reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
227     ASurfaceTransaction* transaction = ASurfaceTransaction_create();
228     if (changeFrameRateStrategy == ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS) {
229         ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate,
230             compatibility);
231     } else {
232         ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surfaceControl, frameRate,
233             compatibility, changeFrameRateStrategy);
234     }
235     ASurfaceTransaction_apply(transaction);
236     ASurfaceTransaction_delete(transaction);
237 }
238 
surfaceControlClearFrameRate(JNIEnv *,jclass,jlong surfaceControlLong)239 void surfaceControlClearFrameRate(JNIEnv*, jclass, jlong surfaceControlLong) {
240     ASurfaceControl* surfaceControl =
241             reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
242     ASurfaceTransaction* transaction = ASurfaceTransaction_create();
243     ASurfaceTransaction_clearFrameRate(transaction, surfaceControl);
244     ASurfaceTransaction_apply(transaction);
245     ASurfaceTransaction_delete(transaction);
246 }
247 
surfaceControlSetVisibility(JNIEnv *,jclass,jlong surfaceControlLong,jboolean visible)248 void surfaceControlSetVisibility(JNIEnv*, jclass, jlong surfaceControlLong, jboolean visible) {
249     ASurfaceControl* surfaceControl =
250             reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
251     ASurfaceTransaction* transaction = ASurfaceTransaction_create();
252     ASurfaceTransaction_setVisibility(transaction, surfaceControl,
253                                       visible == JNI_TRUE ? ASURFACE_TRANSACTION_VISIBILITY_SHOW
254                                                           : ASURFACE_TRANSACTION_VISIBILITY_HIDE);
255     ASurfaceTransaction_apply(transaction);
256     ASurfaceTransaction_delete(transaction);
257 }
258 
surfaceControlPostBuffer(JNIEnv *,jclass,jlong surfaceControlLong,jint argbColor)259 jboolean surfaceControlPostBuffer(JNIEnv*, jclass, jlong surfaceControlLong, jint argbColor) {
260     Surface* surface = reinterpret_cast<Surface*>(surfaceControlLong);
261     ASurfaceControl* surfaceControl = surface->getSurfaceControl();
262     // Android's Color.* values are represented as ARGB. Convert to RGBA.
263     int32_t rgbaColor = 0;
264     int8_t* rgbaColorBytes = reinterpret_cast<int8_t*>(&rgbaColor);
265     rgbaColorBytes[0] = (argbColor >> 16) & 0xff;
266     rgbaColorBytes[1] = (argbColor >> 8) & 0xff;
267     rgbaColorBytes[2] = (argbColor >> 0) & 0xff;
268     rgbaColorBytes[3] = (argbColor >> 24) & 0xff;
269 
270     Buffer buffer(surface->getWidth(), surface->getHeight(), rgbaColor);
271     if (!buffer.isValid()) {
272         return JNI_FALSE;
273     }
274 
275     ASurfaceTransaction* transaction = ASurfaceTransaction_create();
276     ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer.getBuffer(), -1);
277     ASurfaceTransaction_apply(transaction);
278     ASurfaceTransaction_delete(transaction);
279     return JNI_TRUE;
280 }
281 
282 const std::array<JNINativeMethod, 8> JNI_METHODS = {{
283         {"nativeWindowSetFrameRate", "(Landroid/view/Surface;FII)I",
284          (void*)nativeWindowSetFrameRate},
285         {"nativeSurfaceControlCreate", "(Landroid/view/Surface;Ljava/lang/String;IIII)J",
286          (void*)surfaceControlCreate},
287         {"nativeSurfaceControlDestroy", "(J)V", (void*)surfaceControlDestroy},
288         {"nativeSurfaceControlSetFrameRate", "(JFII)V", (void*)surfaceControlSetFrameRate},
289         {"nativeSurfaceControlSetVisibility", "(JZ)V", (void*)surfaceControlSetVisibility},
290         {"nativeSurfaceControlPostBuffer", "(JI)Z", (void*)surfaceControlPostBuffer},
291         {"nativeWindowClearFrameRate", "(Landroid/view/Surface;)I",
292          (void*)nativeWindowClearFrameRate},
293         {"nativeSurfaceControlClearFrameRate", "(J)V", (void*)surfaceControlClearFrameRate},
294 }};
295 
296 } // namespace
297 
register_android_graphics_cts_FrameRateCtsActivity(JNIEnv * env)298 int register_android_graphics_cts_FrameRateCtsActivity(JNIEnv* env) {
299     jclass clazz = env->FindClass("android/graphics/cts/FrameRateCtsActivity");
300     return env->RegisterNatives(clazz, JNI_METHODS.data(), JNI_METHODS.size());
301 }
302