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