1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chromeos/components/multidevice/logging/logging.h"
16 #include "content/public/browser/browser_context.h"
17 #include "content/public/browser/storage_partition.h"
18 #include "net/cookies/canonical_cookie.h"
19 #include "url/gurl.h"
20 
21 namespace {
22 const char kMessagesPairStateCookieName[] = "pair_state_cookie";
23 const char kPairedCookieValue[] = "true";
24 }  // namespace
25 
26 namespace chromeos {
27 
28 namespace android_sms {
29 
AndroidSmsPairingStateTrackerImpl(Profile * profile,AndroidSmsAppManager * android_sms_app_manager)30 AndroidSmsPairingStateTrackerImpl::AndroidSmsPairingStateTrackerImpl(
31     Profile* profile,
32     AndroidSmsAppManager* android_sms_app_manager)
33     : profile_(profile), android_sms_app_manager_(android_sms_app_manager) {
34   android_sms_app_manager_->AddObserver(this);
35   base::ThreadTaskRunnerHandle::Get()->PostTask(
36       FROM_HERE,
37       base::BindOnce(
38           &AndroidSmsPairingStateTrackerImpl::AddCookieChangeListener,
39           weak_ptr_factory_.GetWeakPtr()));
40 }
41 
~AndroidSmsPairingStateTrackerImpl()42 AndroidSmsPairingStateTrackerImpl::~AndroidSmsPairingStateTrackerImpl() {
43   android_sms_app_manager_->RemoveObserver(this);
44 }
45 
IsAndroidSmsPairingComplete()46 bool AndroidSmsPairingStateTrackerImpl::IsAndroidSmsPairingComplete() {
47   return was_paired_on_last_update_;
48 }
49 
AttemptFetchMessagesPairingState()50 void AndroidSmsPairingStateTrackerImpl::AttemptFetchMessagesPairingState() {
51   GetCookieManager()->GetCookieList(
52       GetPairingUrl(), net::CookieOptions::MakeAllInclusive(),
53       base::BindOnce(&AndroidSmsPairingStateTrackerImpl::OnCookiesRetrieved,
54                      base::Unretained(this)));
55 }
56 
OnCookiesRetrieved(const net::CookieAccessResultList & cookies,const net::CookieAccessResultList & excluded_cookies)57 void AndroidSmsPairingStateTrackerImpl::OnCookiesRetrieved(
58     const net::CookieAccessResultList& cookies,
59     const net::CookieAccessResultList& excluded_cookies) {
60   bool was_previously_paired = was_paired_on_last_update_;
61   for (const auto& cookie_with_access_result : cookies) {
62     const net::CanonicalCookie& cookie = cookie_with_access_result.cookie;
63     if (cookie.Name() == kMessagesPairStateCookieName) {
64       PA_LOG(VERBOSE) << "Cookie says Messages paired: " << cookie.Value();
65       was_paired_on_last_update_ = cookie.Value() == kPairedCookieValue;
66       if (was_previously_paired != was_paired_on_last_update_)
67         NotifyPairingStateChanged();
68       return;
69     }
70   }
71 
72   PA_LOG(INFO) << "No Pairing cookie found";
73   was_paired_on_last_update_ = false;
74   if (was_previously_paired != was_paired_on_last_update_)
75     NotifyPairingStateChanged();
76 }
77 
OnCookieChange(const net::CookieChangeInfo & change)78 void AndroidSmsPairingStateTrackerImpl::OnCookieChange(
79     const net::CookieChangeInfo& change) {
80   DCHECK_EQ(kMessagesPairStateCookieName, change.cookie.Name());
81   DCHECK(change.cookie.IsDomainMatch(GetPairingUrl().host()));
82 
83   // NOTE: cookie.Value() cannot be trusted in this callback. The cookie may
84   // have expired or been removed and the Value() does not get updated. It's
85   // cleanest to just re-fetch it.
86   AttemptFetchMessagesPairingState();
87 }
88 
OnInstalledAppUrlChanged()89 void AndroidSmsPairingStateTrackerImpl::OnInstalledAppUrlChanged() {
90   // If the app URL changed, stop any ongoing cookie monitoring and attempt to
91   // add a new change listener.
92   PA_LOG(INFO) << "Installed app url changed to " << GetPairingUrl()
93                << ". Updating cookie listeners.";
94   cookie_listener_receiver_.reset();
95   AddCookieChangeListener();
96 }
97 
GetPairingUrl()98 GURL AndroidSmsPairingStateTrackerImpl::GetPairingUrl() {
99   // If the app registry is not ready, we can't see check what is currently
100   // installed.
101   if (android_sms_app_manager_->IsAppRegistryReady()) {
102     base::Optional<GURL> app_url = android_sms_app_manager_->GetCurrentAppUrl();
103     if (app_url)
104       return *app_url;
105   }
106 
107   // If no app is installed or the app registry is not ready, default to the
108   // expected messages URL.  This will only be incorrect if a migration must
109   // happen.
110   return GetAndroidMessagesURL();
111 }
112 
113 network::mojom::CookieManager*
GetCookieManager()114 AndroidSmsPairingStateTrackerImpl::GetCookieManager() {
115   return content::BrowserContext::GetStoragePartitionForSite(profile_,
116                                                              GetPairingUrl())
117       ->GetCookieManagerForBrowserProcess();
118 }
119 
AddCookieChangeListener()120 void AndroidSmsPairingStateTrackerImpl::AddCookieChangeListener() {
121   // Trigger the first fetch of the sms cookie and start listening for changes.
122   AttemptFetchMessagesPairingState();
123   GetCookieManager()->AddCookieChangeListener(
124       GetPairingUrl(), kMessagesPairStateCookieName,
125       cookie_listener_receiver_.BindNewPipeAndPassRemote());
126 }
127 
128 }  // namespace android_sms
129 
130 }  // namespace chromeos
131