当前位置: 首页 > news >正文

chromium通信系统-ipcz系统(十一)-mojo binding

关于mojo binding的官方文档为mojo docs。 由于比较复杂,这里只做简单源码分析。

我们知道要实现rpc,必须实现客户端和服务端。 mojo 实现了一套领域语言,通过领域语言描述接口和数据, 再通过特有编译器编译成c++代码。 这个过程会生成Mojo对象, 我们以content/common/child_process.mojom 为例子来分析。


interface ChildProcess {......// Requests that the process bind a receiving pipe targeting the service// interface named by |receiver|.//// TODO(crbug.com/977637): Rename this to |RunService()| once the above method// is removed.BindServiceInterface(mojo_base.mojom.GenericPendingReceiver receiver);// Requests that the process bind a receiving pipe targeting the interface// named by |receiver|. Unlike |BindServiceInterface()| this may be used to// bind arbitrary interfaces on many different types of child processes.// Calls to this method generally end up in// |ChildThreadImpl::OnBindReceiver()|.//// Whether or not the interface type encapsulated by |receiver| is supported// depends on the process type and potentially on the Content embedder.BindReceiver(mojo_base.mojom.GenericPendingReceiver receiver);......
};

我们删除一些接口,总体定义如下。生成带c++代码如下。
out/Default/gen/content/common/child_process.mojom.h

class CONTENT_EXPORT ChildProcess: public ChildProcessInterfaceBase {public:......// 接口的名字static const char Name_[];// 方法表,用于将消息序号转化为处理函数static IPCStableHashFunction MessageToMethodInfo_(mojo::Message& message);// 方法名称表static const char* MessageToMethodName_(mojo::Message& message);// 协议版本static constexpr uint32_t Version_ = 0;static constexpr bool PassesAssociatedKinds_ = false;// 是否包含不可中断的方法static constexpr bool HasUninterruptableMethods_ = false;using Base_ = ChildProcessInterfaceBase;// 代理对象,用于客户端调用using Proxy_ = ChildProcessProxy;template <typename ImplRefTraits>// 用于指向服务端实现using Stub_ = ChildProcessStub<ImplRefTraits>;// 请求校验器using RequestValidator_ = ChildProcessRequestValidator;// 回复校验器using ResponseValidator_ = mojo::PassThroughFilter;// 方法版本列表enum MethodMinVersions : uint32_t {kProcessShutdownMinVersion = 0,kSetIPCLoggingEnabledMinVersion = 0,kGetBackgroundTracingAgentProviderMinVersion = 0,kEnableSystemTracingServiceMinVersion = 0,kCrashHungProcessMinVersion = 0,kRunServiceDeprecatedMinVersion = 0,kBindServiceInterfaceMinVersion = 0,kBindReceiverMinVersion = 0,kSetPseudonymizationSaltMinVersion = 0,};......// 下面是方法声明virtual ~ChildProcess() = default;......virtual void BindServiceInterface(::mojo::GenericPendingReceiver receiver) = 0;virtual void BindReceiver(::mojo::GenericPendingReceiver receiver) = 0;......
};

生成的c++ 类除了方法声明之外,还包括如下信息:

  • Name_接口名称
  • MessageToMethodInfo_: 方法表,通过收到的消息找到对应的rpc方法。
  • Proxy_ 代理对象,通过代理对象调用具体方法组装消息,发送给服务端。(包括方法名称,参数等,用于调用服务端对应方法)
  • Stub_ 用于描述服务端,指向服务端具体实现。
  • RequestValidator_ 请求校验。
  • ResponseValidator_ 响应校验。

通过官方文档我们知道通过Remote 调用服务端方法。 来看一下Remote 是如何实现的。
mojo/public/cpp/bindings/remote.h

 887 void ChildProcessProxy::BindServiceInterface(888     ::mojo::GenericPendingReceiver in_receiver) {889 #if BUILDFLAG(MOJO_TRACE_ENABLED)890   TRACE_EVENT1(891     "mojom", "Send content::mojom::ChildProcess::BindServiceInterface", "input_parameters",892     [&](perfetto::TracedValue context){893       auto dict = std::move(context).WriteDictionary();894       perfetto::WriteIntoTracedValueWithFallback(895            dict.AddItem("receiver"), in_receiver,896                         "<value of type ::mojo::GenericPendingReceiver>");897    });898 #endif899   const bool kExpectsResponse = false;900   const bool kIsSync = false;901   const bool kAllowInterrupt = true;902 903   const uint32_t kFlags =904       ((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |905       ((kIsSync) ? mojo::Message::kFlagIsSync : 0) |906       ((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt);907 908   mojo::Message message(909       internal::kChildProcess_BindServiceInterface_Name, kFlags, 0, 0, nullptr);910   mojo::internal::MessageFragment<911       ::content::mojom::internal::ChildProcess_BindServiceInterface_Params_Data> params(912           message);913   params.Allocate();914   mojo::internal::MessageFragment<915       typename decltype(params->receiver)::BaseType> receiver_fragment(916           params.message());917   mojo::internal::Serialize<::mojo_base::mojom::GenericPendingReceiverDataView>(918       in_receiver, receiver_fragment);919   params->receiver.Set(920       receiver_fragment.is_null() ? nullptr : receiver_fragment.data());921   MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(922       params->receiver.is_null(),923       mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,924       "null receiver in ChildProcess.BindServiceInterface request");925 926 #if defined(ENABLE_IPC_FUZZER)927   message.set_interface_name(ChildProcess::Name_);928   message.set_method_name("BindServiceInterface");929 #endif930   // This return value may be ignored as false implies the Connector has931   // encountered an error, which will be visible through other means.932   ::mojo::internal::SendMojoMessage(*receiver_, message);933 }

BindServiceInterface 的主要作用就是组装消息,然后发送。 这里908-909行创建Message的时候指定的消息名称为internal::kChildProcess_BindServiceInterface_Name, 用于指示服务端调用哪个方法。 这里932行receiver_ 持有一个portal,可以将数据写出去。

我们再看服务端怎么处理消息:

259 // Implements the mojom ChildProcess interface and lives on the IO thread.
260 class ChildThreadImpl::IOThreadState
261     : public base::RefCountedThreadSafe<IOThreadState>,
262       public mojom::ChildProcess {......
358 
359   void BindServiceInterface(mojo::GenericPendingReceiver receiver) override {
360     if (service_binder_)
361       service_binder_.Run(&receiver);
362 
363     if (receiver) {
364       main_thread_task_runner_->PostTask(
365           FROM_HERE, base::BindOnce(&ChildThreadImpl::BindServiceInterface,
366                                     weak_main_thread_, std::move(receiver)));
367     }
368   }
369 
370   void BindReceiver(mojo::GenericPendingReceiver receiver) override {
371     if (wait_for_interface_binders_) {
372       pending_binding_requests_.push_back(std::move(receiver));
373       return;
374     }
375 
376     if (interface_binders_.TryBind(&receiver))
377       return;
378 
379     main_thread_task_runner_->PostTask(
380         FROM_HERE, base::BindOnce(&ChildThreadImpl::OnBindReceiver,
381                                   weak_main_thread_, std::move(receiver)));
382   }
383 ......457   mojo::Receiver<mojom::ChildProcess> receiver_{this};
458 
459   // Binding requests which should be handled by |interface_binders|, but which
460   // have been queued because |allow_interface_binders_| is still |false|.
461   std::vector<mojo::GenericPendingReceiver> pending_binding_requests_;
462 };

ChildThreadImpl::IOThreadState 实现了mojom::ChildProcess接口。 它持有了一个receiver_对象, 该对象持有portal一端, 用于处理对端发送的消息。

我们具体来看 mojo::Receivermojom::ChildProcess 的实现。

template <typename Interface,typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
class Receiver {
......explicit Receiver(ImplPointerType impl) : internal_state_(std::move(impl)) {}
......private:internal::BindingState<Interface, ImplRefTraits> internal_state_;
}

Receiver 的参数为ChildThreadImpl::IOThreadState实例。 然后初始化internal_state_变量。internal_state_类型展开宏之后为nternal::BindingState<mojom::ChildProcess, RawPtrImplRefTraitsmojom::ChildProcess> internal_state_。

template <typename Interface, typename ImplRefTraits>
class BindingState : public BindingStateBase {public:using ImplPointerType = typename ImplRefTraits::PointerType;explicit BindingState(ImplPointerType impl) {stub_.set_sink(std::move(impl));}......
}

这里stub_成员变量为 typename Interface::template Stub_ stub_;展开宏为
mojom::ChildProcess::Stub_ stub_, 也就是ChildProcessStub。 我们看一下它的实现以及set_sink 方法。
out/Default/gen/content/common/child_process.mojom.h


template <typename ImplRefTraits =mojo::RawPtrImplRefTraits<ChildProcess>>
class ChildProcessStub: public mojo::MessageReceiverWithResponderStatus {public:
........void set_sink(ImplPointerType sink) { sink_ = std::move(sink); }ImplPointerType& sink() { return sink_; }bool Accept(mojo::Message* message) override {if (ImplRefTraits::IsNull(sink_))return false;return ChildProcessStubDispatch::Accept(ImplRefTraits::GetRawPointer(&sink_), message);}bool AcceptWithResponder(mojo::Message* message,std::unique_ptr<mojo::MessageReceiverWithStatus> responder) override {if (ImplRefTraits::IsNull(sink_))return false;return ChildProcessStubDispatch::AcceptWithResponder(ImplRefTraits::GetRawPointer(&sink_), message, std::move(responder));}private:ImplPointerType sink_;
};

set_sink方法实际设置成员变量sink_, 实际指向ChildThreadImpl::IOThreadState实例。 当receiver收到消息后会调用Accept(异步)方法或者AcceptWithResponder(同步返回结果)方法。

Accept方法调用ChildProcessStubDispatch::Accept方法,第一个参数为sink_, 第二个参数为消息体。我们来看ChildProcessStubDispatch::Accept 方法
out/Default/gen/content/common/child_process.mojom.cc

1021 // static
1022 bool ChildProcessStubDispatch::Accept(
1023     ChildProcess* impl,
1024     mojo::Message* message) {
1025   switch (message->header()->name) {.......
1182     case internal::kChildProcess_BindServiceInterface_Name: {
1183 
1184       DCHECK(message->is_serialized());
1185       internal::ChildProcess_BindServiceInterface_Params_Data* params =
1186           reinterpret_cast<internal::ChildProcess_BindServiceInterface_Params_Data*>(
1187               message->mutable_payload());
1188 
1189       bool success = true;
1190       ::mojo::GenericPendingReceiver p_receiver{};
1191       ChildProcess_BindServiceInterface_ParamsDataView input_data_view(params, message);
1192 
1193       if (success && !input_data_view.ReadReceiver(&p_receiver))
1194         success = false;
1195       if (!success) {
1196         ReportValidationErrorForMessage(
1197             message,
1198             mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
1199             ChildProcess::Name_, 6, false);
1200         return false;
1201       }
1202       // A null |impl| means no implementation was bound.
1203       DCHECK(impl);
1204       impl->BindServiceInterface(
1205 std::move(p_receiver));
1206       return true;
1207     }
1208     case internal::kChildProcess_BindReceiver_Name: {
1209 
1210       DCHECK(message->is_serialized());
1211       internal::ChildProcess_BindReceiver_Params_Data* params =
1212           reinterpret_cast<internal::ChildProcess_BindReceiver_Params_Data*>(
1213               message->mutable_payload());
1214 
1215       bool success = true;
1216       ::mojo::GenericPendingReceiver p_receiver{};
1217       ChildProcess_BindReceiver_ParamsDataView input_data_view(params, message);
1218 
1219       if (success && !input_data_view.ReadReceiver(&p_receiver))
1220         success = false;
1221       if (!success) {
1222         ReportValidationErrorForMessage(
1223             message,
1224             mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
1225             ChildProcess::Name_, 7, false);
1226         return false;
1227       }
1228       // A null |impl| means no implementation was bound.
1229       DCHECK(impl);
1230       impl->BindReceiver(
1231 std::move(p_receiver));
1232       return true;
1233     }.......
1259     }
1260   }
1261   return false;
1262 }

函数很简单,根据message的名称反序列化参数,然后调用ChildThreadImpl::IOThreadState的对应方法。

以上就是典型的ipcz binding 使用。 整体通信借助ipcz 通道。

相关文章:

chromium通信系统-ipcz系统(十一)-mojo binding

关于mojo binding的官方文档为mojo docs。 由于比较复杂&#xff0c;这里只做简单源码分析。 我们知道要实现rpc&#xff0c;必须实现客户端和服务端。 mojo 实现了一套领域语言&#xff0c;通过领域语言描述接口和数据&#xff0c; 再通过特有编译器编译成c代码。 这个过程会…...

鸿蒙开发基础-Web组件之cookie操作

使用ArkTS语言实现一个简单的免登录过程&#xff0c;向大家介绍基本的cookie管理操作。主要包含以下功能&#xff1a; 获取指定url对应的cookie的值。设置cookie。清除所有cookie。免登录访问账户中心。 cookie读写操作 首次打开应用时&#xff0c;应用首页的Web组件内呈现的…...

什么是k8s和声明式编程?

认识k8s之后&#xff0c;他的操作模式对我来说是一种很不错的体验。他提供了更接近现实世界的面向对象接口。 什么是k8s&#xff1f; Kubernetes&#xff08;K8s&#xff09;是一种开源容器编排平台&#xff0c;用于自动化部署、扩展和管理容器化应用程序。它简化了容器化应用…...

Fluids —— MicroSolvers DOP

目录 Gas SubStep —— 重复执行对应的子步 Switch Solver —— 切换解算器 Gas Attribute Swap —— 交换、复制或移动几何体属性 Gas Intermittent Solve —— 固定时间间隔计算子解算器 Gas External Forces —— 计算外部力并更新速度或速度场 Gas Particle Separate…...

工业智能网关:HiWoo Box远程采集设备数据

工业智能网关&#xff1a;HiWoo Box远程采集设备数据 在工业4.0和智能制造的浪潮下&#xff0c;工业互联网已成为推动产业升级、提升生产效率的关键。而在这其中&#xff0c;工业智能网关扮演着至关重要的角色。今天&#xff0c;我们就来深入探讨一下工业智能网关。 一、什么…...

Apollo之原理和使用讲解

文章目录 1 Apollo1.1 简介1.1.1 背景1.1.2 简介1.1.3 特点 1.2 基础模型1.3 Apollo 四个维度1.3.1 application1.3.2 environment1.3.3 cluster1.3.4 namespace 1.4 本地缓存1.5 客户端设计1.5.1 客服端拉取原理1.5.2 配置更新推送实现 1.6 总体设计1.7 可用性考虑 2 操作使用…...

魅族MX4pro系统升级、降级

网上的教程都是按住开机键音量上或者下键&#xff0c;但是我按了没用&#xff0c;还是直接点击压缩包管用。 下载系统 官网地址&#xff08;所有手机固件&#xff09;&#xff1a;https://flyme.cn/firmware.html 官方魅族mx4Pro系统&#xff1a;https://flyme.cn/firmwarelis…...

【Docker】快速入门之Docker的安装及使用

一、引言 1、什么是Docker Docker是一个开源的应用容器引擎&#xff0c;它让开发者可以将他们的应用及其依赖打包到一个可移植的镜像中&#xff0c;然后发布到任何流行的Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之…...

记录汇川:H5U于Factory IO测试13

主程序&#xff1a; 子程序&#xff1a; IO映射 子程序&#xff1a; 辅助出料 子程序&#xff1a; 模式选择 子程序&#xff1a; 示教程序 子程序&#xff1a; 手动程序 子程序&#xff1a; 统计程序 子程序&#xff1a; 异常报警 子程序&#xff1a; 自动程序&#xff1a; F…...

PYTHON通过跳板机巡检CENTOS的简单实现

实现的细节和引用的文件和以前博客记录的基本一致 https://shaka.blog.csdn.net/article/details/106927633 差别在于,这次是通过跳板机登陆获取的主机信息,只记录差异的部份 1.需要在跳板机相应的路径放置PYTHON的脚本resc.py resc.py这个脚本中有引用的文件(pm.sh,diskpn…...

网络配置以及命令详解

传统linux中,网络接口为eth0,eth1,eth2,..... RHEL 7以上版本默认命名是基于分配上的固定名称,ens33 接口类型: en:以太网有线接口 wl:无线局域网接口 ww:无线广域网 dmesg:显示开机信息 适配器类型: s:热插拔插槽 o:板载 p:pci类型 ifconfig ens160(命令行配置,临时生效):查…...

商务外语MR混合现实仿真情景实训教学

MR混合现实技术是一种将虚拟世界与真实世界相结合的技术。通过MR设备&#xff0c;我们可以将虚拟的场景、人物、物品等元素实时地呈现在真实的环境中&#xff0c;实现真实与虚拟的完美融合。在商务外语的实训教学中&#xff0c;MR技术可以为我们提供丰富的场景资源&#xff0c;…...

牛客周赛 Round 28 解题报告 | 珂学家 | 组合数学 + 离散化树状数组

前言 整体评价 还是E稍微有点意思&#xff0c;新周赛好像比预期要简单一些, _. 欢迎关注 珂朵莉 牛客周赛专栏 珂朵莉 牛客小白月赛专栏 A. 小红的新周赛 思路: 模拟 #include <bits/stdc.h>using namespace std;int main() {int res 0;for (int i 0; i < 6; i…...

Python系列(3)—— 变量

变量 一、变量命名规范二、变量赋值三、变量的数据类型四、变量的作用域五、变量类型转换 Python编程中&#xff0c;变量是存储数据的容器。它们用于存储各种数据类型&#xff0c;如整数、浮点数、字符串、列表、字典等。理解变量及其工作原理是Python编程的基础。 一、变量命…...

Java 并发性和多线程2

四、如何创建并运行 java 线程 Java 线程类也是一个 object 类&#xff0c;它的实例都继承自 java.lang.Thread 或其子类。 可以用如下方式用 java 中创建一个线程&#xff1a; Tread thread new Thread(); 执行该线程可以调用该线程的 start()方法: thread.start(); 在上…...

最新消息:OpenAI GPT Store 正式上线,GPTs 应用商店来了!

原文链接 https://openaigptguide.com/gpt-store-and-chatgpt-team/ OpenAI推出的两款新产品和服务&#xff1a;GPT Store和ChatGPT Team&#xff0c;提供了许多全新的解决方案和功能&#xff0c;旨在帮助用户更轻松地使用和构建GPT工具&#xff0c;同时也增加了公司的收入来源…...

memory泄露分析方法(java篇)

#memory泄露主要分为java和native 2种&#xff0c;本文主要介绍java# 测试每天从monkey中筛选出内存超标的app&#xff0c;提单流转到我 首先&#xff0c;辨别内存泄露类型&#xff08;java&#xff0c;还是native&#xff09; 从采到的dumpsys_meminfo_pid看java heap&…...

kubectlkubeletrancherhelmkubeadm这几个命令行工具是什么关系?

背景 在最近学习k8s的过程中&#xff0c;发现kubectl&kubelet&rancher&helm&kubeadm这几个命令怎么在交错使用&#xff0c;他们究竟是什么关系&#xff1f;他们分别应该在什么情况下使用呢&#xff1f;这里我进行了简单的总结&#xff0c;做个区分。 各工具说…...

Day26 669修剪二叉搜索树 108有序数组转为二叉搜索树 538二叉搜索树转换为累加树

669 修剪二叉搜索树 给定一个二叉搜索树&#xff0c;同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[L, R]中 (R>L) 。你可能需要改变树的根节点&#xff0c;所以结果应当返回修剪好的二叉搜索树的新的根节点。 class Solution { pub…...

优化CentOS 7.6的HTTP隧道代理网络性能

在CentOS 7.6上&#xff0c;通过HTTP隧道代理优化网络性能是一项复杂且细致的任务。首先&#xff0c;我们要了解HTTP隧道代理的工作原理&#xff1a;通过建立一个安全的隧道&#xff0c;HTTP隧道代理允许用户绕过某些网络限制&#xff0c;提高数据传输的速度和安全性。然而&…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...