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