• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "js_inputmethod_extension.h"
17 
18 #include "ability_handler.h"
19 #include "ability_info.h"
20 #include "configuration_utils.h"
21 #include "global.h"
22 #include "input_method_ability.h"
23 #include "inputmethod_extension_ability_service_impl.h"
24 #include "inputmethod_trace.h"
25 #include "js_extension_context.h"
26 #include "js_inputmethod_extension_context.h"
27 #include "js_runtime.h"
28 #include "js_runtime_utils.h"
29 #include "napi/native_api.h"
30 #include "napi/native_node_api.h"
31 #include "napi_common_util.h"
32 #include "napi_common_want.h"
33 #include "napi_remote_object.h"
34 #include "tasks/task_ams.h"
35 #include "tasks/task_imsa.h"
36 #include "task_manager.h"
37 
38 namespace OHOS {
39 namespace AbilityRuntime {
40 namespace {
41 constexpr size_t ARGC_ONE = 1;
42 constexpr size_t ARGC_TWO = 2;
43 } // namespace
44 JsInputMethodExtension *JsInputMethodExtension::jsInputMethodExtension = nullptr;
45 using namespace OHOS::AppExecFwk;
46 using namespace OHOS::MiscServices;
47 
AttachInputMethodExtensionContext(napi_env env,void * value,void *)48 napi_value AttachInputMethodExtensionContext(napi_env env, void *value, void *)
49 {
50     IMSA_HILOGI("AttachInputMethodExtensionContext start.");
51     if (value == nullptr) {
52         IMSA_HILOGW("parameter is invalid.");
53         return nullptr;
54     }
55     auto ptr = reinterpret_cast<std::weak_ptr<InputMethodExtensionContext> *>(value)->lock();
56     if (ptr == nullptr) {
57         IMSA_HILOGW("context is invalid.");
58         return nullptr;
59     }
60     napi_value object = CreateJsInputMethodExtensionContext(env, ptr);
61     auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "InputMethodExtensionContext", &object, 1);
62     if (systemModule == nullptr) {
63         IMSA_HILOGE("failed to load system module by engine!");
64         return nullptr;
65     }
66     auto contextObj = systemModule ->GetNapiValue();
67     napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc, AttachInputMethodExtensionContext, value,
68         nullptr);
69     auto workContext = new (std::nothrow) std::weak_ptr<InputMethodExtensionContext>(ptr);
70     if (workContext == nullptr) {
71         IMSA_HILOGE("workContext is nullptr!");
72         return nullptr;
73     }
74     napi_status status = napi_wrap(
75         env, contextObj, workContext,
76         [](napi_env, void *data, void *) {
77             IMSA_HILOGI("finalizer for weak_ptr input method extension context is called.");
78             delete static_cast<std::weak_ptr<InputMethodExtensionContext> *>(data);
79         },
80         nullptr, nullptr);
81     if (status != napi_ok) {
82         IMSA_HILOGE("InputMethodExtensionContext wrap failed: %{public}d!", status);
83         delete workContext;
84         return nullptr;
85     }
86     return object;
87 }
88 
Create(const std::unique_ptr<Runtime> & runtime)89 JsInputMethodExtension *JsInputMethodExtension::Create(const std::unique_ptr<Runtime> &runtime)
90 {
91     IMSA_HILOGI("JsInputMethodExtension Create.");
92     if (runtime == nullptr) {
93         IMSA_HILOGE("runtime is nullptr.");
94         return nullptr;
95     }
96     jsInputMethodExtension = new JsInputMethodExtension(static_cast<JsRuntime &>(*runtime));
97     return jsInputMethodExtension;
98 }
99 
JsInputMethodExtension(JsRuntime & jsRuntime)100 JsInputMethodExtension::JsInputMethodExtension(JsRuntime &jsRuntime) : jsRuntime_(jsRuntime)
101 {
102 }
103 
~JsInputMethodExtension()104 JsInputMethodExtension::~JsInputMethodExtension()
105 {
106     jsRuntime_.FreeNativeReference(std::move(jsObj_));
107 }
108 
Init(const std::shared_ptr<AbilityLocalRecord> & record,const std::shared_ptr<OHOSApplication> & application,std::shared_ptr<AbilityHandler> & handler,const sptr<IRemoteObject> & token)109 void JsInputMethodExtension::Init(const std::shared_ptr<AbilityLocalRecord> &record,
110     const std::shared_ptr<OHOSApplication> &application, std::shared_ptr<AbilityHandler> &handler,
111     const sptr<IRemoteObject> &token)
112 {
113     IMSA_HILOGI("JsInputMethodExtension Init.");
114     InputMethodExtension::Init(record, application, handler, token);
115     std::string srcPath;
116     GetSrcPath(srcPath);
117     if (srcPath.empty()) {
118         IMSA_HILOGE("failed to get srcPath!");
119         return;
120     }
121 
122     if (abilityInfo_ == nullptr) {
123         IMSA_HILOGE("abilityInfo_ is nullptr!");
124         return;
125     }
126     std::string moduleName(Extension::abilityInfo_->moduleName);
127     moduleName.append("::").append(abilityInfo_->name);
128     IMSA_HILOGI("JsInputMethodExtension, module: %{public}s, srcPath:%{public}s.", moduleName.c_str(), srcPath.c_str());
129     HandleScope handleScope(jsRuntime_);
130     napi_env env = jsRuntime_.GetNapiEnv();
131     jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath,
132         abilityInfo_->compileMode == CompileMode::ES_MODULE);
133     if (jsObj_ == nullptr) {
134         IMSA_HILOGE("failed to get jsObj_!");
135         return;
136     }
137     IMSA_HILOGI("JsInputMethodExtension::Init GetNapiValue.");
138     napi_value obj = jsObj_->GetNapiValue();
139     if (obj == nullptr) {
140         IMSA_HILOGE("failed to get JsInputMethodExtension object!");
141         return;
142     }
143     BindContext(env, obj);
144     handler_ = handler;
145     InitDisplayCache();
146     ListenWindowManager();
147     IMSA_HILOGI("JsInputMethodExtension end.");
148 }
149 
ListenWindowManager()150 void JsInputMethodExtension::ListenWindowManager()
151 {
152     IMSA_HILOGD("register window manager service listener.");
153     auto jsInputMethodExtension = std::static_pointer_cast<JsInputMethodExtension>(shared_from_this());
154     displayListener_ = sptr<JsInputMethodExtensionDisplayListener>::MakeSptr(jsInputMethodExtension);
155     if (displayListener_ == nullptr) {
156         IMSA_HILOGE("failed to create display listener!");
157         return;
158     }
159     Rosen::DisplayManager::GetInstance().RegisterDisplayListener(displayListener_);
160 }
161 
OnConfigurationUpdated(const AppExecFwk::Configuration & config)162 void JsInputMethodExtension::OnConfigurationUpdated(const AppExecFwk::Configuration &config)
163 {
164     InputMethodExtension::OnConfigurationUpdated(config);
165     IMSA_HILOGD("called.");
166     auto context = GetContext();
167     if (context == nullptr) {
168         IMSA_HILOGE("context is invalid!");
169         return;
170     }
171 
172     auto contextConfig = context->GetConfiguration();
173     if (contextConfig != nullptr) {
174         std::vector<std::string> changeKeyValue;
175         contextConfig->CompareDifferent(changeKeyValue, config);
176         if (!changeKeyValue.empty()) {
177             contextConfig->Merge(changeKeyValue, config);
178         }
179         IMSA_HILOGD("config dump merge: %{public}s.", contextConfig->GetName().c_str());
180     }
181     ConfigurationUpdated();
182 }
183 
ConfigurationUpdated()184 void JsInputMethodExtension::ConfigurationUpdated()
185 {
186     IMSA_HILOGD("called.");
187     HandleScope handleScope(jsRuntime_);
188     napi_env env = jsRuntime_.GetNapiEnv();
189 
190     // Notify extension context
191     auto context = GetContext();
192     if (context == nullptr) {
193         IMSA_HILOGE("context is nullptr!");
194         return;
195     }
196     auto fullConfig = context->GetConfiguration();
197     if (fullConfig == nullptr) {
198         IMSA_HILOGE("configuration is nullptr!");
199         return;
200     }
201 
202     JsExtensionContext::ConfigurationUpdated(env, shellContextRef_, fullConfig);
203 }
204 
BindContext(napi_env env,napi_value obj)205 void JsInputMethodExtension::BindContext(napi_env env, napi_value obj)
206 {
207     IMSA_HILOGI("JsInputMethodExtension::BindContext");
208     auto context = GetContext();
209     if (context == nullptr) {
210         IMSA_HILOGE("failed to get context!");
211         return;
212     }
213     IMSA_HILOGD("JsInputMethodExtension::Init CreateJsInputMethodExtensionContext.");
214     napi_value contextObj = CreateJsInputMethodExtensionContext(env, context);
215     auto shellContextRef = jsRuntime_.LoadSystemModule("InputMethodExtensionContext", &contextObj, ARGC_ONE);
216     if (shellContextRef == nullptr) {
217         IMSA_HILOGE("shellContextRef is nullptr!");
218         return;
219     }
220     contextObj = shellContextRef->GetNapiValue();
221     if (contextObj == nullptr) {
222         IMSA_HILOGE("failed to get input method extension native object!");
223         return;
224     }
225     auto workContext = new (std::nothrow) std::weak_ptr<InputMethodExtensionContext>(context);
226     if (workContext == nullptr) {
227         IMSA_HILOGE("workContext is nullptr!");
228         return;
229     }
230     napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc, AttachInputMethodExtensionContext,
231         workContext, nullptr);
232     IMSA_HILOGD("JsInputMethodExtension::Init Bind.");
233     context->Bind(jsRuntime_, shellContextRef.release());
234     IMSA_HILOGD("JsInputMethodExtension::SetProperty.");
235     napi_set_named_property(env, obj, "context", contextObj);
236     napi_status status = napi_wrap(
237         env, contextObj, workContext,
238         [](napi_env, void *data, void *) {
239             IMSA_HILOGI("Finalizer for weak_ptr input method extension context is called.");
240             delete static_cast<std::weak_ptr<InputMethodExtensionContext> *>(data);
241         },
242         nullptr, nullptr);
243     if (status != napi_ok) {
244         IMSA_HILOGE("InputMethodExtensionContext wrap failed: %{public}d", status);
245         delete workContext;
246     }
247 }
248 
OnStart(const AAFwk::Want & want)249 void JsInputMethodExtension::OnStart(const AAFwk::Want &want)
250 {
251     auto task = std::make_shared<TaskAmsInit>();
252     TaskManager::GetInstance().PostTask(task);
253 
254     InputMethodAbility::GetInstance().InitConnect();
255     StartAsync("OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_EXTENSION));
256     StartAsync("Extension::OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_MIDDLE_EXTENSION));
257     Extension::OnStart(want);
258     FinishAsync("Extension::OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_MIDDLE_EXTENSION));
259     IMSA_HILOGI("JsInputMethodExtension OnStart begin.");
260     HandleScope handleScope(jsRuntime_);
261     napi_env env = jsRuntime_.GetNapiEnv();
262     napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
263     napi_value argv[] = { napiWant };
264     StartAsync("onCreate", static_cast<int32_t>(TraceTaskId::ONCREATE_EXTENSION));
265     CallObjectMethod("onCreate", argv, ARGC_ONE);
266     FinishAsync("onCreate", static_cast<int32_t>(TraceTaskId::ONCREATE_EXTENSION));
267     TaskManager::GetInstance().PostTask(std::make_shared<TaskImsaSetCoreAndAgent>());
268     IMSA_HILOGI("ime bind imf");
269     FinishAsync("OnStart", static_cast<int32_t>(TraceTaskId::ONSTART_EXTENSION));
270     TaskManager::GetInstance().Complete(task->GetSeqId());
271 }
272 
InitDisplayCache()273 void JsInputMethodExtension::InitDisplayCache()
274 {
275     auto foldStatus = Rosen::DisplayManager::GetInstance().GetFoldStatus();
276     auto displayPtr = Rosen::DisplayManager::GetInstance().GetDefaultDisplaySync();
277     if (displayPtr == nullptr) {
278         IMSA_HILOGE("displayPtr is null");
279         return;
280     }
281     cacheDisplay_.SetCacheDisplay(
282         displayPtr->GetWidth(), displayPtr->GetHeight(), displayPtr->GetRotation(), foldStatus);
283 }
284 
OnStop()285 void JsInputMethodExtension::OnStop()
286 {
287     InputMethodExtension::OnStop();
288     IMSA_HILOGI("JsInputMethodExtension OnStop start.");
289     CallObjectMethod("onDestroy");
290     auto context = GetContext();
291     if (context == nullptr) {
292         IMSA_HILOGE("context is nullptr.");
293         return;
294     }
295     bool ret = ConnectionManager::GetInstance().DisconnectCaller(context->GetToken());
296     if (ret) {
297         IMSA_HILOGI("the input method extension connection is not disconnected.");
298     }
299     IMSA_HILOGI("JsInputMethodExtension %{public}s end.", __func__);
300 }
301 
OnConnect(const AAFwk::Want & want)302 sptr<IRemoteObject> JsInputMethodExtension::OnConnect(const AAFwk::Want &want)
303 {
304     IMSA_HILOGI("JsInputMethodExtension OnConnect start.");
305     Extension::OnConnect(want);
306     auto remoteObj = new (std::nothrow) InputMethodExtensionAbilityServiceImpl();
307     if (remoteObj == nullptr) {
308         IMSA_HILOGE("failed to create InputMethodExtensionAbilityServiceImpl!");
309         return nullptr;
310     }
311     return remoteObj;
312 }
313 
OnDisconnect(const AAFwk::Want & want)314 void JsInputMethodExtension::OnDisconnect(const AAFwk::Want &want)
315 {
316     IMSA_HILOGI("JsInputMethodExtension OnDisconnect start.");
317     Extension::OnDisconnect(want);
318     IMSA_HILOGI("%{public}s start.", __func__);
319     HandleScope handleScope(jsRuntime_);
320     napi_env env = jsRuntime_.GetNapiEnv();
321     napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
322     napi_value argv[] = { napiWant };
323     if (jsObj_ == nullptr) {
324         IMSA_HILOGE("not found InputMethodExtension.js!");
325         return;
326     }
327 
328     napi_value obj = jsObj_->GetNapiValue();
329     if (obj == nullptr) {
330         IMSA_HILOGE("failed to get InputMethodExtension object!");
331         return;
332     }
333 
334     napi_value method = nullptr;
335     napi_get_named_property(env, obj, "onDisconnect", &method);
336     if (method == nullptr) {
337         IMSA_HILOGE("failed to get onDisconnect from InputMethodExtension object!");
338         return;
339     }
340     napi_value remoteNapi = nullptr;
341     napi_call_function(env, obj, method, ARGC_ONE, argv, &remoteNapi);
342     IMSA_HILOGI("%{public}s end.", __func__);
343 }
344 
OnCommand(const AAFwk::Want & want,bool restart,int startId)345 void JsInputMethodExtension::OnCommand(const AAFwk::Want &want, bool restart, int startId)
346 {
347     IMSA_HILOGI("JsInputMethodExtension OnCommand start.");
348     Extension::OnCommand(want, restart, startId);
349     IMSA_HILOGI("%{public}s start restart=%{public}s,startId=%{public}d.", __func__, restart ? "true" : "false",
350         startId);
351     HandleScope handleScope(jsRuntime_);
352     napi_env env = jsRuntime_.GetNapiEnv();
353     napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
354     napi_value napiStartId = nullptr;
355     napi_create_int32(env, startId, &napiStartId);
356     napi_value argv[] = { napiWant, napiStartId };
357     CallObjectMethod("onRequest", argv, ARGC_TWO);
358     IMSA_HILOGI("%{public}s end.", __func__);
359 }
360 
CallObjectMethod(const char * name,const napi_value * argv,size_t argc)361 napi_value JsInputMethodExtension::CallObjectMethod(const char *name, const napi_value *argv, size_t argc)
362 {
363     IMSA_HILOGI("JsInputMethodExtension::CallObjectMethod(%{public}s), start.", name);
364 
365     if (jsObj_ == nullptr) {
366         IMSA_HILOGW("not found InputMethodExtension.js.");
367         return nullptr;
368     }
369 
370     HandleScope handleScope(jsRuntime_);
371     napi_env env = jsRuntime_.GetNapiEnv();
372     napi_value obj = jsObj_->GetNapiValue();
373     if (obj == nullptr) {
374         IMSA_HILOGE("failed to get InputMethodExtension object!");
375         return nullptr;
376     }
377 
378     napi_value method = nullptr;
379     napi_get_named_property(env, obj, name, &method);
380     if (method == nullptr) {
381         IMSA_HILOGE("failed to get '%{public}s' from InputMethodExtension object!", name);
382         return nullptr;
383     }
384     IMSA_HILOGI("JsInputMethodExtension::CallFunction(%{public}s), success.", name);
385     napi_value remoteNapi = nullptr;
386     napi_status status = napi_call_function(env, obj, method, argc, argv, &remoteNapi);
387     if (status != napi_ok) {
388         return nullptr;
389     }
390     return remoteNapi;
391 }
392 
GetSrcPath(std::string & srcPath)393 void JsInputMethodExtension::GetSrcPath(std::string &srcPath)
394 {
395     IMSA_HILOGD("JsInputMethodExtension GetSrcPath start.");
396     if (abilityInfo_ == nullptr) {
397         IMSA_HILOGE("abilityInfo_ is nullptr!");
398         return;
399     }
400     if (!Extension::abilityInfo_->isModuleJson) {
401         /* temporary compatibility api8 + config.json */
402         srcPath.append(Extension::abilityInfo_->package);
403         srcPath.append("/assets/js/");
404         if (!Extension::abilityInfo_->srcPath.empty()) {
405             srcPath.append(Extension::abilityInfo_->srcPath);
406         }
407         srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
408         return;
409     }
410 
411     if (!Extension::abilityInfo_->srcEntrance.empty()) {
412         srcPath.append(Extension::abilityInfo_->moduleName + "/");
413         srcPath.append(Extension::abilityInfo_->srcEntrance);
414         srcPath.erase(srcPath.rfind('.'));
415         srcPath.append(".abc");
416     }
417 }
418 
OnCreate(Rosen::DisplayId displayId)419 void JsInputMethodExtension::OnCreate(Rosen::DisplayId displayId)
420 {
421     IMSA_HILOGD("enter");
422 }
423 
OnDestroy(Rosen::DisplayId displayId)424 void JsInputMethodExtension::OnDestroy(Rosen::DisplayId displayId)
425 {
426     IMSA_HILOGD("exit");
427 }
428 
CheckNeedAdjustKeyboard(Rosen::DisplayId displayId)429 void JsInputMethodExtension::CheckNeedAdjustKeyboard(Rosen::DisplayId displayId)
430 {
431     auto displayPtr = Rosen::DisplayManager::GetInstance().GetDefaultDisplaySync();
432     if (displayPtr == nullptr) {
433         return;
434     }
435     auto defaultDisplayId = displayPtr->GetId();
436     if (displayId != defaultDisplayId) {
437         return;
438     }
439     auto foldStatus = Rosen::DisplayManager::GetInstance().GetFoldStatus();
440     auto width = displayPtr->GetWidth();
441     auto height = displayPtr->GetHeight();
442     auto rotation = displayPtr->GetRotation();
443     IMSA_HILOGD("display width: %{public}d, height: %{public}d, rotation: %{public}d, foldStatus: %{public}d",
444         width, height, rotation, foldStatus);
445     if (!cacheDisplay_.IsEmpty()) {
446         if ((cacheDisplay_.displayWidth != width ||
447             cacheDisplay_.displayHeight != height) &&
448             cacheDisplay_.displayFoldStatus == foldStatus &&
449             cacheDisplay_.displayRotation == rotation) {
450             InputMethodAbility::GetInstance().AdjustKeyboard();
451         }
452     }
453     cacheDisplay_.SetCacheDisplay(width, height, rotation, foldStatus);
454 }
455 
OnChange(Rosen::DisplayId displayId)456 void JsInputMethodExtension::OnChange(Rosen::DisplayId displayId)
457 {
458     IMSA_HILOGD("displayId: %{public}" PRIu64 "", displayId);
459     auto context = GetContext();
460     if (context == nullptr) {
461         IMSA_HILOGE("context is invalid!");
462         return;
463     }
464 
465     auto contextConfig = context->GetConfiguration();
466     if (contextConfig == nullptr) {
467         IMSA_HILOGE("configuration is invalid!");
468         return;
469     }
470 
471     bool isConfigChanged = false;
472     auto configUtils = std::make_shared<ConfigurationUtils>();
473     configUtils->UpdateDisplayConfig(displayId, contextConfig, context->GetResourceManager(), isConfigChanged);
474     IMSA_HILOGD("OnChange, isConfigChanged: %{public}d, Config after update: %{public}s.", isConfigChanged,
475         contextConfig->GetName().c_str());
476 
477     if (isConfigChanged) {
478         auto inputMethodExtension = std::static_pointer_cast<JsInputMethodExtension>(shared_from_this());
479         auto task = [inputMethodExtension]() {
480             if (inputMethodExtension) {
481                 inputMethodExtension->ConfigurationUpdated();
482             }
483         };
484         if (handler_ != nullptr) {
485             handler_->PostTask(task, "JsInputMethodExtension:OnChange", 0, AppExecFwk::EventQueue::Priority::VIP);
486         }
487     }
488 }
489 } // namespace AbilityRuntime
490 } // namespace OHOS
491