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