1 /*
2 * Copyright 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 #include <jni.h>
17 #include <inttypes.h>
18 #include <android/fdsan.h>
19 #include <android/sync.h>
20 #include <android/log.h>
21 #include <cstdint>
22 #include <poll.h>
23 #include <unistd.h>
24 #include <sys/syscall.h>
25 #include <android/file_descriptor_jni.h>
26 #include <errno.h>
27 #include <dlfcn.h>
28 #include <mutex>
29
30 static constexpr int64_t SIGNAL_TIME_INVALID = -1;
31 static constexpr int64_t SIGNAL_TIME_PENDING = INT64_MAX;
32
33 #define SYNC_FENCE "SYNC_FENCE"
34 #define ALOGE(msg, ...) \
35 __android_log_print(ANDROID_LOG_ERROR, SYNC_FENCE, (msg), __VA_ARGS__)
36 #define ALOGW(msg, ...) \
37 __android_log_print(ANDROID_LOG_ERROR, SYNC_FENCE, (msg))
38
SyncFence_nClose(JNIEnv * env,jobject,jint fd)39 void SyncFence_nClose(JNIEnv *env, jobject, jint fd) {
40 close(fd);
41 }
42
43 /**
44 * Flag to ensure that we only attempt to load libsync.so once
45 */
46 static std::once_flag load_sync_lib_flag;
47
48 static std::once_flag load_fdsan_lib_flag;
49
50 /**
51 * Function pointer to the dynamically linked sync_file_info method
52 */
53 static decltype(&sync_file_info) sync_file_info_ptr = nullptr;
54
55 /**
56 * Function pointer to the dynamically linked sync_file_info_free method
57 */
58 static decltype(&sync_file_info_free) sync_file_info_free_ptr = nullptr;
59
60 /**
61 * Function pointer to resolve the corresponding owner tag from a file descriptor
62 */
63 static uint64_t (*android_fdsan_get_owner_tag_ptr)(int) = nullptr;
64
65 /**
66 * Function pointer to close the corresponding file descriptor with the given tag
67 */
68 static int (*android_fdsan_close_with_tag_ptr)(int, uint64_t) = nullptr;
69
70 /**
71 * Helper method to attempt to load libsync.so. It only attempts to load the library once and the
72 * result is cached. On successful load, this method resolves both sync_file_info and
73 * sync_file_info_free methods as well.
74 */
load_libsync()75 static void load_libsync() {
76 std::call_once(load_sync_lib_flag, []() {
77 void* handle = dlopen("libsync.so", RTLD_NOW);
78 if (handle) {
79 sync_file_info_ptr =
80 reinterpret_cast<decltype(&sync_file_info)>(
81 dlsym(handle, "sync_file_info"));
82 if (!sync_file_info_ptr) {
83 ALOGW("Unable to resolve sync_file_info from dlsym");
84 }
85 sync_file_info_free_ptr =
86 reinterpret_cast<decltype(&sync_file_info_free)>(
87 dlsym(handle, "sync_file_info_free"));
88 if (!sync_file_info_free_ptr) {
89 ALOGW("Unable to resolve sync_file_info_free from dlsym");
90 }
91 } else {
92 ALOGW("Unable to load libsync.so");
93 sync_file_info_ptr = nullptr;
94 sync_file_info_free_ptr = nullptr;
95 }
96 });
97 }
98
load_lib_fdsan()99 static void load_lib_fdsan() {
100 std::call_once(load_fdsan_lib_flag, []() {
101 void* handle = dlopen("libc.so", RTLD_NOW);
102 if (handle) {
103 android_fdsan_get_owner_tag_ptr =
104 reinterpret_cast<uint64_t (*)(int)>(
105 dlsym(handle, "android_fdsan_get_owner_tag"));
106 if (!android_fdsan_get_owner_tag_ptr) {
107 ALOGW("Unable to resolve android_fdsan_get_owner_tag");
108 }
109
110 android_fdsan_close_with_tag_ptr =
111 reinterpret_cast<int(*)(int, uint64_t)>(
112 dlsym(handle, "android_fdsan_close_with_tag"));
113 if (!android_fdsan_close_with_tag_ptr) {
114 ALOGW("Unable to resolve android_fdsan_close_with_tag");
115 }
116
117 } else {
118 ALOGW("Unable to load libc.so");
119 android_fdsan_close_with_tag_ptr = nullptr;
120 android_fdsan_get_owner_tag_ptr = nullptr;
121 }
122 });
123 }
124
125 /**
126 * Obtains a sync_file_info object from the provided file descriptor.
127 * Internally this attempts to dynamcially resolve the sync_file_info method from libsync.so
128 * @param fd File descriptor to resolve the corresponding sync_file_info from
129 * @return sync_file_info or nullptr if the method cannot be resolved or there is no sync_file_info
130 * associated with the provided file descriptor
131 */
get_sync_file_info(int fd)132 static struct sync_file_info* get_sync_file_info(int fd) {
133 load_libsync();
134 if (sync_file_info_ptr) {
135 return sync_file_info_ptr(fd);
136 } else {
137 return nullptr;
138 }
139 }
140
141 /**
142 * Helper method that resolves and invokes sync_file_info_free from libsync.so.
143 * This attempts to dynamically resolve the method once and caches the function pointer.
144 * If the release method cannot be invoked this method call is a no-op.
145 * @param info sync_file_info instance to release
146 */
release_sync_file_info(struct sync_file_info * info)147 static void release_sync_file_info(struct sync_file_info* info) {
148 load_libsync();
149 if (sync_file_info_free_ptr) {
150 sync_file_info_free_ptr(info);
151 }
152 }
153
SyncFenceBindings_nForceClose(JNIEnv * env,jclass,jint fd)154 void SyncFenceBindings_nForceClose(JNIEnv *env, jclass, jint fd) {
155 load_lib_fdsan();
156 if (android_fdsan_get_owner_tag_ptr && android_fdsan_close_with_tag_ptr) {
157 uint64_t tag = android_fdsan_get_owner_tag_ptr(static_cast<int>(fd));
158 uint64_t type = tag >> 56;
159 // From fdsan.h docs on android_fdsan_owner_type, native file descriptors have their upper
160 // most bits as all zeros. So limit the closure of file descriptors to only this type in
161 // order to avoid potential double closure instances.
162 if (type == android_fdsan_owner_type::ANDROID_FDSAN_OWNER_TYPE_GENERIC_00) {
163 android_fdsan_close_with_tag_ptr(fd, tag);
164 }
165 }
166 }
167
SyncFenceBindings_nGetSignalTime(JNIEnv * env,jclass,jint fd)168 jlong SyncFenceBindings_nGetSignalTime(JNIEnv *env, jclass, jint fd) {
169 // Implementation sampled from Fence::getSignalTime in the framework
170 if (fd == -1) {
171 return SIGNAL_TIME_INVALID;
172 }
173
174 struct sync_file_info* finfo = get_sync_file_info(fd);
175 if (finfo == nullptr) {
176 return SIGNAL_TIME_INVALID;
177 }
178
179 if (finfo->status != 1) {
180 const auto status = finfo->status;
181 if (status < 0) {
182 ALOGE("nGetSignalTime: sync_file_info contains an error: <%d> for fd: <%d>", status,
183 fd);
184 }
185 release_sync_file_info(finfo);
186 return status < 0 ? SIGNAL_TIME_INVALID : SIGNAL_TIME_PENDING;
187 }
188
189 uint64_t timestamp = 0;
190 struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
191 for (size_t i = 0; i < finfo->num_fences; i++) {
192 if (pinfo[i].timestamp_ns > timestamp) {
193 timestamp = pinfo[i].timestamp_ns;
194 }
195 }
196
197 release_sync_file_info(finfo);
198 return static_cast<int64_t>(timestamp);
199 }
200
201 // Implementation of sync_wait obtained from libsync/sync.c in the framework
sync_wait(int fd,int timeout)202 static int sync_wait(int fd, int timeout)
203 {
204 struct pollfd fds{};
205 int ret;
206
207 if (fd < 0) {
208 errno = EINVAL;
209 return -1;
210 }
211
212 fds.fd = fd;
213 fds.events = POLLIN;
214
215 do {
216 ret = poll(&fds, 1, timeout);
217 if (ret > 0) {
218 if (fds.revents & (POLLERR | POLLNVAL)) {
219 errno = EINVAL;
220 return -1;
221 }
222 return 0;
223 } else if (ret == 0) {
224 errno = ETIME;
225 return -1;
226 }
227 } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
228
229 return ret;
230 }
231
SyncFence_nWait(JNIEnv * env,jobject,jint fd,jint timeout_millis)232 jboolean SyncFence_nWait(JNIEnv *env, jobject, jint fd, jint timeout_millis) {
233 if (fd == -1) {
234 return static_cast<jboolean>(true);
235 }
236
237 // SyncFence#wait takes a timeout as a long in nanoseconds, however, the poll
238 // API appears to consume an int. Also the documentation in Fence.cpp seems to indicate
239 // that the timeout is consumed as an int in milliseconds
240 int err = sync_wait(fd, timeout_millis);
241 return static_cast<jboolean>(err == 0);
242 }
243
SyncFenceBindings_nResolveSyncFileInfo(JNIEnv * env,jclass)244 jboolean SyncFenceBindings_nResolveSyncFileInfo(JNIEnv *env, jclass) {
245 load_libsync();
246 return sync_file_info_ptr != nullptr;
247 }
248
SyncFenceBindings_nResolveSyncFileInfoFree(JNIEnv * env,jclass)249 jboolean SyncFenceBindings_nResolveSyncFileInfoFree(JNIEnv *env, jclass) {
250 load_libsync();
251 return sync_file_info_free_ptr != nullptr;
252 }
253
SyncFence_nDup(JNIEnv * env,jobject,jint fd)254 jint SyncFence_nDup(JNIEnv *env, jobject, jint fd) {
255 return static_cast<jint>(dup(static_cast<int>(fd)));
256 }
257
258 static const JNINativeMethod SYNC_FENCE_METHOD_TABLE[] = {
259 {
260 "nClose",
261 "(I)V",
262 (void*)SyncFence_nClose
263 },
264 {
265 "nWait",
266 "(II)Z",
267 (void*)SyncFence_nWait
268 },
269 {
270 "nDup",
271 "(I)I",
272 (void*)SyncFence_nDup
273 }
274 };
275
276 static const JNINativeMethod SYNC_FENCE_BINDINGS_METHOD_TABLE[] = {
277 {
278 "nResolveSyncFileInfo",
279 "()Z",
280 (void*)SyncFenceBindings_nResolveSyncFileInfo
281 },
282 {
283 "nResolveSyncFileInfoFree",
284 "()Z",
285 (void*)SyncFenceBindings_nResolveSyncFileInfoFree
286 },
287 {
288 "nGetSignalTime",
289 "(I)J",
290 (void*)SyncFenceBindings_nGetSignalTime
291 },
292 {
293 "nForceClose",
294 "(I)V",
295 (void *) SyncFenceBindings_nForceClose
296 }
297 };
298
loadSyncFenceMethods(JNIEnv * env)299 jint loadSyncFenceMethods(JNIEnv* env) {
300 jclass syncFenceClass = env->FindClass("androidx/hardware/SyncFenceV19");
301 if (syncFenceClass == nullptr) {
302 return JNI_ERR;
303 }
304
305 if (env->RegisterNatives(syncFenceClass, SYNC_FENCE_METHOD_TABLE,
306 sizeof(SYNC_FENCE_METHOD_TABLE) / sizeof(JNINativeMethod)) != JNI_OK) {
307 return JNI_ERR;
308 }
309
310 jclass syncFenceTestBindings = env->FindClass("androidx/hardware/SyncFenceBindings");
311 if (syncFenceTestBindings == nullptr) {
312 return JNI_ERR;
313 }
314
315 if (env->RegisterNatives(syncFenceTestBindings, SYNC_FENCE_BINDINGS_METHOD_TABLE,
316 sizeof(SYNC_FENCE_BINDINGS_METHOD_TABLE) / sizeof(JNINativeMethod)) != JNI_OK) {
317 return JNI_ERR;
318 }
319
320 return JNI_OK;
321 }
322