• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <ani.h>
17 #include <array>
18 #include <iostream>
19 #include <algorithm>
20 #include <chrono>
21 #include <future>
22 #include <thread>
23 #include <map>
24 #include <string>
25 
26 #include "frameworks/bridge/declarative_frontend/engine/jsi/jsi_declarative_engine.h"
27 #include "frameworks/base/utils/utils.h"
28 #include "frameworks/bridge/common/utils/engine_helper.h"
29 #include "frameworks/bridge/common/media_query/media_queryer.h"
30 #include "frameworks/core/common/container.h"
31 #include "frameworks/core/common/container_scope.cpp"
32 #include "frameworks/core/common/container_scope.h"
33 #include "frameworks/core/animation/cubic_curve.h"
34 #include "frameworks/core/animation/curve.h"
35 
36 namespace {
37 constexpr int32_t TWO_ARGS = 2;
38 }
39 
40 struct MediaQueryResult {
41     bool matches_ = false;
42     std::string media_;
43 
MediaQueryResultMediaQueryResult44     MediaQueryResult(bool match, const std::string& media) : matches_(match), media_(media) {}
45     virtual ~MediaQueryResult() = default;
AniSerializerMediaQueryResult46     virtual void AniSerializer([[maybe_unused]] ani_env *env, ani_object& result)
47     {
48         ani_boolean match = false;
49         ani_string media = nullptr;
50         static const char *className = "L@ohos/mediaquery/mediaquery/Mediaquery;";
51         ani_class cls;
52         if (ANI_OK != env->FindClass(className, &cls)) {
53             return;
54         }
55         ani_method method;
56         if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &method)) {
57             return;
58         }
59         env->Object_New(cls, method, &result, match, media);
60         ani_size nr_refs = 16;
61         if (ANI_OK != env->CreateLocalScope(nr_refs)) {
62             return;
63         }
64         const char* mediac = media_.c_str();
65         env->String_NewUTF8(mediac, strlen(mediac), &media);
66         match = static_cast<ani_boolean>(matches_);
67         env->Object_SetFieldByName_Boolean(result, "matches", match);
68         env->Object_SetFieldByName_Ref(result, "matches", static_cast<ani_ref>(media));
69         env->DestroyLocalScope();
70     }
71 };
72 
73 class MediaQueryListener : public MediaQueryResult {
74 public:
MediaQueryListener(bool match,const std::string & media)75     MediaQueryListener(bool match, const std::string& media) : MediaQueryResult(match, media) {}
~MediaQueryListener()76     ~MediaQueryListener() override
77     {
78         {
79             std::lock_guard<std::mutex> lock(mutex_);
80             TAG_LOGI(OHOS::Ace::AceLogTag::ACE_MEDIA_QUERY, "clean:%{public}s", media_.c_str());
81             CleanListenerSet();
82         }
83         if (env_ == nullptr) {
84             return;
85         }
86         for (auto& item : cbList_) {
87             env_->GlobalReference_Delete(item);
88         }
89     }
90 
IdlCallback(OHOS::Ace::Framework::JsEngine * jsEngine)91     static void IdlCallback(OHOS::Ace::Framework::JsEngine* jsEngine)
92     {
93         OnIdlCallback(jsEngine);
94     }
95 
OnIdlCallback(OHOS::Ace::Framework::JsEngine * jsEngine)96     static void OnIdlCallback(OHOS::Ace::Framework::JsEngine* jsEngine)
97     {
98         std::set<std::unique_ptr<MediaQueryListener>> delayDeleteListenerSets;
99         std::set<ani_ref> delayDeleteCallbacks;
100         std::vector<MediaQueryListener*> copyListeners;
101         {
102             std::lock_guard<std::mutex> lock(mutex_);
103             auto& currentListeners = listenerSets_[OHOS::Ace::AceType::WeakClaim(jsEngine)];
104             copyListeners.insert(copyListeners.end(), currentListeners.begin(), currentListeners.end());
105         }
106         struct Leave {
107             ~Leave()
108             {
109                 for (auto& cbRef : *delayDeleteCallbacks_) {
110                     delayDeleteEnv_->GlobalReference_Delete(cbRef);
111                 }
112                 delayDeleteCallbacks_ = nullptr;
113                 delayDeleteListenerSets_ = nullptr;
114             }
115         } leave;
116 
117         delayDeleteCallbacks_ = &delayDeleteCallbacks;
118         delayDeleteListenerSets_ = &delayDeleteListenerSets;
119 
120         TriggerAllCallbacks(copyListeners);
121     }
122 
TriggerAllCallbacks(std::vector<MediaQueryListener * > & copyListeners)123     static void TriggerAllCallbacks(std::vector<MediaQueryListener*>& copyListeners)
124     {
125         OHOS::Ace::Framework::MediaQueryer queryer;
126         for (auto& listener : copyListeners) {
127             auto json = OHOS::Ace::Framework::MediaQueryInfo::GetMediaQueryJsonInfo();
128             listener->matches_ = queryer.MatchCondition(listener->media_, json);
129             std::set<ani_ref> delayDeleteCallbacks;
130             std::vector<ani_ref> copyCallbacks;
131             {
132                 std::lock_guard<std::mutex> lock(mutex_);
133                 auto& currentCallbacks = listener->cbList_;
134                 copyCallbacks.insert(copyCallbacks.end(), currentCallbacks.begin(), currentCallbacks.end());
135             }
136 
137             for (const auto &cbRef : copyCallbacks) {
138                 if (delayDeleteCallbacks_->find(cbRef) != delayDeleteCallbacks_->end()) {
139                     continue;
140                 }
141                 TAG_LOGI(OHOS::Ace::AceLogTag::ACE_MEDIA_QUERY, "trigger:%{public}s matches:%{public}d",
142                     listener->media_.c_str(), listener->matches_);
143                 ani_size nr_refs = 16;
144                 if (ANI_OK != listener->env_->CreateLocalScope(nr_refs)) {
145                     return;
146                 }
147                 ani_wref cbWref;
148                 listener->env_->WeakReference_Create(cbRef, &cbWref);
149                 ani_ref ref;
150                 ani_boolean wasReleased;
151                 listener->env_->WeakReference_GetReference(cbWref, &wasReleased, &ref);
152                 ani_object result;
153                 listener->MediaQueryResult::AniSerializer(listener->env_, result);
154                 ani_ref resultRef = static_cast<ani_ref>(result);
155                 listener->env_->FunctionalObject_Call(static_cast<ani_fn_object>(ref), 1, &resultRef, nullptr);
156                 listener->env_->DestroyLocalScope();
157             }
158         }
159     }
160 
FindCbList(ani_object cb)161     std::list<ani_ref>::iterator FindCbList(ani_object cb)
162     {
163         return std::find_if(cbList_.begin(), cbList_.end(), [env = env_, cb](const ani_ref& item) -> bool {
164             ani_boolean result = false;
165             ani_wref cbWref;
166             env->WeakReference_Create(item, &cbWref);
167             ani_ref ref;
168             ani_boolean wasReleased;
169             env->WeakReference_GetReference(cbWref, &wasReleased, &ref);
170             env->Reference_StrictEquals(ref, cb, &result);
171             return static_cast<bool>(result);
172         });
173     }
174 
On(ani_env * env,ani_object object,ani_string type,ani_object callback)175     static void On([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object,
176         ani_string type, ani_object callback)
177     {
178         auto jsEngine = OHOS::Ace::EngineHelper::GetCurrentEngineSafely();
179         if (!jsEngine) {
180             return;
181         }
182         jsEngine->RegisterMediaUpdateCallback(MediaQueryListener::IdlCallback);
183         ani_size nr_refs = 16;
184         if (ANI_OK != env->CreateLocalScope(nr_refs)) {
185             return;
186         }
187         size_t argc = ParseArgs(env, object, type, callback);
188         if (argc == TWO_ARGS) {
189             env->DestroyLocalScope();
190             return;
191         }
192 
193         MediaQueryListener* listener = GetListener(env, object);
194         if (!listener) {
195             env->DestroyLocalScope();
196             return;
197         }
198         auto iter = listener->FindCbList(callback);
199         if (iter != listener->cbList_.end()) {
200             env->DestroyLocalScope();
201             return;
202         }
203         ani_ref ref;
204         env->GlobalReference_Create(static_cast<ani_ref>(callback), &ref);
205         listener->cbList_.emplace_back(ref);
206         TAG_LOGI(OHOS::Ace::AceLogTag::ACE_MEDIA_QUERY, "on:%{public}s num=%{public}d", listener->media_.c_str(),
207             static_cast<int>(listener->cbList_.size()));
208         env->DestroyLocalScope();
209     }
210 
Off(ani_env * env,ani_object object,ani_string type,ani_object callback)211     static void Off([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object,
212         ani_string type, ani_object callback)
213     {
214         size_t argc = ParseArgs(env, object, type, callback);
215         MediaQueryListener* listener = GetListener(env, object);
216         if (!listener || argc == 0) {
217             return;
218         }
219         if (argc == 1) {
220             if (delayDeleteCallbacks_) {
221                 delayDeleteEnv_ = env;
222                 for (auto& item : listener->cbList_) {
223                     (*delayDeleteCallbacks_).emplace(item);
224                 }
225             } else {
226                 for (auto& item : listener->cbList_) {
227                     listener->env_->GlobalReference_Delete(item);
228                 }
229             }
230             listener->cbList_.clear();
231         } else {
232             auto iter = listener->FindCbList(callback);
233             if (iter != listener->cbList_.end()) {
234                 if (delayDeleteCallbacks_) {
235                     (*delayDeleteCallbacks_).emplace(*iter);
236                 } else {
237                     listener->env_->GlobalReference_Delete(*iter);
238                 }
239                 listener->cbList_.erase(iter);
240             }
241         }
242         TAG_LOGI(OHOS::Ace::AceLogTag::ACE_MEDIA_QUERY, "off:%{public}s num=%{public}d", listener->media_.c_str(),
243             static_cast<int>(listener->cbList_.size()));
244         return;
245     }
246 
AniSerializer(ani_env * env,ani_object & result)247     void AniSerializer([[maybe_unused]] ani_env *env, ani_object& result) override
248     {
249         MediaQueryResult::AniSerializer(env, result);
250         static const char *mediaquery = "L@ohos/mediaquery/mediaquery/Mediaquery;";
251         ani_class cls2;
252         if (ANI_OK != env->FindClass(mediaquery, &cls2)) {
253             std::cerr << "Not found '" << mediaquery << "'" << std::endl;
254             return;
255         }
256         ani_field serializerField;
257         if (ANI_OK != env->Class_FindField(cls2, "nativeSerializerResult", &serializerField)) {
258             std::cerr << "animator create Get Field Fail" << "'" << std::endl;
259             return;
260         }
261         env->Object_SetField_Long(result, serializerField, reinterpret_cast<ani_long>(this));
262     }
263 
264 private:
CleanListenerSet()265     void CleanListenerSet()
266     {
267         auto iter = listenerSets_.begin();
268         while (iter != listenerSets_.end()) {
269             iter->second.erase(this);
270             if (iter->second.empty()) {
271                 auto jsEngineWeak = iter->first.Upgrade();
272                 if (jsEngineWeak) {
273                     jsEngineWeak->UnregisterMediaUpdateCallback();
274                 }
275                 iter = listenerSets_.erase(iter);
276             } else {
277                 iter++;
278             }
279         }
280     }
281 
Initialize(ani_env * env)282     void Initialize([[maybe_unused]] ani_env *env)
283     {
284         ani_size nr_refs = 16;
285         if (ANI_OK != env->CreateLocalScope(nr_refs)) {
286             return;
287         }
288         if (env_ == nullptr) {
289             env_ = env;
290         }
291         env->DestroyLocalScope();
292         auto jsEngine = OHOS::Ace::EngineHelper::GetCurrentEngineSafely();
293         if (!jsEngine) {
294             return;
295         }
296         {
297             std::lock_guard<std::mutex> lock(mutex_);
298             listenerSets_[jsEngine].emplace(this);
299         }
300     }
301 
GetListener(ani_env * env,ani_object object)302     static MediaQueryListener* GetListener([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object)
303     {
304         MediaQueryListener* listener = nullptr;
305         ani_long serializer;
306         env->Object_GetFieldByName_Long(object, "nativeSerializerResult", &serializer);
307         listener = reinterpret_cast<MediaQueryListener*>(serializer);
308         CHECK_NULL_RETURN(listener, nullptr);
309         listener->Initialize(env);
310         return listener;
311     }
312 
ANIUtils_ANIStringToStdString(ani_env * env,ani_string ani_str)313     static std::string ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str)
314     {
315         ani_size  strSize;
316         env->String_GetUTF8Size(ani_str, &strSize);
317 
318         std::vector<char> buffer(strSize + 1);
319         char* utf8Buffer = buffer.data();
320 
321         ani_size bytes_written = 0;
322         env->String_GetUTF8(ani_str, utf8Buffer, strSize + 1, &bytes_written);
323 
324         utf8Buffer[bytes_written] = '\0';
325         std::string content = std::string(utf8Buffer);
326         return content;
327     }
328 
ParseArgs(ani_env * env,ani_object object,ani_string type,ani_object callback)329     static size_t ParseArgs([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object,
330         ani_string type, ani_object callback)
331     {
332         auto typeStr = ANIUtils_ANIStringToStdString(env, type);
333         if (typeStr != "change") {
334             return 0;
335         }
336         ani_boolean isUndefinedResponse;
337         env->Reference_IsUndefined(callback, &isUndefinedResponse);
338         if (isUndefinedResponse) {
339             return 1;
340         }
341         return TWO_ARGS;
342     }
343 
344     ani_env *env_;
345     std::list<ani_ref> cbList_;
346     static std::set<std::unique_ptr<MediaQueryListener>>* delayDeleteListenerSets_;
347     static std::set<ani_ref>* delayDeleteCallbacks_;
348     static ani_env* delayDeleteEnv_;
349     static std::map<OHOS::Ace::WeakPtr<OHOS::Ace::Framework::JsEngine>, std::set<MediaQueryListener*>> listenerSets_;
350     static std::mutex mutex_;
351 };
352 std::set<std::unique_ptr<MediaQueryListener>>* MediaQueryListener::delayDeleteListenerSets_;
353 ani_env* MediaQueryListener::delayDeleteEnv_;
354 std::set<ani_ref>* MediaQueryListener::delayDeleteCallbacks_;
355 std::map<OHOS::Ace::WeakPtr<OHOS::Ace::Framework::JsEngine>, std::set<MediaQueryListener*>>
356     MediaQueryListener::listenerSets_;
357 std::mutex MediaQueryListener::mutex_;
358 
On(ani_env * env,ani_object object,ani_string type,ani_object callback)359 void On([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, ani_string type, ani_object callback)
360 {
361     MediaQueryListener::On(env, object, type, callback);
362 #if defined(PREVIEW)
363     MediaQueryListener::IdlCallback(AceType::RawPtr(jsEngine));
364 #endif
365 }
366 
Off(ani_env * env,ani_object object,ani_string type,ani_object callback)367 void Off([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, ani_string type, ani_object callback)
368 {
369     MediaQueryListener::Off(env, object, type, callback);
370 }
371 
JSMatchMediaSync(ani_env * env,ani_string condition)372 static ani_object JSMatchMediaSync([[maybe_unused]] ani_env *env, ani_string condition)
373 {
374     ani_size strSize = 0U;
375     env->String_GetUTF8Size(condition, &strSize);
376     std::vector<char> buffer(strSize + 1); // +1 for null terminator
377     char* utf8Buffer = buffer.data();
378     ani_size bytes_written = 0;
379     env->String_GetUTF8(condition, utf8Buffer, strSize + 1, &bytes_written);
380     utf8Buffer[bytes_written] = '\0';
381     std::string mediaCondition = std::string(utf8Buffer);
382 
383     static const char *className = "L@ohos/mediaquery/mediaquery;";
384     ani_object mediaquery_obj = {};
385     ani_class cls;
386     if (ANI_OK != env->FindClass(className, &cls)) {
387         return mediaquery_obj;
388     }
389 
390     ani_method ctor;
391     if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
392         return mediaquery_obj;
393     }
394 
395     OHOS::Ace::Framework::MediaQueryer queryer;
396     auto mediaFeature = OHOS::Ace::Framework::MediaQueryInfo::GetMediaQueryJsonInfo();
397     bool matchResult = queryer.MatchCondition(mediaCondition, mediaFeature);
398     MediaQueryListener* listener = new MediaQueryListener(matchResult, mediaCondition);
399 
400     listener->AniSerializer(env, mediaquery_obj);
401     return mediaquery_obj;
402 }
403 
ANI_Constructor(ani_vm * vm,uint32_t * result)404 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
405 {
406     ani_env *env;
407     if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
408         return ANI_ERROR;
409     }
410 
411     ani_namespace ns;
412     if (ANI_OK != env->FindNamespace("L@ohos/mediaquery/mediaquery;", &ns)) {
413         return ANI_ERROR;
414     }
415     std::array methods = {
416         ani_native_function {"matchMediaSync", nullptr, reinterpret_cast<void *>(JSMatchMediaSync)},
417     };
418 
419     if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) {
420         return ANI_ERROR;
421     }
422 
423     static const char *className = "L@ohos/mediaquery/mediaquery/Mediaquery;";
424     ani_class cls;
425     if (ANI_OK != env->FindClass(className, &cls)) {
426         return ANI_ERROR;
427     }
428 
429     std::array methodsListener = {
430         ani_native_function {"on", nullptr, reinterpret_cast<void *>(On)},
431         ani_native_function {"off", nullptr, reinterpret_cast<void *>(Off)},
432     };
433 
434     if (ANI_OK != env->Class_BindNativeMethods(cls, methodsListener.data(), methodsListener.size())) {
435         return ANI_ERROR;
436     };
437     *result = ANI_VERSION_1;
438     return ANI_OK;
439 }