summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-15 21:42:33 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-15 21:42:33 +0000
commit403f2e1e0ac2730f2475c71954f47bee3fa886a4 (patch)
tree29638bfdf391a3a2afe1e295cb787dcfd9fb4fa5
parentc519295e83ab20eb42e58e40f28493df43d3d1c4 (diff)
downloadchromium_src-403f2e1e0ac2730f2475c71954f47bee3fa886a4.zip
chromium_src-403f2e1e0ac2730f2475c71954f47bee3fa886a4.tar.gz
chromium_src-403f2e1e0ac2730f2475c71954f47bee3fa886a4.tar.bz2
Wire easy unlock settings UI
- Add API to get/set/clear the pairing info into user prefs; - Add an onTurnFlowFinished event so that the app can clear its state; - Extend EasyUnlockService to store pairing info and provide turn-off flow support; - Update settings UI to trigger turn off flow and observe the turn-off flow status to show pending/error or dismiss when done; - Add EasyUnlockToggleFlow to wrap api calls to server; - Update OAuth2ApiFlow to support application/json content-type and handle 204 as success; BUG=397356,394640 Review URL: https://codereview.chromium.org/475483003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290019 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc98
-rw-r--r--chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h77
-rw-r--r--chrome/browser/resources/options/browser_options.js3
-rw-r--r--chrome/browser/resources/options/easy_unlock_turn_off_overlay.js76
-rw-r--r--chrome/browser/signin/easy_unlock_service.cc131
-rw-r--r--chrome/browser/signin/easy_unlock_service.h43
-rw-r--r--chrome/browser/signin/easy_unlock_service_observer.h17
-rw-r--r--chrome/browser/signin/easy_unlock_toggle_flow.cc213
-rw-r--r--chrome/browser/signin/easy_unlock_toggle_flow.h69
-rw-r--r--chrome/browser/ui/webui/options/browser_options_handler.cc19
-rw-r--r--chrome/browser/ui/webui/options/easy_unlock_handler.cc107
-rw-r--r--chrome/browser/ui/webui/options/easy_unlock_handler.h43
-rw-r--r--chrome/browser/ui/webui/options/options_ui.cc2
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/common/extensions/api/easy_unlock_private.idl63
-rw-r--r--extensions/browser/extension_function_histogram_value.h5
-rw-r--r--google_apis/gaia/oauth2_api_call_flow.cc9
-rw-r--r--google_apis/gaia/oauth2_api_call_flow.h1
-rw-r--r--tools/metrics/histograms/histograms.xml5
20 files changed, 956 insertions, 29 deletions
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
index 1b7f87e..2c242b1 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
@@ -424,5 +424,103 @@ bool EasyUnlockPrivateUpdateScreenlockStateFunction::RunSync() {
return false;
}
+EasyUnlockPrivateSetPermitAccessFunction::
+ EasyUnlockPrivateSetPermitAccessFunction() {
+}
+
+EasyUnlockPrivateSetPermitAccessFunction::
+ ~EasyUnlockPrivateSetPermitAccessFunction() {
+}
+
+bool EasyUnlockPrivateSetPermitAccessFunction::RunSync() {
+ scoped_ptr<easy_unlock_private::SetPermitAccess::Params> params(
+ easy_unlock_private::SetPermitAccess::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ Profile* profile = Profile::FromBrowserContext(browser_context());
+ EasyUnlockService::Get(profile)
+ ->SetPermitAccess(*params->permit_access.ToValue());
+
+ return true;
+}
+
+EasyUnlockPrivateGetPermitAccessFunction::
+ EasyUnlockPrivateGetPermitAccessFunction() {
+}
+
+EasyUnlockPrivateGetPermitAccessFunction::
+ ~EasyUnlockPrivateGetPermitAccessFunction() {
+}
+
+bool EasyUnlockPrivateGetPermitAccessFunction::RunSync() {
+ Profile* profile = Profile::FromBrowserContext(browser_context());
+ const base::DictionaryValue* permit_value =
+ EasyUnlockService::Get(profile)->GetPermitAccess();
+ if (permit_value) {
+ scoped_ptr<easy_unlock_private::PermitRecord> permit =
+ easy_unlock_private::PermitRecord::FromValue(*permit_value);
+ results_ = easy_unlock_private::GetPermitAccess::Results::Create(*permit);
+ }
+
+ return true;
+}
+
+EasyUnlockPrivateClearPermitAccessFunction::
+ EasyUnlockPrivateClearPermitAccessFunction() {
+}
+
+EasyUnlockPrivateClearPermitAccessFunction::
+ ~EasyUnlockPrivateClearPermitAccessFunction() {
+}
+
+bool EasyUnlockPrivateClearPermitAccessFunction::RunSync() {
+ Profile* profile = Profile::FromBrowserContext(browser_context());
+ EasyUnlockService::Get(profile)->ClearPermitAccess();
+ return true;
+}
+
+EasyUnlockPrivateSetRemoteDevicesFunction::
+ EasyUnlockPrivateSetRemoteDevicesFunction() {
+}
+
+EasyUnlockPrivateSetRemoteDevicesFunction::
+ ~EasyUnlockPrivateSetRemoteDevicesFunction() {
+}
+
+bool EasyUnlockPrivateSetRemoteDevicesFunction::RunSync() {
+ scoped_ptr<easy_unlock_private::SetRemoteDevices::Params> params(
+ easy_unlock_private::SetRemoteDevices::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ Profile* profile = Profile::FromBrowserContext(browser_context());
+ if (params->devices.empty()) {
+ EasyUnlockService::Get(profile)->ClearRemoteDevices();
+ } else {
+ base::ListValue devices;
+ for (size_t i = 0; i < params->devices.size(); ++i) {
+ devices.Append(params->devices[i]->ToValue().release());
+ }
+ EasyUnlockService::Get(profile)->SetRemoteDevices(devices);
+ }
+
+ return true;
+}
+
+EasyUnlockPrivateGetRemoteDevicesFunction::
+ EasyUnlockPrivateGetRemoteDevicesFunction() {
+}
+
+EasyUnlockPrivateGetRemoteDevicesFunction::
+ ~EasyUnlockPrivateGetRemoteDevicesFunction() {
+}
+
+bool EasyUnlockPrivateGetRemoteDevicesFunction::RunSync() {
+ Profile* profile = Profile::FromBrowserContext(browser_context());
+ const base::ListValue* devices =
+ EasyUnlockService::Get(profile)->GetRemoteDevices();
+ SetResult(devices ? devices->DeepCopy() : new base::ListValue());
+ return true;
+}
+
} // namespace api
} // namespace extensions
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
index 3802861..215b469 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_function.h"
@@ -180,6 +181,82 @@ class EasyUnlockPrivateUpdateScreenlockStateFunction
DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateUpdateScreenlockStateFunction);
};
+class EasyUnlockPrivateSetPermitAccessFunction : public SyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.setPermitAccess",
+ EASYUNLOCKPRIVATE_SETPERMITACCESS)
+ EasyUnlockPrivateSetPermitAccessFunction();
+
+ private:
+ virtual ~EasyUnlockPrivateSetPermitAccessFunction();
+
+ // SyncExtensionFunction:
+ virtual bool RunSync() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateSetPermitAccessFunction);
+};
+
+class EasyUnlockPrivateGetPermitAccessFunction : public SyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.getPermitAccess",
+ EASYUNLOCKPRIVATE_GETPERMITACCESS)
+ EasyUnlockPrivateGetPermitAccessFunction();
+
+ private:
+ virtual ~EasyUnlockPrivateGetPermitAccessFunction();
+
+ // SyncExtensionFunction:
+ virtual bool RunSync() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateGetPermitAccessFunction);
+};
+
+class EasyUnlockPrivateClearPermitAccessFunction
+ : public SyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.clearPermitAccess",
+ EASYUNLOCKPRIVATE_CLEARPERMITACCESS)
+ EasyUnlockPrivateClearPermitAccessFunction();
+
+ private:
+ virtual ~EasyUnlockPrivateClearPermitAccessFunction();
+
+ // SyncExtensionFunction:
+ virtual bool RunSync() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateClearPermitAccessFunction);
+};
+
+class EasyUnlockPrivateSetRemoteDevicesFunction : public SyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.setRemoteDevices",
+ EASYUNLOCKPRIVATE_SETREMOTEDEVICES)
+ EasyUnlockPrivateSetRemoteDevicesFunction();
+
+ private:
+ virtual ~EasyUnlockPrivateSetRemoteDevicesFunction();
+
+ // SyncExtensionFunction:
+ virtual bool RunSync() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateSetRemoteDevicesFunction);
+};
+
+class EasyUnlockPrivateGetRemoteDevicesFunction : public SyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.getRemoteDevices",
+ EASYUNLOCKPRIVATE_GETREMOTEDEVICES)
+ EasyUnlockPrivateGetRemoteDevicesFunction();
+
+ private:
+ virtual ~EasyUnlockPrivateGetRemoteDevicesFunction();
+
+ // SyncExtensionFunction:
+ virtual bool RunSync() OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateGetRemoteDevicesFunction);
+};
+
} // namespace api
} // namespace extensions
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js
index 39d2df0c..d0354ed 100644
--- a/chrome/browser/resources/options/browser_options.js
+++ b/chrome/browser/resources/options/browser_options.js
@@ -1051,6 +1051,9 @@ cr.define('options', function() {
updateEasyUnlock_: function(hasPairing) {
$('easy-unlock-setup').hidden = hasPairing;
$('easy-unlock-enable').hidden = !hasPairing;
+ if (!hasPairing && EasyUnlockTurnOffOverlay.getInstance().visible) {
+ EasyUnlockTurnOffOverlay.dismiss();
+ }
},
/**
diff --git a/chrome/browser/resources/options/easy_unlock_turn_off_overlay.js b/chrome/browser/resources/options/easy_unlock_turn_off_overlay.js
index dd19b26..8b2a589 100644
--- a/chrome/browser/resources/options/easy_unlock_turn_off_overlay.js
+++ b/chrome/browser/resources/options/easy_unlock_turn_off_overlay.js
@@ -6,6 +6,16 @@ cr.define('options', function() {
var Page = cr.ui.pageManager.Page;
var PageManager = cr.ui.pageManager.PageManager;
+ // UI state of the turn off overlay.
+ // @enum {string}
+ var UIState = {
+ UNKNOWN: 'unknown',
+ OFFLINE: 'offline',
+ IDLE: 'idle',
+ PENDING: 'pending',
+ SERVER_ERROR: 'server-error',
+ };
+
/**
* EasyUnlockTurnOffOverlay class
* Encapsulated handling of the Factory Reset confirmation overlay page.
@@ -23,6 +33,37 @@ cr.define('options', function() {
// Inherit EasyUnlockTurnOffOverlay from Page.
__proto__: Page.prototype,
+ /** Current UI state */
+ uiState_: UIState.UNKNKOWN,
+ get uiState() {
+ return this.uiState_;
+ },
+ set uiState(newUiState) {
+ if (newUiState == this.uiState_)
+ return;
+
+ this.uiState_ = newUiState;
+ switch (this.uiState_) {
+ case UIState.OFFLINE:
+ this.setUpOfflineUI_();
+ break;
+ case UIState.IDLE:
+ this.setUpTurnOffUI_(false);
+ break;
+ case UIState.PENDING:
+ this.setUpTurnOffUI_(true);
+ break;
+ case UIState.SERVER_ERROR:
+ this.setUpServerErrorUI_();
+ break;
+ default:
+ console.error('Unknow Easy unlock turn off UI state: ' +
+ this.uiState_);
+ this.setUpTurnOffUI_(false);
+ break;
+ }
+ },
+
/** @override */
initializePage: function() {
Page.prototype.initializePage.call(this);
@@ -31,23 +72,26 @@ cr.define('options', function() {
EasyUnlockTurnOffOverlay.dismiss();
};
$('easy-unlock-turn-off-confirm').onclick = function(event) {
- $('easy-unlock-turn-off-confirm').disabled = true;
- this.setSpinnerVisible_(true);
-
- // TODO(xiyuan): Wire this up.
- // chrome.send('turnOffEasyUnlock');
+ this.uiState = UIState.PENDING;
+ chrome.send('easyUnlockRequestTurnOff');
}.bind(this);
},
/** @override */
didShowPage: function() {
if (navigator.onLine) {
- this.setUpTurnOffUI_();
+ this.uiState = UIState.IDLE;
+ chrome.send('easyUnlockGetTurnOffFlowStatus');
} else {
- this.setUpOfflineUI_();
+ this.uiState = UIState.OFFLINE;
}
},
+ /** @override */
+ didClosePage: function() {
+ chrome.send('easyUnlockTurnOffOverlayDismissed');
+ },
+
/**
* Returns the button strip element.
* @return {HTMLDivElement} The container div of action buttons.
@@ -91,9 +135,10 @@ cr.define('options', function() {
/**
* Set up UI for turning off Easy Unlock.
+ * @param {boolean} pending Whether there is a pending turn-off call.
* @private
*/
- setUpTurnOffUI_: function() {
+ setUpTurnOffUI_: function(pending) {
$('easy-unlock-turn-off-title').textContent =
loadTimeData.getString('easyUnlockTurnOffTitle');
$('easy-unlock-turn-off-messagee').textContent =
@@ -102,8 +147,8 @@ cr.define('options', function() {
loadTimeData.getString('easyUnlockTurnOffButton');
this.setActionButtonsVisible_(true);
- this.setSpinnerVisible_(false);
- $('easy-unlock-turn-off-confirm').disabled = false;
+ this.setSpinnerVisible_(pending);
+ $('easy-unlock-turn-off-confirm').disabled = pending;
$('easy-unlock-turn-off-dismiss').hidden = false;
},
@@ -126,10 +171,21 @@ cr.define('options', function() {
},
};
+ /**
+ * Closes the Easy unlock turn off overlay.
+ */
EasyUnlockTurnOffOverlay.dismiss = function() {
PageManager.closeOverlay();
};
+ /**
+ * Update UI to reflect the turn off operation status.
+ * @param {string} newState The UIState string representing the new state.
+ */
+ EasyUnlockTurnOffOverlay.updateUIState = function(newState) {
+ EasyUnlockTurnOffOverlay.getInstance().uiState = newState;
+ };
+
// Export
return {
EasyUnlockTurnOffOverlay: EasyUnlockTurnOffOverlay
diff --git a/chrome/browser/signin/easy_unlock_service.cc b/chrome/browser/signin/easy_unlock_service.cc
index 10dbf20..52d9d5c 100644
--- a/chrome/browser/signin/easy_unlock_service.cc
+++ b/chrome/browser/signin/easy_unlock_service.cc
@@ -9,12 +9,15 @@
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
#include "base/values.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
#include "chrome/browser/signin/easy_unlock_service_factory.h"
+#include "chrome/browser/signin/easy_unlock_service_observer.h"
+#include "chrome/browser/signin/easy_unlock_toggle_flow.h"
#include "chrome/browser/signin/screenlock_bridge.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/common/chrome_switches.h"
@@ -31,6 +34,15 @@
namespace {
+// Key name of the local device permit record dictonary in kEasyUnlockPairing.
+const char kKeyPermitAccess[] = "permitAccess";
+
+// Key name of the remote device list in kEasyUnlockPairing.
+const char kKeyDevices[] = "devices";
+
+// Key name of the phone public key in a device dictionary.
+const char kKeyPhoneId[] = "permitRecord.id";
+
extensions::ComponentLoader* GetComponentLoader(
content::BrowserContext* context) {
extensions::ExtensionSystem* extension_system =
@@ -48,6 +60,7 @@ EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
EasyUnlockService::EasyUnlockService(Profile* profile)
: profile_(profile),
+ turn_off_flow_status_(IDLE),
weak_ptr_factory_(this) {
extensions::ExtensionSystem::Get(profile_)->ready().Post(
FROM_HERE,
@@ -108,7 +121,6 @@ bool EasyUnlockService::IsAllowed() {
#endif
}
-
EasyUnlockScreenlockStateHandler*
EasyUnlockService::GetScreenlockStateHandler() {
if (!IsAllowed())
@@ -122,6 +134,98 @@ EasyUnlockScreenlockStateHandler*
return screenlock_state_handler_.get();
}
+const base::DictionaryValue* EasyUnlockService::GetPermitAccess() const {
+ const base::DictionaryValue* pairing_dict =
+ profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
+ const base::DictionaryValue* permit_dict = NULL;
+ if (pairing_dict &&
+ pairing_dict->GetDictionary(kKeyPermitAccess, &permit_dict)) {
+ return permit_dict;
+ }
+
+ return NULL;
+}
+
+void EasyUnlockService::SetPermitAccess(const base::DictionaryValue& permit) {
+ DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
+ prefs::kEasyUnlockPairing);
+ pairing_update->SetWithoutPathExpansion(kKeyPermitAccess, permit.DeepCopy());
+}
+
+void EasyUnlockService::ClearPermitAccess() {
+ DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
+ prefs::kEasyUnlockPairing);
+ pairing_update->RemoveWithoutPathExpansion(kKeyPermitAccess, NULL);
+}
+
+const base::ListValue* EasyUnlockService::GetRemoteDevices() const {
+ const base::DictionaryValue* pairing_dict =
+ profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
+ const base::ListValue* devices = NULL;
+ if (pairing_dict && pairing_dict->GetList(kKeyDevices, &devices)) {
+ return devices;
+ }
+
+ return NULL;
+}
+
+void EasyUnlockService::SetRemoteDevices(const base::ListValue& devices) {
+ DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
+ prefs::kEasyUnlockPairing);
+ pairing_update->SetWithoutPathExpansion(kKeyDevices, devices.DeepCopy());
+}
+
+void EasyUnlockService::ClearRemoteDevices() {
+ DictionaryPrefUpdate pairing_update(profile_->GetPrefs(),
+ prefs::kEasyUnlockPairing);
+ pairing_update->RemoveWithoutPathExpansion(kKeyDevices, NULL);
+}
+
+void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void EasyUnlockService::RemoveObserver(EasyUnlockServiceObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void EasyUnlockService::RunTurnOffFlow() {
+ if (turn_off_flow_status_ == PENDING)
+ return;
+
+ SetTurnOffFlowStatus(PENDING);
+
+ // Currently there should only be one registered phone.
+ // TODO(xiyuan): Revisit this when server supports toggle for all or
+ // there are multiple phones.
+ const base::DictionaryValue* pairing_dict =
+ profile_->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
+ const base::ListValue* devices_list = NULL;
+ const base::DictionaryValue* first_device = NULL;
+ std::string phone_public_key;
+ if (!pairing_dict || !pairing_dict->GetList(kKeyDevices, &devices_list) ||
+ !devices_list || !devices_list->GetDictionary(0, &first_device) ||
+ !first_device ||
+ !first_device->GetString(kKeyPhoneId, &phone_public_key)) {
+ LOG(WARNING) << "Bad easy unlock pairing data, wiping out local data";
+ OnTurnOffFlowFinished(true);
+ return;
+ }
+
+ turn_off_flow_.reset(new EasyUnlockToggleFlow(
+ profile_,
+ phone_public_key,
+ false,
+ base::Bind(&EasyUnlockService::OnTurnOffFlowFinished,
+ base::Unretained(this))));
+ turn_off_flow_->Start();
+}
+
+void EasyUnlockService::ResetTurnOffFlow() {
+ turn_off_flow_.reset();
+ SetTurnOffFlowStatus(IDLE);
+}
+
void EasyUnlockService::Initialize() {
registrar_.Init(profile_->GetPrefs());
registrar_.Add(
@@ -169,3 +273,28 @@ void EasyUnlockService::OnPrefsChanged() {
screenlock_state_handler_.reset();
}
}
+
+void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) {
+ turn_off_flow_status_ = status;
+ FOR_EACH_OBSERVER(
+ EasyUnlockServiceObserver, observers_, OnTurnOffOperationStatusChanged());
+}
+
+void EasyUnlockService::OnTurnOffFlowFinished(bool success) {
+ turn_off_flow_.reset();
+
+ if (!success) {
+ SetTurnOffFlowStatus(FAIL);
+ return;
+ }
+
+ ClearRemoteDevices();
+ SetTurnOffFlowStatus(IDLE);
+
+ if (GetComponentLoader(profile_)->Exists(extension_misc::kEasyUnlockAppId)) {
+ extensions::ExtensionSystem* extension_system =
+ extensions::ExtensionSystem::Get(profile_);
+ extension_system->extension_service()->ReloadExtension(
+ extension_misc::kEasyUnlockAppId);
+ }
+}
diff --git a/chrome/browser/signin/easy_unlock_service.h b/chrome/browser/signin/easy_unlock_service.h
index a8379f7..996b411 100644
--- a/chrome/browser/signin/easy_unlock_service.h
+++ b/chrome/browser/signin/easy_unlock_service.h
@@ -6,19 +6,34 @@
#define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_H_
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
#include "base/prefs/pref_change_registrar.h"
#include "components/keyed_service/core/keyed_service.h"
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
namespace user_prefs {
class PrefRegistrySyncable;
}
class EasyUnlockScreenlockStateHandler;
+class EasyUnlockServiceObserver;
+class EasyUnlockToggleFlow;
class Profile;
class EasyUnlockService : public KeyedService {
public:
+ enum TurnOffFlowStatus {
+ IDLE,
+ PENDING,
+ FAIL,
+ };
+
explicit EasyUnlockService(Profile* profile);
virtual ~EasyUnlockService();
@@ -42,16 +57,44 @@ class EasyUnlockService : public KeyedService {
// Unlock gets disabled.
EasyUnlockScreenlockStateHandler* GetScreenlockStateHandler();
+ // Gets/Sets/Clears the permit access for the local device.
+ const base::DictionaryValue* GetPermitAccess() const;
+ void SetPermitAccess(const base::DictionaryValue& permit);
+ void ClearPermitAccess();
+
+ // Gets/Sets/Clears the remote devices list.
+ const base::ListValue* GetRemoteDevices() const;
+ void SetRemoteDevices(const base::ListValue& devices);
+ void ClearRemoteDevices();
+
+ void RunTurnOffFlow();
+ void ResetTurnOffFlow();
+
+ void AddObserver(EasyUnlockServiceObserver* observer);
+ void RemoveObserver(EasyUnlockServiceObserver* observer);
+
+ TurnOffFlowStatus turn_off_flow_status() const {
+ return turn_off_flow_status_;
+ }
+
private:
void Initialize();
void LoadApp();
void UnloadApp();
void OnPrefsChanged();
+ void SetTurnOffFlowStatus(TurnOffFlowStatus status);
+ void OnTurnOffFlowFinished(bool success);
+
Profile* profile_;
PrefChangeRegistrar registrar_;
// Created lazily in |GetScreenlockStateHandler|.
scoped_ptr<EasyUnlockScreenlockStateHandler> screenlock_state_handler_;
+
+ TurnOffFlowStatus turn_off_flow_status_;
+ scoped_ptr<EasyUnlockToggleFlow> turn_off_flow_;
+ ObserverList<EasyUnlockServiceObserver> observers_;
+
base::WeakPtrFactory<EasyUnlockService> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(EasyUnlockService);
diff --git a/chrome/browser/signin/easy_unlock_service_observer.h b/chrome/browser/signin/easy_unlock_service_observer.h
new file mode 100644
index 0000000..f5569ff6
--- /dev/null
+++ b/chrome/browser/signin/easy_unlock_service_observer.h
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_OBSERVER_H_
+#define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_OBSERVER_H_
+
+class EasyUnlockServiceObserver {
+ public:
+ // Invoked when turn-off operation status changes.
+ virtual void OnTurnOffOperationStatusChanged() = 0;
+
+ protected:
+ virtual ~EasyUnlockServiceObserver() {}
+};
+
+#endif // CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SERVICE_OBSERVER_H_
diff --git a/chrome/browser/signin/easy_unlock_toggle_flow.cc b/chrome/browser/signin/easy_unlock_toggle_flow.cc
new file mode 100644
index 0000000..7655b2b
--- /dev/null
+++ b/chrome/browser/signin/easy_unlock_toggle_flow.cc
@@ -0,0 +1,213 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/easy_unlock_toggle_flow.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "extensions/browser/extension_system.h"
+#include "google_apis/gaia/oauth2_api_call_flow.h"
+#include "net/url_request/url_fetcher.h"
+
+namespace {
+
+const char kEasyUnlockToggleUrl[] =
+ "https://www.googleapis.com/cryptauth/v1/deviceSync/toggleeasyunlock";
+
+std::vector<std::string> GetScopes() {
+ std::vector<std::string> scopes;
+ scopes.push_back("https://www.googleapis.com/auth/proximity_auth");
+ scopes.push_back("https://www.googleapis.com/auth/cryptauth");
+ return scopes;
+}
+
+std::string GetEasyUnlockAppClientId(Profile * profile) {
+ extensions::ExtensionSystem* extension_system =
+ extensions::ExtensionSystem::Get(profile);
+ ExtensionService* extension_service = extension_system->extension_service();
+ const extensions::Extension* easy_unlock_app =
+ extension_service->GetInstalledExtension(
+ extension_misc::kEasyUnlockAppId);
+ if (!easy_unlock_app)
+ return std::string();
+
+ const extensions::OAuth2Info& oauth2_info =
+ extensions::OAuth2Info::GetOAuth2Info(easy_unlock_app);
+ return oauth2_info.client_id;
+}
+
+} // namespace
+
+class EasyUnlockToggleFlow::ToggleApiCall : public OAuth2ApiCallFlow {
+ public:
+ ToggleApiCall(EasyUnlockToggleFlow* flow,
+ net::URLRequestContextGetter* context,
+ const std::string& access_token,
+ const std::string& phone_public_key,
+ bool toggle_enable);
+ virtual ~ToggleApiCall();
+
+ // OAuth2ApiCallFlow
+ virtual GURL CreateApiCallUrl() OVERRIDE;
+ virtual std::string CreateApiCallBody() OVERRIDE;
+ virtual std::string CreateApiCallBodyContentType() OVERRIDE;
+ virtual void ProcessApiCallSuccess(const net::URLFetcher* source) OVERRIDE;
+ virtual void ProcessApiCallFailure(const net::URLFetcher* source) OVERRIDE;
+ virtual void ProcessNewAccessToken(const std::string& access_token) OVERRIDE;
+ virtual void ProcessMintAccessTokenFailure(
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ private:
+ EasyUnlockToggleFlow* flow_;
+ const std::string phone_public_key_;
+ const bool toggle_enable_;
+
+ DISALLOW_COPY_AND_ASSIGN(ToggleApiCall);
+};
+
+EasyUnlockToggleFlow::ToggleApiCall::ToggleApiCall(
+ EasyUnlockToggleFlow* flow,
+ net::URLRequestContextGetter* context,
+ const std::string& access_token,
+ const std::string& phone_public_key,
+ bool toggle_enable)
+ : OAuth2ApiCallFlow(context,
+ std::string(),
+ access_token,
+ GetScopes()),
+ flow_(flow),
+ phone_public_key_(phone_public_key),
+ toggle_enable_(toggle_enable) {
+}
+
+EasyUnlockToggleFlow::ToggleApiCall::~ToggleApiCall() {
+}
+
+GURL EasyUnlockToggleFlow::ToggleApiCall::CreateApiCallUrl() {
+ return GURL(kEasyUnlockToggleUrl);
+}
+
+std::string EasyUnlockToggleFlow::ToggleApiCall::CreateApiCallBody() {
+ const char kBodyFormat[] = "{\"enable\":%s,\"publicKey\":\"%s\"}";
+ return base::StringPrintf(
+ kBodyFormat,
+ toggle_enable_ ? "true" : "false",
+ phone_public_key_.c_str());
+}
+
+std::string
+EasyUnlockToggleFlow::ToggleApiCall::CreateApiCallBodyContentType() {
+ return "application/json";
+}
+
+void EasyUnlockToggleFlow::ToggleApiCall::ProcessApiCallSuccess(
+ const net::URLFetcher* source) {
+ flow_->ReportToggleApiCallResult(true);
+}
+
+void EasyUnlockToggleFlow::ToggleApiCall::ProcessApiCallFailure(
+ const net::URLFetcher* source) {
+ flow_->ReportToggleApiCallResult(false);
+}
+
+void EasyUnlockToggleFlow::ToggleApiCall::ProcessNewAccessToken(
+ const std::string& access_token) {
+ NOTREACHED();
+}
+
+void EasyUnlockToggleFlow::ToggleApiCall::ProcessMintAccessTokenFailure(
+ const GoogleServiceAuthError& error) {
+ NOTREACHED();
+}
+
+EasyUnlockToggleFlow::EasyUnlockToggleFlow(Profile* profile,
+ const std::string& phone_public_key,
+ bool toggle_enable,
+ const ToggleFlowCallback& callback)
+ : OAuth2TokenService::Consumer("easy_unlock_toggle"),
+ profile_(profile),
+ phone_public_key_(phone_public_key),
+ toggle_enable_(toggle_enable),
+ callback_(callback) {
+}
+
+EasyUnlockToggleFlow::~EasyUnlockToggleFlow() {
+}
+
+void EasyUnlockToggleFlow::Start() {
+ ProfileOAuth2TokenService* token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile_);
+ token_request_ =
+ token_service->StartRequest(signin_manager->GetAuthenticatedAccountId(),
+ OAuth2TokenService::ScopeSet(),
+ this);
+}
+
+void EasyUnlockToggleFlow::OnGetTokenSuccess(
+ const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) {
+ DCHECK_EQ(token_request_.get(), request);
+ token_request_.reset();
+
+ mint_token_flow_.reset(
+ new OAuth2MintTokenFlow(profile_->GetRequestContext(),
+ this,
+ OAuth2MintTokenFlow::Parameters(
+ access_token,
+ extension_misc::kEasyUnlockAppId,
+ GetEasyUnlockAppClientId(profile_),
+ GetScopes(),
+ OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE)));
+ mint_token_flow_->Start();
+}
+
+void EasyUnlockToggleFlow::OnGetTokenFailure(
+ const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) {
+ DCHECK_EQ(token_request_.get(), request);
+ token_request_.reset();
+
+ LOG(ERROR) << "Easy unlock toggle flow, failed to get access token,"
+ << "error=" << error.state();
+ callback_.Run(false);
+}
+
+void EasyUnlockToggleFlow::OnMintTokenSuccess(const std::string& access_token,
+ int time_to_live) {
+ toggle_api_call_.reset(new ToggleApiCall(this,
+ profile_->GetRequestContext(),
+ access_token,
+ phone_public_key_,
+ toggle_enable_));
+ toggle_api_call_->Start();
+}
+
+void EasyUnlockToggleFlow::OnMintTokenFailure(
+ const GoogleServiceAuthError& error) {
+ LOG(ERROR) << "Easy unlock toggle flow, failed to mint access token,"
+ << "error=" << error.state();
+ callback_.Run(false);
+}
+
+void EasyUnlockToggleFlow::OnIssueAdviceSuccess(
+ const IssueAdviceInfo& issue_advice) {
+ NOTREACHED();
+}
+
+void EasyUnlockToggleFlow::ReportToggleApiCallResult(bool success) {
+ callback_.Run(success);
+}
diff --git a/chrome/browser/signin/easy_unlock_toggle_flow.h b/chrome/browser/signin/easy_unlock_toggle_flow.h
new file mode 100644
index 0000000..91ec965
--- /dev/null
+++ b/chrome/browser/signin/easy_unlock_toggle_flow.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_EASY_UNLOCK_TOGGLE_FLOW_H_
+#define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_TOGGLE_FLOW_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_mint_token_flow.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+class Profile;
+
+class EasyUnlockToggleFlow : public OAuth2TokenService::Consumer,
+ public OAuth2MintTokenFlow::Delegate {
+ public:
+ // Callback to indicate whether the call succeeds or not.
+ typedef base::Callback<void(bool)> ToggleFlowCallback;
+
+ EasyUnlockToggleFlow(Profile* profile,
+ const std::string& phone_public_key,
+ bool toggle_enable,
+ const ToggleFlowCallback& callback);
+ virtual ~EasyUnlockToggleFlow();
+
+ void Start();
+
+ // OAuth2TokenService::Consumer
+ virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+ const std::string& access_token,
+ const base::Time& expiration_time) OVERRIDE;
+ virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ // OAuth2MintTokenFlow::Delegate
+ virtual void OnMintTokenSuccess(const std::string& access_token,
+ int time_to_live) OVERRIDE;
+ virtual void OnMintTokenFailure(
+ const GoogleServiceAuthError& error) OVERRIDE;
+ virtual void OnIssueAdviceSuccess(
+ const IssueAdviceInfo& issue_advice) OVERRIDE;
+
+ private:
+ // Derived OAuth2ApiCallFlow class to make toggle api call after access token
+ // is available.
+ class ToggleApiCall;
+
+ // Callbacks from ToggleApiCall
+ void ReportToggleApiCallResult(bool success);
+
+ Profile* profile_;
+ const std::string phone_public_key_;
+ const bool toggle_enable_;
+ ToggleFlowCallback callback_;
+
+ scoped_ptr<OAuth2TokenService::Request> token_request_;
+ scoped_ptr<OAuth2MintTokenFlow> mint_token_flow_;
+ scoped_ptr<ToggleApiCall> toggle_api_call_;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockToggleFlow);
+};
+
+#endif // CHROME_BROWSER_SIGNIN_EASY_UNLOCK_TOGGLE_FLOW_H_
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index d33ad33..1863c7d 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -250,20 +250,6 @@ void BrowserOptionsHandler::GetLocalizedValues(base::DictionaryValue* values) {
{ "easyUnlockSectionTitle", IDS_OPTIONS_EASY_UNLOCK_SECTION_TITLE },
{ "easyUnlockSetupButton", IDS_OPTIONS_EASY_UNLOCK_SETUP_BUTTON },
{ "easyUnlockSetupIntro", IDS_OPTIONS_EASY_UNLOCK_SETUP_INTRO },
- { "easyUnlockTurnOffButton", IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_BUTTON },
- { "easyUnlockTurnOffTitle", IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_TITLE },
- { "easyUnlockTurnOffDescription",
- IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_DESCRIPTION },
- { "easyUnlockTurnOffOfflineTitle",
- IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_OFFLINE_TITLE },
- { "easyUnlockTurnOffOfflineMessage",
- IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_OFFLINE_MESSAGE },
- { "easyUnlockTurnOffErrorTitle",
- IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_ERROR_TITLE },
- { "easyUnlockTurnOffErrorMessage",
- IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_ERROR_MESSAGE },
- { "easyUnlockTurnOffRetryButton",
- IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_RETRY_BUTTON },
{ "extensionControlled", IDS_OPTIONS_TAB_EXTENSION_CONTROLLED },
{ "extensionDisable", IDS_OPTIONS_TAB_EXTENSION_CONTROLLED_DISABLE },
{ "fontSettingsCustomizeFontsButton",
@@ -1838,8 +1824,9 @@ void BrowserOptionsHandler::SetupManagingSupervisedUsers() {
void BrowserOptionsHandler::SetupEasyUnlock() {
// TODO(xiyuan): Update when pairing data is really availble.
- bool has_pairing = !Profile::FromWebUI(web_ui())->GetPrefs()
- ->GetDictionary(prefs::kEasyUnlockPairing)->empty();
+ const base::ListValue* devices =
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->GetRemoteDevices();
+ bool has_pairing = devices && !devices->empty();
base::FundamentalValue has_pairing_value(has_pairing);
web_ui()->CallJavascriptFunction(
"BrowserOptions.updateEasyUnlock",
diff --git a/chrome/browser/ui/webui/options/easy_unlock_handler.cc b/chrome/browser/ui/webui/options/easy_unlock_handler.cc
new file mode 100644
index 0000000..493a6d7
--- /dev/null
+++ b/chrome/browser/ui/webui/options/easy_unlock_handler.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/options/easy_unlock_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
+#include "content/public/browser/web_ui.h"
+#include "grit/generated_resources.h"
+
+namespace options {
+
+EasyUnlockHandler::EasyUnlockHandler() {
+}
+
+EasyUnlockHandler::~EasyUnlockHandler() {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->RemoveObserver(this);
+}
+
+void EasyUnlockHandler::GetLocalizedValues(base::DictionaryValue* values) {
+ static OptionsStringResource resources[] = {
+ {"easyUnlockTurnOffButton", IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_BUTTON},
+ {"easyUnlockTurnOffTitle", IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_TITLE},
+ {"easyUnlockTurnOffDescription",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_DESCRIPTION},
+ {"easyUnlockTurnOffOfflineTitle",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_OFFLINE_TITLE},
+ {"easyUnlockTurnOffOfflineMessage",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_OFFLINE_MESSAGE},
+ {"easyUnlockTurnOffErrorTitle",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_ERROR_TITLE},
+ {"easyUnlockTurnOffErrorMessage",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_ERROR_MESSAGE},
+ {"easyUnlockTurnOffRetryButton",
+ IDS_OPTIONS_EASY_UNLOCK_TURN_OFF_RETRY_BUTTON},
+ };
+
+ RegisterStrings(values, resources, arraysize(resources));
+}
+
+void EasyUnlockHandler::InitializeHandler() {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->AddObserver(this);
+}
+
+void EasyUnlockHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockGetTurnOffFlowStatus",
+ base::Bind(&EasyUnlockHandler::HandleGetTurnOffFlowStatus,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockRequestTurnOff",
+ base::Bind(&EasyUnlockHandler::HandleRequestTurnOff,
+ base::Unretained(this)));
+ web_ui()->RegisterMessageCallback(
+ "easyUnlockTurnOffOverlayDismissed",
+ base::Bind(&EasyUnlockHandler::HandlePageDismissed,
+ base::Unretained(this)));
+}
+
+void EasyUnlockHandler::OnTurnOffOperationStatusChanged() {
+ SendTurnOffOperationStatus();
+}
+
+void EasyUnlockHandler::SendTurnOffOperationStatus() {
+ EasyUnlockService::TurnOffFlowStatus status =
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))
+ ->turn_off_flow_status();
+
+ // Translate status into JS UI state string. Note the translated string
+ // should match UIState defined in easy_unlock_turn_off_overlay.js.
+ std::string status_string;
+ switch (status) {
+ case EasyUnlockService::IDLE:
+ status_string = "idle";
+ break;
+ case EasyUnlockService::PENDING:
+ status_string = "pending";
+ break;
+ case EasyUnlockService::FAIL:
+ status_string = "server-error";
+ break;
+ default:
+ LOG(ERROR) << "Unknown Easy unlock turn-off operation status: " << status;
+ status_string = "idle";
+ break;
+ }
+ web_ui()->CallJavascriptFunction("EasyUnlockTurnOffOverlay.updateUIState",
+ base::StringValue(status_string));
+}
+
+void EasyUnlockHandler::HandleGetTurnOffFlowStatus(
+ const base::ListValue* args) {
+ SendTurnOffOperationStatus();
+}
+
+void EasyUnlockHandler::HandleRequestTurnOff(const base::ListValue* args) {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->RunTurnOffFlow();
+}
+
+void EasyUnlockHandler::HandlePageDismissed(const base::ListValue* args) {
+ EasyUnlockService::Get(Profile::FromWebUI(web_ui()))->ResetTurnOffFlow();
+}
+
+} // namespace options
diff --git a/chrome/browser/ui/webui/options/easy_unlock_handler.h b/chrome/browser/ui/webui/options/easy_unlock_handler.h
new file mode 100644
index 0000000..24173fa
--- /dev/null
+++ b/chrome/browser/ui/webui/options/easy_unlock_handler.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_EASY_UNLOCK_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_EASY_UNLOCK_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/signin/easy_unlock_service_observer.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+
+namespace options {
+
+class EasyUnlockHandler : public OptionsPageUIHandler,
+ public EasyUnlockServiceObserver {
+ public:
+ EasyUnlockHandler();
+ virtual ~EasyUnlockHandler();
+
+ // OptionsPageUIHandler
+ virtual void InitializeHandler() OVERRIDE;
+ virtual void GetLocalizedValues(base::DictionaryValue* values) OVERRIDE;
+
+ // WebUIMessageHandler
+ virtual void RegisterMessages() OVERRIDE;
+
+ // EasyUnlockServiceObserver
+ virtual void OnTurnOffOperationStatusChanged() OVERRIDE;
+
+ private:
+ void SendTurnOffOperationStatus();
+
+ // JS callbacks.
+ void HandleGetTurnOffFlowStatus(const base::ListValue* args);
+ void HandleRequestTurnOff(const base::ListValue* args);
+ void HandlePageDismissed(const base::ListValue* args);
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_EASY_UNLOCK_HANDLER_H_
diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc
index 14a49f3..4a703e2 100644
--- a/chrome/browser/ui/webui/options/options_ui.cc
+++ b/chrome/browser/ui/webui/options/options_ui.cc
@@ -30,6 +30,7 @@
#include "chrome/browser/ui/webui/options/cookies_view_handler.h"
#include "chrome/browser/ui/webui/options/core_options_handler.h"
#include "chrome/browser/ui/webui/options/create_profile_handler.h"
+#include "chrome/browser/ui/webui/options/easy_unlock_handler.h"
#include "chrome/browser/ui/webui/options/font_settings_handler.h"
#include "chrome/browser/ui/webui/options/handler_options_handler.h"
#include "chrome/browser/ui/webui/options/home_page_overlay_handler.h"
@@ -273,6 +274,7 @@ OptionsUI::OptionsUI(content::WebUI* web_ui)
AddOptionsPageUIHandler(localized_strings, new ContentSettingsHandler());
AddOptionsPageUIHandler(localized_strings, new CookiesViewHandler());
AddOptionsPageUIHandler(localized_strings, new CreateProfileHandler());
+ AddOptionsPageUIHandler(localized_strings, new EasyUnlockHandler());
AddOptionsPageUIHandler(localized_strings, new FontSettingsHandler());
#if defined(ENABLE_GOOGLE_NOW)
AddOptionsPageUIHandler(localized_strings, new GeolocationOptionsHandler());
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 83a4042..0345d1b 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1544,6 +1544,8 @@
'browser/signin/easy_unlock_service.h',
'browser/signin/easy_unlock_service_factory.cc',
'browser/signin/easy_unlock_service_factory.h',
+ 'browser/signin/easy_unlock_toggle_flow.cc',
+ 'browser/signin/easy_unlock_toggle_flow.h',
'browser/speech/extension_api/tts_engine_extension_api.cc',
'browser/speech/extension_api/tts_engine_extension_api.h',
'browser/speech/extension_api/tts_engine_extension_observer.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 3db0a98..5718eee 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1706,6 +1706,8 @@
'browser/ui/webui/options/core_options_handler.h',
'browser/ui/webui/options/create_profile_handler.cc',
'browser/ui/webui/options/create_profile_handler.h',
+ 'browser/ui/webui/options/easy_unlock_handler.cc',
+ 'browser/ui/webui/options/easy_unlock_handler.h',
'browser/ui/webui/options/font_settings_handler.cc',
'browser/ui/webui/options/font_settings_handler.h',
'browser/ui/webui/options/font_settings_utils.h',
diff --git a/chrome/common/extensions/api/easy_unlock_private.idl b/chrome/common/extensions/api/easy_unlock_private.idl
index 32e6410..1648853 100644
--- a/chrome/common/extensions/api/easy_unlock_private.idl
+++ b/chrome/common/extensions/api/easy_unlock_private.idl
@@ -44,6 +44,9 @@
AUTHENTICATED
};
+ // Type of a permit. All lower case to match permit.PermitRecord.Type.
+ enum PermitType {access, license};
+
// Options that can be passed to |unwrapSecureMessage| method.
dictionary UnwrapSecureMessageOptions {
// The data associated with the message. For the message to be succesfully
@@ -86,6 +89,39 @@
SignatureType? signType;
};
+ // A permit record contains the credentials used to request or grant
+ // authorization of a permit.
+ dictionary PermitRecord {
+ // ID of the permit, which identifies the service/application that these
+ // permit records are used in.
+ DOMString permitId;
+
+ // An identifier for this record that should be unique among all other
+ // records of the same permit.
+ DOMString id;
+
+ // Type of the record.
+ PermitType type;
+
+ // Websafe base64 encoded payload data of the record.
+ DOMString data;
+ };
+
+ // Device information that can be authenticated for Easy unlock.
+ dictionary Device {
+ // The Bluetooth address of the device.
+ DOMString bluetoothAddress;
+
+ // The name of the device.
+ DOMString? name;
+
+ // The permit record of the device.
+ PermitRecord? permitRecord;
+
+ // Websafe base64 encoded persistent symmetric key.
+ DOMString? psk;
+ };
+
// Callback for crypto methods that return a single array buffer.
callback DataCallback = void(optional ArrayBuffer data);
@@ -99,6 +135,12 @@
callback KeyPairCallback = void(optional ArrayBuffer public_key,
optional ArrayBuffer private_key);
+ // Callback for the getPermitAccess() method.
+ callback GetPermitAccessCallback = void(optional PermitRecord permitAccess);
+
+ // Callback for the getRemoteDevices() method.
+ callback GetRemoteDevicesCallback = void(Device[] devices);
+
interface Functions {
// Gets localized strings required to render the API.
//
@@ -175,5 +217,26 @@
// Updates the screenlock state to reflect the Easy Unlock app state.
static void updateScreenlockState(State state,
optional EmptyCallback callback);
+
+ // Saves the permit record for the local device.
+ // |permitAccess|: The permit record to be saved.
+ // |callback|: Called to indicate success or failure.
+ static void setPermitAccess(PermitRecord permitAccess,
+ optional EmptyCallback callback);
+
+ // Gets the permit record for the local device.
+ static void getPermitAccess(GetPermitAccessCallback callback);
+
+ // Clears the permit record for the local device.
+ static void clearPermitAccess(optional EmptyCallback callback);
+
+ // Saves the remote device list.
+ // |devices|: The list of remote devices to be saved.
+ // |callback|: Called to indicate success or failure.
+ static void setRemoteDevices(Device[] devices,
+ optional EmptyCallback callback);
+
+ // Gets the remote device list.
+ static void getRemoteDevices(GetRemoteDevicesCallback callback);
};
};
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index f77176c..d7687ea 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -936,6 +936,11 @@ enum HistogramValue {
USBPRIVATE_GETDEVICEINFO,
EASYUNLOCKPRIVATE_UPDATESCREENLOCKSTATE,
CAST_CHANNEL_GETLOGS,
+ EASYUNLOCKPRIVATE_SETPERMITACCESS,
+ EASYUNLOCKPRIVATE_GETPERMITACCESS,
+ EASYUNLOCKPRIVATE_CLEARPERMITACCESS,
+ EASYUNLOCKPRIVATE_SETREMOTEDEVICES,
+ EASYUNLOCKPRIVATE_GETREMOTEDEVICES,
// Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms.xml.
ENUM_BOUNDARY
diff --git a/google_apis/gaia/oauth2_api_call_flow.cc b/google_apis/gaia/oauth2_api_call_flow.cc
index 08312eb..07f3208 100644
--- a/google_apis/gaia/oauth2_api_call_flow.cc
+++ b/google_apis/gaia/oauth2_api_call_flow.cc
@@ -90,7 +90,8 @@ void OAuth2ApiCallFlow::EndApiCall(const net::URLFetcher* source) {
return;
}
- if (source->GetResponseCode() != net::HTTP_OK) {
+ if (source->GetResponseCode() != net::HTTP_OK &&
+ source->GetResponseCode() != net::HTTP_NO_CONTENT) {
state_ = ERROR_STATE;
ProcessApiCallFailure(source);
return;
@@ -125,6 +126,10 @@ void OAuth2ApiCallFlow::EndMintAccessToken(
}
}
+std::string OAuth2ApiCallFlow::CreateApiCallBodyContentType() {
+ return "application/x-www-form-urlencoded";
+}
+
OAuth2AccessTokenFetcher* OAuth2ApiCallFlow::CreateAccessTokenFetcher() {
return new OAuth2AccessTokenFetcherImpl(this, context_, refresh_token_);
}
@@ -166,7 +171,7 @@ URLFetcher* OAuth2ApiCallFlow::CreateURLFetcher() {
result->SetAutomaticallyRetryOnNetworkChanges(3);
if (!empty_body)
- result->SetUploadData("application/x-www-form-urlencoded", body);
+ result->SetUploadData(CreateApiCallBodyContentType(), body);
return result;
}
diff --git a/google_apis/gaia/oauth2_api_call_flow.h b/google_apis/gaia/oauth2_api_call_flow.h
index b29bc8a..70c9b51 100644
--- a/google_apis/gaia/oauth2_api_call_flow.h
+++ b/google_apis/gaia/oauth2_api_call_flow.h
@@ -66,6 +66,7 @@ class OAuth2ApiCallFlow
// Methods to help create HTTP request.
virtual GURL CreateApiCallUrl() = 0;
virtual std::string CreateApiCallBody() = 0;
+ virtual std::string CreateApiCallBodyContentType();
// Sub-classes can expose an appropriate observer interface by implementing
// these template methods.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 341772f..85fcfe1 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -40788,6 +40788,11 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="875" label="USBPRIVATE_GETDEVICEINFO"/>
<int value="876" label="EASYUNLOCKPRIVATE_UPDATESCREENLOCKSTATE"/>
<int value="877" label="CAST_CHANNEL_GETLOGS"/>
+ <int value="878" label="EASYUNLOCKPRIVATE_SETPERMITACCESS"/>
+ <int value="879" label="EASYUNLOCKPRIVATE_GETPERMITACCESS"/>
+ <int value="880" label="EASYUNLOCKPRIVATE_CLEARPERMITACCESS"/>
+ <int value="881" label="EASYUNLOCKPRIVATE_SETREMOTEDEVICES"/>
+ <int value="882" label="EASYUNLOCKPRIVATE_GETREMOTEDEVICES"/>
</enum>
<enum name="ExtensionInstallCause" type="int">