1 /*
2  * Copyright (C) 2022 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 #include <android/gui/IHdrConversionConstants.h>
18 #include <android_util_Binder.h>
19 #include <gui/SurfaceComposerClient.h>
20 #include <jni.h>
21 #include <nativehelper/ScopedPrimitiveArray.h>
22 #include <nativehelper/ScopedUtfChars.h>
23 
24 namespace android {
25 
nativeCreateDisplay(JNIEnv * env,jclass clazz,jstring nameObj,jboolean secure,jfloat requestedRefreshRate)26 static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj, jboolean secure,
27                                    jfloat requestedRefreshRate) {
28     ScopedUtfChars name(env, nameObj);
29     sp<IBinder> token(SurfaceComposerClient::createDisplay(String8(name.c_str()), bool(secure),
30                                                            requestedRefreshRate));
31     return javaObjectForIBinder(env, token);
32 }
33 
nativeDestroyDisplay(JNIEnv * env,jclass clazz,jobject tokenObj)34 static void nativeDestroyDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
35     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
36     if (token == NULL) return;
37     SurfaceComposerClient::destroyDisplay(token);
38 }
39 
nativeOverrideHdrTypes(JNIEnv * env,jclass clazz,jobject tokenObject,jintArray jHdrTypes)40 static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObject,
41                                    jintArray jHdrTypes) {
42     sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
43     if (token == nullptr || jHdrTypes == nullptr) return;
44 
45     ScopedIntArrayRO hdrTypes(env, jHdrTypes);
46     size_t numHdrTypes = hdrTypes.size();
47 
48     std::vector<ui::Hdr> hdrTypesVector;
49     hdrTypesVector.reserve(numHdrTypes);
50     for (int i = 0; i < numHdrTypes; i++) {
51         hdrTypesVector.push_back(static_cast<ui::Hdr>(hdrTypes[i]));
52     }
53 
54     status_t error = SurfaceComposerClient::overrideHdrTypes(token, hdrTypesVector);
55     if (error != NO_ERROR) {
56         jniThrowExceptionFmt(env, "java/lang/SecurityException",
57                              "ACCESS_SURFACE_FLINGER is missing");
58     }
59 }
60 
nativeSetHdrConversionMode(JNIEnv * env,jclass clazz,jint hdrConversionMode,jint preferredHdrOutputType,jintArray autoHdrOutputTypes,jint autoHdrOutputTypesLength)61 static int nativeSetHdrConversionMode(JNIEnv* env, jclass clazz, jint hdrConversionMode,
62                                       jint preferredHdrOutputType, jintArray autoHdrOutputTypes,
63                                       jint autoHdrOutputTypesLength) {
64     gui::HdrConversionStrategy hdrConversionStrategy;
65     switch (hdrConversionMode) {
66         case gui::IHdrConversionConstants::HdrConversionModePassthrough: {
67             hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::passthrough>(true);
68             break;
69         }
70         case gui::IHdrConversionConstants::HdrConversionModeAuto: {
71             jint* autoHdrOutputTypesArray = env->GetIntArrayElements(autoHdrOutputTypes, 0);
72             std::vector<int> autoHdrOutputTypesVector(autoHdrOutputTypesLength);
73             for (int i = 0; i < autoHdrOutputTypesLength; i++) {
74                 autoHdrOutputTypesVector[i] = autoHdrOutputTypesArray[i];
75             }
76             hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::autoAllowedHdrTypes>(
77                     autoHdrOutputTypesVector);
78             break;
79         }
80         case gui::IHdrConversionConstants::HdrConversionModeForce: {
81             hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::forceHdrConversion>(
82                     preferredHdrOutputType);
83             break;
84         }
85     }
86     ui::Hdr prefHdrType;
87     SurfaceComposerClient::setHdrConversionStrategy(hdrConversionStrategy, &prefHdrType);
88     if (static_cast<jint>(prefHdrType) == 0) {
89         return -1;
90     } else {
91         return static_cast<jint>(prefHdrType);
92     }
93 }
94 
nativeGetSupportedHdrOutputTypes(JNIEnv * env,jclass clazz)95 static jintArray nativeGetSupportedHdrOutputTypes(JNIEnv* env, jclass clazz) {
96     std::vector<gui::HdrConversionCapability> hdrConversionCapabilities;
97     SurfaceComposerClient::getHdrConversionCapabilities(&hdrConversionCapabilities);
98 
99     // Extract unique HDR output types.
100     std::set<int> hdrOutputTypes;
101     for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
102         // Filter out the value for SDR which is 0.
103         if (hdrConversionCapability.outputType > 0) {
104             hdrOutputTypes.insert(hdrConversionCapability.outputType);
105         }
106     }
107     jintArray array = env->NewIntArray(hdrOutputTypes.size());
108     if (array == nullptr) {
109         jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
110         return nullptr;
111     }
112     jint* arrayValues = env->GetIntArrayElements(array, 0);
113     size_t index = 0;
114     for (auto hdrOutputType : hdrOutputTypes) {
115         arrayValues[index++] = static_cast<jint>(hdrOutputType);
116     }
117     env->ReleaseIntArrayElements(array, arrayValues, 0);
118     return array;
119 }
120 
nativeGetHdrOutputTypesWithLatency(JNIEnv * env,jclass clazz)121 static jintArray nativeGetHdrOutputTypesWithLatency(JNIEnv* env, jclass clazz) {
122     std::vector<gui::HdrConversionCapability> hdrConversionCapabilities;
123     SurfaceComposerClient::getHdrConversionCapabilities(&hdrConversionCapabilities);
124 
125     // Extract unique HDR output types with latency.
126     std::set<int> hdrOutputTypes;
127     for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
128         if (hdrConversionCapability.outputType > 0 && hdrConversionCapability.addsLatency) {
129             hdrOutputTypes.insert(hdrConversionCapability.outputType);
130         }
131     }
132     jintArray array = env->NewIntArray(hdrOutputTypes.size());
133     if (array == nullptr) {
134         jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
135         return nullptr;
136     }
137     jint* arrayValues = env->GetIntArrayElements(array, 0);
138     size_t index = 0;
139     for (auto hdrOutputType : hdrOutputTypes) {
140         arrayValues[index++] = static_cast<jint>(hdrOutputType);
141     }
142     env->ReleaseIntArrayElements(array, arrayValues, 0);
143     return array;
144 }
145 
nativeGetHdrOutputConversionSupport(JNIEnv * env,jclass clazz)146 static jboolean nativeGetHdrOutputConversionSupport(JNIEnv* env, jclass clazz) {
147     bool isSupported;
148     status_t err = SurfaceComposerClient::getHdrOutputConversionSupport(&isSupported);
149     if (err == OK) {
150         return isSupported;
151     }
152     return JNI_FALSE;
153 }
154 
nativeGetPhysicalDisplayIds(JNIEnv * env,jclass clazz)155 static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
156     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
157     ScopedLongArrayRW values(env, env->NewLongArray(displayIds.size()));
158     if (values.get() == nullptr) {
159         jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
160         return nullptr;
161     }
162 
163     for (size_t i = 0; i < displayIds.size(); ++i) {
164         values[i] = static_cast<jlong>(displayIds[i].value);
165     }
166 
167     return values.getJavaArray();
168 }
169 
nativeGetPhysicalDisplayToken(JNIEnv * env,jclass clazz,jlong physicalDisplayId)170 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
171     const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
172     if (!id) return nullptr;
173     sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
174     return javaObjectForIBinder(env, token);
175 }
176 
177 // ----------------------------------------------------------------------------
178 
179 static const JNINativeMethod sDisplayMethods[] = {
180         // clang-format off
181     {"nativeCreateDisplay", "(Ljava/lang/String;ZF)Landroid/os/IBinder;",
182             (void*)nativeCreateDisplay },
183     {"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
184             (void*)nativeDestroyDisplay },
185     {"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
186                 (void*)nativeOverrideHdrTypes },
187     {"nativeGetPhysicalDisplayIds", "()[J",
188             (void*)nativeGetPhysicalDisplayIds },
189     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
190             (void*)nativeGetPhysicalDisplayToken },
191     {"nativeSetHdrConversionMode", "(II[II)I",
192             (void*)nativeSetHdrConversionMode },
193     {"nativeGetSupportedHdrOutputTypes", "()[I",
194             (void*)nativeGetSupportedHdrOutputTypes },
195     {"nativeGetHdrOutputTypesWithLatency", "()[I",
196             (void*)nativeGetHdrOutputTypesWithLatency },
197     {"nativeGetHdrOutputConversionSupport", "()Z",
198             (void*) nativeGetHdrOutputConversionSupport },
199         // clang-format on
200 };
201 
register_com_android_server_display_DisplayControl(JNIEnv * env)202 int register_com_android_server_display_DisplayControl(JNIEnv* env) {
203     return jniRegisterNativeMethods(env, "com/android/server/display/DisplayControl",
204                                     sDisplayMethods, NELEM(sDisplayMethods));
205 }
206 
207 } // namespace android
208