1/*
2 * Copyright (c) 2021 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#include <chrono>
16#include <thread>
17#include <unistd.h>
18#include <gtest/gtest.h>
19#include <iservice_registry.h>
20#include <surface.h>
21#include "accesstoken_kit.h"
22#include "iconsumer_surface.h"
23#include "nativetoken_kit.h"
24#include "token_setproc.h"
25
26using namespace testing;
27using namespace testing::ext;
28
29namespace OHOS::Rosen {
30class SurfaceIPCTest : public testing::Test, public IBufferConsumerListenerClazz {
31public:
32    static void SetUpTestCase();
33    void OnBufferAvailable() override;
34    OHOS::GSError SetData(sptr<SurfaceBuffer> &buffer, sptr<Surface> &pSurface);
35    bool GetData(sptr<SurfaceBuffer> &buffer);
36    pid_t ChildProcessMain();
37    sptr<OHOS::Surface> CreateSurface();
38
39    static inline sptr<IConsumerSurface> cSurface = nullptr;
40    static inline int32_t pipeFd[2] = {};
41    static inline int32_t ipcSystemAbilityID = 34156;
42    static inline BufferRequestConfig requestConfig = {};
43    static inline BufferFlushConfig flushConfig = {};
44};
45
46void SurfaceIPCTest::SetUpTestCase()
47{
48    GTEST_LOG_(INFO) << getpid();
49    requestConfig = {
50        .width = 0x100,  // small
51        .height = 0x100, // small
52        .strideAlignment = 0x8,
53        .format = GRAPHIC_PIXEL_FMT_RGBA_8888,
54        .usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA,
55        .timeout = 0,
56    };
57    flushConfig = { .damage = {
58        .w = 0x100,
59        .h = 0x100,
60    } };
61}
62
63void SurfaceIPCTest::OnBufferAvailable()
64{
65}
66
67static inline GSError OnBufferRelease(sptr<SurfaceBuffer> &buffer)
68{
69    return GSERROR_OK;
70}
71
72OHOS::GSError SurfaceIPCTest::SetData(sptr<SurfaceBuffer> &buffer, sptr<Surface> &pSurface)
73{
74    buffer->GetExtraData()->ExtraSet("123", 0x123);
75    buffer->GetExtraData()->ExtraSet("345", (int64_t)0x345);
76    buffer->GetExtraData()->ExtraSet("567", "567");
77
78    uint32_t reserveInts = 1;
79    GraphicExtDataHandle *handle = AllocExtDataHandle(reserveInts);
80    handle->reserve[0] = 1;
81    OHOS::GSError ret = pSurface->SetTunnelHandle(handle);
82    FreeExtDataHandle(handle);
83    handle = nullptr;
84    return ret;
85}
86
87bool SurfaceIPCTest::GetData(sptr<SurfaceBuffer> &buffer)
88{
89    int32_t int32;
90    int64_t int64;
91    std::string str;
92    buffer->GetExtraData()->ExtraGet("123", int32);
93    buffer->GetExtraData()->ExtraGet("345", int64);
94    buffer->GetExtraData()->ExtraGet("567", str);
95    if ((int32 != 0x123) || (int64 != 0x345) || (str != "567")) {
96        return false;
97    }
98
99    sptr<SurfaceTunnelHandle> handleGet = nullptr;
100    handleGet = cSurface->GetTunnelHandle();
101    if ((handleGet == nullptr) || (handleGet->GetHandle()->fd != -1) ||
102        (handleGet->GetHandle()->reserveInts != 1) || (handleGet->GetHandle()->reserve[0] != 1)) {
103            return false;
104    }
105
106    GraphicPresentTimestamp timestamp = {GRAPHIC_DISPLAY_PTS_DELAY, 1};  // mock data for test
107    auto sRet = cSurface->SetPresentTimestamp(buffer->GetSeqNum(), timestamp);
108    return (sRet == OHOS::GSERROR_OK);
109}
110
111sptr<OHOS::Surface> SurfaceIPCTest::CreateSurface()
112{
113    sptr<IRemoteObject> robj = nullptr;
114    while (true) {
115        auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
116        robj = sam->GetSystemAbility(ipcSystemAbilityID);
117        if (robj != nullptr) {
118            break;
119        }
120        sleep(0);
121    }
122
123    auto producer = iface_cast<IBufferProducer>(robj);
124    return Surface::CreateSurfaceAsProducer(producer);
125}
126
127pid_t SurfaceIPCTest::ChildProcessMain()
128{
129    pipe(pipeFd);
130    pid_t pid = fork();
131    if (pid != 0) {
132        return pid;
133    }
134
135    int64_t data;
136    read(pipeFd[0], &data, sizeof(data));
137
138    auto pSurface = CreateSurface();
139    pSurface->RegisterReleaseListener(OnBufferRelease);
140    sptr<SurfaceBuffer> buffer = nullptr;
141    int releaseFence = -1;
142    auto sRet = pSurface->RequestBuffer(buffer, releaseFence, requestConfig);
143    if (sRet != OHOS::GSERROR_OK) {
144        data = sRet;
145        write(pipeFd[1], &data, sizeof(data));
146        exit(0);
147    }
148    sRet = SetData(buffer, pSurface);
149    if (sRet != OHOS::GSERROR_OK) {
150        data = sRet;
151        write(pipeFd[1], &data, sizeof(data));
152        exit(0);
153    }
154
155    sRet = pSurface->FlushBuffer(buffer, -1, flushConfig);
156    data = sRet;
157    write(pipeFd[1], &data, sizeof(data));
158    usleep(1000); // sleep 1000 microseconds (equals 1 milliseconds)
159    read(pipeFd[0], &data, sizeof(data));
160    usleep(1000); // sleep 1000 microseconds (equals 1 milliseconds)
161    GraphicPresentTimestampType type = GraphicPresentTimestampType::GRAPHIC_DISPLAY_PTS_DELAY;
162    int64_t time = 0;
163    sRet = pSurface->GetPresentTimestamp(buffer->GetSeqNum(), type, time);
164    if (sRet != OHOS::GSERROR_OK || time != 1) {
165        data = sRet;
166        write(pipeFd[1], &data, sizeof(data));
167        exit(0);
168    }
169    pSurface->UnRegisterReleaseListener();
170    close(pipeFd[0]);
171    close(pipeFd[1]);
172    exit(0);
173    return 0;
174}
175
176/*
177* Function: produce and consumer surface by IPC
178* Type: Function
179* Rank: Important(2)
180* EnvConditions: N/A
181* CaseDescription: 1. produce surface, fill buffer
182*                  2. consume surface and check buffer
183*                  3. call RequestBuffer in this process, check sRet and buffer
184* @tc.require: issueI5I57K issueI5GMZN issueI5IWHW
185 */
186HWTEST_F(SurfaceIPCTest, BufferIPC001, Function | MediumTest | Level2)
187{
188    auto pid = ChildProcessMain();
189    ASSERT_GE(pid, 0);
190
191    uint64_t tokenId;
192    const char *perms[2];
193    perms[0] = "ohos.permission.DISTRIBUTED_DATASYNC";
194    perms[1] = "ohos.permission.CAMERA";
195    NativeTokenInfoParams infoInstance = {
196        .dcapsNum = 0,
197        .permsNum = 2,
198        .aclsNum = 0,
199        .dcaps = NULL,
200        .perms = perms,
201        .acls = NULL,
202        .processName = "dcamera_client_demo",
203        .aplStr = "system_basic",
204    };
205    tokenId = GetAccessTokenId(&infoInstance);
206    SetSelfTokenID(tokenId);
207    int32_t rett = Security::AccessToken::AccessTokenKit::ReloadNativeTokenInfo();
208    ASSERT_EQ(rett, Security::AccessToken::RET_SUCCESS);
209    cSurface = IConsumerSurface::Create("test");
210    cSurface->RegisterConsumerListener(this);
211    auto producer = cSurface->GetProducer();
212    auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
213    sam->AddSystemAbility(ipcSystemAbilityID, producer->AsObject());
214
215    int64_t data = 0;
216    write(pipeFd[1], &data, sizeof(data));
217    usleep(1000); // sleep 1000 microseconds (equals 1 milliseconds)
218    read(pipeFd[0], &data, sizeof(data));
219    EXPECT_EQ(data, OHOS::GSERROR_OK);
220
221    sptr<SurfaceBuffer> buffer = nullptr;
222    int32_t fence = -1;
223    int64_t timestamp;
224    Rect damage;
225    auto sRet = cSurface->AcquireBuffer(buffer, fence, timestamp, damage);
226    EXPECT_EQ(sRet, OHOS::GSERROR_OK);
227    EXPECT_NE(buffer, nullptr);
228    EXPECT_EQ(GetData(buffer), true);
229
230    sRet = cSurface->ReleaseBuffer(buffer, -1);
231    EXPECT_EQ(sRet, OHOS::GSERROR_OK);
232
233    // RequestBuffer cannot be called in two processes
234    auto pSurfaceSecond = Surface::CreateSurfaceAsProducer(producer);
235    sptr<SurfaceBuffer> bufferSecond = nullptr;
236    int releaseFence = -1;
237    sRet = pSurfaceSecond->RequestBuffer(bufferSecond, releaseFence, requestConfig);
238    ASSERT_EQ(sRet, GSERROR_CONSUMER_IS_CONNECTED);
239    ASSERT_EQ(bufferSecond, nullptr);
240
241    //close resource
242    write(pipeFd[1], &data, sizeof(data));
243    close(pipeFd[0]);
244    close(pipeFd[1]);
245    sam->RemoveSystemAbility(ipcSystemAbilityID);
246    int32_t ret = 0;
247    do {
248        waitpid(pid, nullptr, 0);
249    } while (ret == -1 && errno == EINTR);
250}
251
252/*
253* Function: disconnect
254* Type: Function
255* Rank: Important(1)
256* EnvConditions: N/A
257* CaseDescription: 1. call Disconnect in other process, check sRet
258 */
259HWTEST_F(SurfaceIPCTest, Disconnect001, Function | MediumTest | Level1)
260{
261    cSurface->RegisterConsumerListener(this);
262    auto producer = cSurface->GetProducer();
263    auto pSurface = Surface::CreateSurfaceAsProducer(producer);
264    auto sRet = pSurface->Disconnect();
265    ASSERT_EQ(sRet, GSERROR_CONSUMER_DISCONNECTED);  // Disconnect cannot be called in two processes
266}
267}
268