1 /*
2  * Copyright (c) 2024 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 "ffi_remote_data.h"
17 
18 #include <cinttypes>
19 
20 #include "hilog/log_cpp.h"
21 
22 using namespace OHOS::FFI;
23 using namespace OHOS;
24 using namespace OHOS::HiviewDFX;
25 
26 namespace {
27 constexpr HiLogLabel LABEL = { LOG_CORE, 0xD003901, "CJ-FFIBindNative" };
28 } // namespace
29 
30 FFIDataManager* FFIDataManager::instance_ = nullptr;
31 
GetInstance()32 FFIDataManager* FFIDataManager::GetInstance()
33 {
34     if (instance_ == nullptr) {
35         instance_ = new FFIDataManager();
36     }
37     return instance_;
38 }
39 
StoreFFIData(const sptr<FFIData> & data)40 void FFIDataManager::StoreFFIData(const sptr<FFIData>& data)
41 {
42     std::lock_guard<std::mutex> lock(mtx);
43     int64_t id = data->GetID();
44     // 0 represents invalid status
45     if (id == 0) {
46         HiLog::Fatal(LABEL, "FFIData store invalid key");
47         return;
48     }
49     HiLog::Info(LABEL, "FFIData store_ key put in: %{public}" PRId64, id);
50     ffiDataStore_[id] = data;
51 }
52 
StoreRemoteData(const sptr<RemoteData> & data)53 void FFIDataManager::StoreRemoteData(const sptr<RemoteData>& data)
54 {
55     std::lock_guard<std::mutex> lock(mtx);
56     remoteDataStore_[data->GetID()] = data;
57 }
58 
NewFFIDataId()59 int64_t FFIDataManager::NewFFIDataId()
60 {
61     std::lock_guard<std::mutex> lock(mtx);
62     if (static_cast<int64_t>(ffiDataStore_.size()) >= maxCapacity) {
63         HiLog::Fatal(LABEL, "FFIData store_ over max capacity: %{public}" PRId64, maxCapacity);
64         // 0 represents invalid status in CJ RemoteData, will be handled by CJ Exception
65         return 0;
66     }
67     // When ffiDataId over uint64 max value, resetTime will be increased with 1.
68     auto resetTimes = FFIDataIdSafeIncrease();
69     auto resetTimeUpperBound = 2;
70     while ((ffiDataStore_.find(curFFIDataId_) != ffiDataStore_.end()) && resetTimes < resetTimeUpperBound) {
71         resetTimes += FFIDataIdSafeIncrease();
72     }
73     // It means there is no one available id, When resetTimes come to UpperBound 2.
74     if (resetTimes >= resetTimeUpperBound) {
75         HiLog::Fatal(LABEL, "FFIData id run out");
76         // 0 represents invalid status in CJ RemoteData, will be handled by CJ Exception
77         return 0;
78     }
79     HiLog::Info(LABEL, "FFIDataManager new ID : %{public}" PRId64 ", cache size: %{public}zu", curFFIDataId_,
80         ffiDataStore_.size());
81     return curFFIDataId_;
82 }
83 
FFIDataIdSafeIncrease()84 int FFIDataManager::FFIDataIdSafeIncrease()
85 {
86     curFFIDataId_++;
87     if (curFFIDataId_ >= static_cast<uint64_t>(maxId)) {
88         HiLog::Warn(LABEL, "FFIData id: %{public}" PRId64 " over max %{public}" PRId64 ", reset to 0", curFFIDataId_,
89             maxCapacity);
90         curFFIDataId_ = 1;
91         return 1;
92     }
93     return 0;
94 }
95 
RemoteData(int64_t id)96 RemoteData::RemoteData(int64_t id) : id_(id), isValid_(id != 0)
97 {
98     HiLog::Debug(LABEL, "RemoteData constructed: %{public}" PRId64 ".", id_);
99 }
100 
~RemoteData()101 RemoteData::~RemoteData()
102 {
103     HiLog::Debug(LABEL, "RemoteData destructed: %{public}" PRId64 ".", id_);
104     auto cjFunc = CJFFIFnInvoker::GetInstance()->GetCJFuncs().atCOHOSFFIReleaseFFIData;
105     if (!cjFunc) {
106         HiLog::Error(LABEL, "Failed to invoke CJ function: FFIReleaseFFIData!");
107         return;
108     }
109     cjFunc(GetID());
110     isValid_ = false;
111 }
112 
~CJLambdaRemoteData()113 CJLambdaRemoteData::~CJLambdaRemoteData()
114 {
115     auto manager = FFIDataManager::GetInstance();
116     manager->RemoveRemoteData(GetID());
117 }
118 
GetID() const119 int64_t RemoteData::GetID() const
120 {
121     if (!isValid_) {
122         HiLog::Error(LABEL, "RemoteData::GetID error, remote data invalid: %{public}" PRId64 ".", id_);
123     }
124     return id_;
125 }
126 
~FFIData()127 FFIData::~FFIData()
128 {
129     auto cjFunc = CJFFIFnInvoker::GetInstance()->GetCJFuncs().atCOHOSFFIReleaseRemoteData;
130     if (!cjFunc) {
131         HiLog::Error(LABEL, "Failed to invoke CJ function: FFIReleaseRemoteData!");
132         return;
133     }
134     cjFunc(GetID());
135 }
136