update_engine-整體結構(三)


在update_engine-整體結構(二)中分析到了Action,那么我們接着繼續分析.

 首先來看一下BuildUpdateActons(...)這個方法。

src/system/update_engine/update_attempter_android.cc

 1 void UpdateAttempterAndroid::BuildUpdateActions(const string& url) { 2   CHECK(!processor_->IsRunning()); 3   processor_->set_delegate(this); 4 
 5   // Actions:
 6   shared_ptr<InstallPlanAction> install_plan_action( 7       new InstallPlanAction(install_plan_)); 8 
 9   HttpFetcher* download_fetcher = nullptr; 10   if (FileFetcher::SupportedUrl(url)) { 11     DLOG(INFO) << "Using FileFetcher for file URL."; 12     download_fetcher = new FileFetcher(); 13   } else { 14 #ifdef _UE_SIDELOAD 15     LOG(FATAL) << "Unsupported sideload URI: " << url; 16 #else
17     LibcurlHttpFetcher* libcurl_fetcher =
18         new LibcurlHttpFetcher(&proxy_resolver_, hardware_); 19     libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload); 20     download_fetcher = libcurl_fetcher; 21 #endif  // _UE_SIDELOAD
22 } 23   shared_ptr<DownloadAction> download_action( 24       new DownloadAction(prefs_, 25 boot_control_, 26 hardware_, 27                          nullptr,             // system_state, not used.
28                          download_fetcher));  // passes ownership
29   shared_ptr<FilesystemVerifierAction> filesystem_verifier_action( 30       new FilesystemVerifierAction()); 31 
32   shared_ptr<PostinstallRunnerAction> postinstall_runner_action( 33       new PostinstallRunnerAction(boot_control_, hardware_)); 34 
35   download_action->set_delegate(this); 36   download_action->set_base_offset(base_offset_); 37   download_action_ = download_action; 38   postinstall_runner_action->set_delegate(this); 39 
40   actions_.push_back(shared_ptr<AbstractAction>(install_plan_action)); 41   actions_.push_back(shared_ptr<AbstractAction>(download_action)); 42   actions_.push_back(shared_ptr<AbstractAction>(filesystem_verifier_action)); 43   actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action)); 44 
45   // Bond them together. We have to use the leaf-types when calling
46   // BondActions().
47   BondActions(install_plan_action.get(), download_action.get()); 48   BondActions(download_action.get(), filesystem_verifier_action.get()); 49   BondActions(filesystem_verifier_action.get(), 50               postinstall_runner_action.get()); 51 
52   // Enqueue the actions.
53   for (const shared_ptr<AbstractAction>& action : actions_) 54     processor_->EnqueueAction(action.get()); 55 }

 我們會發現processor_,InstallPlanAction,DownloadAction,FilesystemVerifierAction,PostinstallRunnerAction。首先分析processor_,它是在UpdateAttempterAndroid的構造方法中被賦值

 1 UpdateAttempterAndroid::UpdateAttempterAndroid( 2     DaemonStateInterface* daemon_state, 3     PrefsInterface* prefs, 4     BootControlInterface* boot_control, 5     HardwareInterface* hardware) 6 : daemon_state_(daemon_state), 7 prefs_(prefs), 8 boot_control_(boot_control), 9 hardware_(hardware), 10       processor_(new ActionProcessor()) { 11   network_selector_ = network::CreateNetworkSelector(); 12 }

 ActionProcessor的數據結構為:

  1 // An ActionProcessor keeps a queue of Actions and processes them in order.
  2 
  3 namespace chromeos_update_engine { 4 
  5 class AbstractAction; 6 class ActionProcessorDelegate; 7 
  8 class ActionProcessor { 9  public: 10   ActionProcessor() = default; 11 
 12   virtual ~ActionProcessor(); 13 
 14   // Starts processing the first Action in the queue. If there's a delegate,
 15   // when all processing is complete, ProcessingDone() will be called on the
 16   // delegate.
 17   virtual void StartProcessing(); 18 
 19   // Aborts processing. If an Action is running, it will have
 20   // TerminateProcessing() called on it. The Action that was running and all the
 21   // remaining actions will be lost and must be re-enqueued if this Processor is
 22   // to use it.
 23   void StopProcessing(); 24 
 25   // Suspend the processing. If an Action is running, it will have the
 26   // SuspendProcessing() called on it, and it should suspend operations until
 27   // ResumeProcessing() is called on this class to continue. While suspended,
 28   // no new actions will be started. Calling SuspendProcessing while the
 29   // processing is suspended or not running this method performs no action.
 30   void SuspendProcessing(); 31 
 32   // Resume the suspended processing. If the ActionProcessor is not suspended
 33   // or not running in the first place this method performs no action.
 34   void ResumeProcessing(); 35 
 36   // Returns true iff the processing was started but not yet completed nor
 37   // stopped.
 38   bool IsRunning() const { return current_action_ != nullptr || suspended_; } 39 
 40   // Adds another Action to the end of the queue.
 41   virtual void EnqueueAction(AbstractAction* action); 42 
 43   // Sets/gets the current delegate. Set to null to remove a delegate.
 44   ActionProcessorDelegate* delegate() const { return delegate_; } 45   void set_delegate(ActionProcessorDelegate *delegate) { 46     delegate_ = delegate; 47 } 48 
 49   // Returns a pointer to the current Action that's processing.
 50   AbstractAction* current_action() const { 51     return current_action_; 52 } 53 
 54   // Called by an action to notify processor that it's done. Caller passes self.
 55   void ActionComplete(AbstractAction* actionptr, ErrorCode code); 56 
 57  private: 58   // Continue processing actions (if any) after the last action terminated with
 59   // the passed error code. If there are no more actions to process, the
 60   // processing will terminate.
 61   void StartNextActionOrFinish(ErrorCode code); 62 
 63   // Actions that have not yet begun processing, in the order in which
 64   // they'll be processed.
 65   std::deque<AbstractAction*> actions_; 66 
 67   // A pointer to the currently processing Action, if any.
 68   AbstractAction* current_action_{nullptr}; 69 
 70   // The ErrorCode reported by an action that was suspended but finished while
 71   // being suspended. This error code is stored here to be reported back to the
 72   // delegate once the processor is resumed.
 73 ErrorCode suspended_error_code_{ErrorCode::kSuccess}; 74 
 75   // Whether the action processor is or should be suspended.
 76   bool suspended_{false}; 77 
 78   // A pointer to the delegate, or null if none.
 79   ActionProcessorDelegate* delegate_{nullptr}; 80 
 81 DISALLOW_COPY_AND_ASSIGN(ActionProcessor); 82 }; 83 
 84 // A delegate object can be used to be notified of events that happen
 85 // in an ActionProcessor. An instance of this class can be passed to an
 86 // ActionProcessor to register itself.
 87 class ActionProcessorDelegate { 88  public: 89   virtual ~ActionProcessorDelegate() = default; 90 
 91   // Called when all processing in an ActionProcessor has completed. A pointer
 92   // to the ActionProcessor is passed. |code| is set to the exit code of the
 93   // last completed action.
 94   virtual void ProcessingDone(const ActionProcessor* processor, 95 ErrorCode code) {} 96 
 97   // Called when processing has stopped. Does not mean that all Actions have
 98   // completed. If/when all Actions complete, ProcessingDone() will be called.
 99   virtual void ProcessingStopped(const ActionProcessor* processor) {} 100 
101   // Called whenever an action has finished processing, either successfully
102   // or otherwise.
103   virtual void ActionCompleted(ActionProcessor* processor, 104                                AbstractAction* action, 105 ErrorCode code) {} 106 }; 107 
108 }  

 從中可以看到ActionProcessor其實就是用來管理Action的,它的方法都比較簡單,根據注釋我們大體就能夠明白每個方法的意思,在遇到的時候某一個方法再具體分析。接下來再看Action它所存在的繼承關系如下

Aciton繼承關系

 

 FilesystemVerifierAction,PostinstallRunnerAction,DownloadAction都繼承了InstallPlanAction,根據繼承關系可以看出他們都會有PerformAction,ActionCompleted等方法。PerformAction()是在Action開始執行前進行調用,而ActionCompleted是在執行完成后進行調用。先來看看InstallPlanAction中的內容

 src/system/update_engine/payload_consumer/install_plan.h

 1 class InstallPlanAction : public Action<InstallPlanAction> { 2  public: 3 InstallPlanAction() {} 4   explicit InstallPlanAction(const InstallPlan& install_plan): 5 install_plan_(install_plan) {} 6 
 7   void PerformAction() override { 8     if (HasOutputPipe()) { 9 SetOutputObject(install_plan_); 10 } 11     processor_->ActionComplete(this, ErrorCode::kSuccess); 12 } 13 
14   InstallPlan* install_plan() { return &install_plan_; } 15 
16   static std::string StaticType() { return "InstallPlanAction"; } 17   std::string Type() const override { return StaticType(); } 18 
19   typedef ActionTraits<InstallPlanAction>::InputObjectType InputObjectType; 20   typedef ActionTraits<InstallPlanAction>::OutputObjectType OutputObjectType; 21 
22  private: 23 InstallPlan install_plan_; 24 
25 DISALLOW_COPY_AND_ASSIGN(InstallPlanAction); 26 };

 可以看到InstallAction比較簡單,僅僅是將install_plan_設置為了輸出對象,傳遞給了下一個Action,這是Action之間的一個通信方式,這個方式可以稱之為pipe方式,下面來分析一下這種通信方式。先來看在Action這個類里面提到的ActionPipe

 src/system/update_engine/common/action_pipe.h

 1 namespace chromeos_update_engine { 2 
 3 // Used by Actions an InputObjectType or OutputObjectType to specify that
 4 // for that type, no object is taken/given.
 5 class NoneType {}; 6 
 7 template<typename T>
 8 class Action; 9 
10 template<typename ObjectType>
11 class ActionPipe { 12  public: 13   virtual ~ActionPipe() {} 14 
15   // This should be called by an Action on its input pipe.
16   // Returns a reference to the stored object.
17   const ObjectType& contents() const { return contents_; }                //獲取管道中的內容
18  
19   // This should be called by an Action on its output pipe.
20   // Stores a copy of the passed object in this pipe.
21   void set_contents(const ObjectType& contents) { contents_ = contents; }  //設置管道中的內容
22 
23   // Bonds two Actions together with a new ActionPipe. The ActionPipe is
24   // jointly owned by the two Actions and will be automatically destroyed
25   // when the last Action is destroyed.
26   template<typename FromAction, typename ToAction>
27   static void Bond(FromAction* from, ToAction* to) {                               //將兩個Action連接通過pipe連接在一起
28     std::shared_ptr<ActionPipe<ObjectType>> pipe(new ActionPipe<ObjectType>); 29     from->set_out_pipe(pipe); 30 
31     to->set_in_pipe(pipe);  // If you get an error on this line, then
32     // it most likely means that the From object's OutputObjectType is
33     // different from the To object's InputObjectType.
34 } 35 
36  private: 37 ObjectType contents_; 38 
39   // The ctor is private. This is because this class should construct itself
40   // via the static Bond() method.
41 ActionPipe() {} 42 DISALLOW_COPY_AND_ASSIGN(ActionPipe); 43 }; 44 
45 // Utility function
46 template<typename FromAction, typename ToAction>
47 void BondActions(FromAction* from, ToAction* to) { 48 static_assert( 49       std::is_same<typename FromAction::OutputObjectType, 50                    typename ToAction::InputObjectType>::value, 51       "FromAction::OutputObjectType doesn't match ToAction::InputObjectType"); 52   ActionPipe<typename FromAction::OutputObjectType>::Bond(from, to); 53 } 54 
55 } 

  可以看到ActionPipe主要就是將兩個Action連接在一起。為什么說就會連接在一起呢?再來看Action中相關的方法

src/system/update_engine/common/action.h 

 1 template<typename SubClass>
 2 class Action : public AbstractAction { 3  public: 4   ~Action() override {} 5 
 6   // Attaches an input pipe to this Action. This is optional; an Action
 7   // doesn't need to have an input pipe. The input pipe must be of the type
 8   // of object that this class expects.
 9   // This is generally called by ActionPipe::Bond()
10   void set_in_pipe(                                                  //設置輸入管道
11       // this type is a fancy way of saying: a shared_ptr to an
12       // ActionPipe<InputObjectType>.
13       const std::shared_ptr<ActionPipe<
14           typename ActionTraits<SubClass>::InputObjectType>>& in_pipe) { 15     in_pipe_ = in_pipe; 16 } 17 
18   // Attaches an output pipe to this Action. This is optional; an Action
19   // doesn't need to have an output pipe. The output pipe must be of the type
20   // of object that this class expects.
21   // This is generally called by ActionPipe::Bond()
22   void set_out_pipe(                                                 //設置輸出管道
23       // this type is a fancy way of saying: a shared_ptr to an
24       // ActionPipe<OutputObjectType>.
25       const std::shared_ptr<ActionPipe<
26           typename ActionTraits<SubClass>::OutputObjectType>>& out_pipe) { 27     out_pipe_ = out_pipe; 28 } 29 
30   // Returns true iff there is an associated input pipe. If there's an input
31   // pipe, there's an input object, but it may have been constructed with the
32   // default ctor if the previous action didn't call SetOutputObject().
33   bool HasInputObject() const { return in_pipe_.get(); }                    //是否有輸入管道
34 
35   // returns a const reference to the object in the input pipe.
36   const typename ActionTraits<SubClass>::InputObjectType& GetInputObject()         //獲取輸入的內容
37       const { 38 CHECK(HasInputObject()); 39     return in_pipe_->contents(); 40 } 41 
42   // Returns true iff there's an output pipe.
43   bool HasOutputPipe() const {                                                  //是否有輸出管道
44     return out_pipe_.get(); 45 } 46 
47   // Copies the object passed into the output pipe. It will be accessible to
48   // the next Action via that action's input pipe (which is the same as this
49   // Action's output pipe).
50   void SetOutputObject(                                                          //設置輸出的內容
51       const typename ActionTraits<SubClass>::OutputObjectType& out_obj) { 52 CHECK(HasOutputPipe()); 53     out_pipe_->set_contents(out_obj); 54 } 55 
56   // Returns a reference to the object sitting in the output pipe.
57   const typename ActionTraits<SubClass>::OutputObjectType& GetOutputObject() {          //獲取輸出的內容
58 CHECK(HasOutputPipe()); 59     return out_pipe_->contents(); 60 } 61 
62  protected: 63   // We use a shared_ptr to the pipe. shared_ptr objects destroy what they
64   // point to when the last such shared_ptr object dies. We consider the
65   // Actions on either end of a pipe to "own" the pipe. When the last Action
66   // of the two dies, the ActionPipe will die, too.
67   std::shared_ptr<ActionPipe<typename ActionTraits<SubClass>::InputObjectType>>
68 in_pipe_; 69   std::shared_ptr<ActionPipe<typename ActionTraits<SubClass>::OutputObjectType>>
70 out_pipe_; 71 };

從這里我們就能夠看出每個Action其實有兩個ActionPipe,一個是輸入ActionPipe,一個是輸出ActionPipe,輸入ActionPipe和前一個Action的輸出ActionPipe其實是一個ActionPipe,輸出Actionpipe和下一個Action的輸出ActionPipe是一個ActionPipe.

ActionTraits在這個類里僅僅是為InstallPlan這個類型定義了一個新的類型

src/system/update_engine/payload_consumer/install_plan.h

1 template<>
2 class ActionTraits<InstallPlanAction> { 3  public: 4   // Takes the install plan as input
5  typedef InstallPlan InputObjectType; 6   // Passes the install plan as output
7  typedef InstallPlan OutputObjectType; 8 };

 到這里Action機制也分析的差不多了,我們可以回到BuildUpdateActions中繼續進行分析了。

 1 void UpdateAttempterAndroid::BuildUpdateActions(const string& url) {
 2   CHECK(!processor_->IsRunning());
 3   processor_->set_delegate(this);
 4 
 5   // Actions:
 6   shared_ptr<InstallPlanAction> install_plan_action(
 7       new InstallPlanAction(install_plan_));
 8 
 9   HttpFetcher* download_fetcher = nullptr;
10   if (FileFetcher::SupportedUrl(url)) {
11     DLOG(INFO) << "Using FileFetcher for file URL.";
12     download_fetcher = new FileFetcher();
13   } else {
14 #ifdef _UE_SIDELOAD
15     LOG(FATAL) << "Unsupported sideload URI: " << url;
16 #else
17     LibcurlHttpFetcher* libcurl_fetcher =
18         new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
19     libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
20     download_fetcher = libcurl_fetcher;
21 #endif  // _UE_SIDELOAD
22   }
23   shared_ptr<DownloadAction> download_action(
24       new DownloadAction(prefs_,
25                          boot_control_,
26                          hardware_,
27                          nullptr,             // system_state, not used.
28                          download_fetcher));  // passes ownership
29   shared_ptr<FilesystemVerifierAction> filesystem_verifier_action(
30       new FilesystemVerifierAction());
31 
32   shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
33       new PostinstallRunnerAction(boot_control_, hardware_));
34 
35   download_action->set_delegate(this);
36   download_action->set_base_offset(base_offset_);
37   download_action_ = download_action;
38   postinstall_runner_action->set_delegate(this);
39 
40   actions_.push_back(shared_ptr<AbstractAction>(install_plan_action));
41   actions_.push_back(shared_ptr<AbstractAction>(download_action));
42   actions_.push_back(shared_ptr<AbstractAction>(filesystem_verifier_action));
43   actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
44 
45   // Bond them together. We have to use the leaf-types when calling
46   // BondActions().
47   BondActions(install_plan_action.get(), download_action.get());
48   BondActions(download_action.get(), filesystem_verifier_action.get());
49   BondActions(filesystem_verifier_action.get(),
50               postinstall_runner_action.get());
51 
52   // Enqueue the actions.
53   for (const shared_ptr<AbstractAction>& action : actions_)
54     processor_->EnqueueAction(action.get());
55 }
View Code

在這個方法里主要做了:

1.為processor_設置delegate,其實也就是注冊了回調方法,UpdateAttempterAndroid實現了ActionProcessDelegate中的方法.

2. 創建了InstallPlanAction

3.創建了download_fetcher,我們這里假定用的是本地的文件既使用file:///協議,所以download_fetcher即為FileFetcher,從這一部分的代碼可以看HtppFetcher,FileFetcher,LibcurlHttpFetcher之間具有繼承或實現的關系。

4.創建DownloadAction,注意在創建的時候傳入了download_fetcher為FileFetcher類型

5.創建FilesystemVerifierAction,PostinstallRunnerAction.從這里可以看出升級流程的精華應該就是這三個Action了

6.為download_action設置delegate,設置開始下載的offfset等,因為代碼中設置delegate的操作比較多,如果不注意很有可能記混亂了。

7.為postinstall_runner_action設置delegate

8.將Action加入到Action的集合中

9.使用BondActions方法為Action之間建立管道。

10.將action遍歷放入到processor_的隊列中,並且設置action的管理者為processor_。

在分析完這個方法所干的事情之后,再分析一下HtppFetcher,FileFetcher,LibcurlHttpFetcher這三者之間的關系

 HtppFetcher,FileFetcher,LibcurlHttpFetcher這三者之間的關系

 

現在繼續分析ApplyPayload中的最后一個方法UpdateBootFlags()

 1 void UpdateAttempterAndroid::UpdateBootFlags() { 2   if (updated_boot_flags_) { 3     LOG(INFO) << "Already updated boot flags. Skipping."; 4     CompleteUpdateBootFlags(true); 5     return; 6 } 7   // This is purely best effort.
 8   LOG(INFO) << "Marking booted slot as good."; 9   if (!boot_control_->MarkBootSuccessfulAsync( 10           Bind(&UpdateAttempterAndroid::CompleteUpdateBootFlags, 11                base::Unretained(this)))) { 12     LOG(ERROR) << "Failed to mark current boot as successful."; 13     CompleteUpdateBootFlags(false); 14 } 15 }

首先檢查當前運行的slot是否已經被標記為successful狀態,如果是則調用CompleteUpdateBootFlags方法,否則的就調用MarkBootSuccessfulAsync將當前的slot標記為successful。標記完成后調用CompleteUpdateBootFlags方法

1 void UpdateAttempterAndroid::CompleteUpdateBootFlags(bool successful) { 2   updated_boot_flags_ = true; 3 ScheduleProcessingStart(); 4 }

 從這里看出即使標記失敗了仍然調用 ScheduleProcessingStart(),這個方法主要就是開始執行Action

1 void UpdateAttempterAndroid::ScheduleProcessingStart() { 2   LOG(INFO) << "Scheduling an action processor start."; 3   brillo::MessageLoop::current()->PostTask( 4 FROM_HERE, 5       Bind([](ActionProcessor* processor) { processor->StartProcessing(); }, 6            base::Unretained(processor_.get()))); 7 }

 在來看看StartProcessing()方法的實現,首先是獲取對列中的第一個action,打印action的類型,之后將action移出隊列,並且調用PerformAction。

src/system/update_engine/common/action_processor.cc

1 void ActionProcessor::StartProcessing() { 2   CHECK(!IsRunning()); 3   if (!actions_.empty()) { 4     current_action_ = actions_.front(); 5     LOG(INFO) << "ActionProcessor: starting " << current_action_->Type(); 6 actions_.pop_front(); 7     current_action_->PerformAction(); 8 } 9 }

分析到了這里就對整體的update_engine有了一定的了解,接下來只需要對各個Action逐個擊破就好了。在之前已經看過了InstallPlanAction,它的內容很簡單,僅僅是在輸出管道中設置了install_plan_,接下來就調用了processor_->ActionComplete(this, ErrorCode::kSuccess),看一下ActionComplete的內容,它是如何讓下一個action開始執行的。

 1 void ActionProcessor::ActionComplete(AbstractAction* actionptr, 2 ErrorCode code) { 3 CHECK_EQ(actionptr, current_action_); 4   if (delegate_) 5     delegate_->ActionCompleted(this, actionptr, code); 6   string old_type = current_action_->Type(); 7   current_action_->ActionCompleted(code); 8   current_action_->SetProcessor(nullptr); 9   current_action_ = nullptr; 10   LOG(INFO) << "ActionProcessor: finished "
11             << (actions_.empty() ? "last action " : "") << old_type 12             << (suspended_ ? " while suspended" : "") 13             << " with code " << utils::ErrorCodeToString(code); 14   if (!actions_.empty() && code != ErrorCode::kSuccess) { 15     LOG(INFO) << "ActionProcessor: Aborting processing due to failure."; 16 actions_.clear(); 17 } 18   if (suspended_) { 19     // If an action finished while suspended we don't start the next action (or
20     // terminate the processing) until the processor is resumed. This condition
21     // will be flagged by a nullptr current_action_ while suspended_ is true.
22     suspended_error_code_ = code; 23     return; 24 } 25 StartNextActionOrFinish(code); 26 }

 其實這個方法中也就進行了善后和開始下一個Action的工作。包括:

1.判斷是否注冊了回調方法。這里的delegate_的類型為UpdateAttempterAndroid。如果注冊了就回調ActionCompleted方法,在UpdateAttempterAndroid中它的內容為

 1 void UpdateAttempterAndroid::ActionCompleted(ActionProcessor* processor, 2                                              AbstractAction* action, 3 ErrorCode code) { 4   // Reset download progress regardless of whether or not the download
 5   // action succeeded.
 6   const string type = action->Type(); 7   if (type == DownloadAction::StaticType()) { 8     download_progress_ = 0; 9 } 10   if (code != ErrorCode::kSuccess) { 11     // If an action failed, the ActionProcessor will cancel the whole thing.
12     return; 13 } 14   if (type == DownloadAction::StaticType()) { 15 SetStatusAndNotify(UpdateStatus::FINALIZING); 16 } 17 }

可以看到這個方法主要就是為重置下載的進度。

2.調用當前的Action的ActionCompleted,將Processor和當前Action置空等。

3.如果執行到某人Action的時候出了錯,則停止執行其他的Action

4. processor如果被掛起,則暫停執行下一個Action

5.執行下一個Action或者是否完成了所有的Action,StartNextActionOrFinish(code),該方法比較簡單,就不進行分析了。

到這里整體的Action的執行流程也就通了,下一篇開始會分析其他三個Action

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM