blob: 39035816a3b6e502ffc282c955680637516905e2 [file] [log] [blame]
/* Copyright 2017 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.
*/
ProximityAuth = {
cryptauthController_: null,
remoteDevicesController_: null,
findEligibleDevicesController_: null,
/**
* Initializes all UI elements of the ProximityAuth debug page.
*/
init: function() {
ProximityAuth.cryptauthController_ = new CryptAuthController();
ProximityAuth.remoteDevicesController_ = new DeviceListController(
document.getElementById('remote-devices-control'), true, false);
ProximityAuth.eligibleDevicesController_ = new EligibleDevicesController();
WebUI.getLocalState();
}
};
/**
* Controller for the CryptAuth controls section.
*/
class CryptAuthController {
constructor() {
this.elements_ = {
localDeviceId: document.getElementById('local-device-id'),
gcmRegistration: document.getElementById('gcm-registration'),
currentEid: document.getElementById('current-eid'),
enrollmentTitle: document.getElementById('enrollment-title'),
lastEnrollment: document.getElementById('last-enrollment'),
nextEnrollment: document.getElementById('next-enrollment'),
enrollmentButton: document.getElementById('force-enrollment'),
deviceSyncTitle: document.getElementById('device-sync-title'),
lastDeviceSync: document.getElementById('last-device-sync'),
nextDeviceSync: document.getElementById('next-device-sync'),
deviceSyncButton: document.getElementById('force-device-sync'),
newUserNotifButton: document.getElementById('show-new-user-notif'),
existingUserNewHostNotifButton:
document.getElementById('show-existing-user-new-host-notif'),
existingUserNewChromebookNotifButton:
document.getElementById('show-existing-user-new-chromebook-notif'),
};
this.elements_.enrollmentButton.onclick = this.forceEnrollment_.bind(this);
this.elements_.deviceSyncButton.onclick = this.forceDeviceSync_.bind(this);
this.elements_.newUserNotifButton.onclick =
this.showNewUserNotification_.bind(this);
this.elements_.existingUserNewHostNotifButton.onclick =
this.showExistingUserNewHostNotification_.bind(this);
this.elements_.existingUserNewChromebookNotifButton.onclick =
this.showExistingUserNewChromebookNotification_.bind(this);
this.multiDeviceSetup =
new chromeos.multideviceSetup.mojom.MultiDeviceSetupPtr();
Mojo.bindInterface(
chromeos.multideviceSetup.mojom.MultiDeviceSetup.name,
mojo.makeRequest(this.multiDeviceSetup).handle);
}
/**
* Sets the local device's ID. Note that this value is truncated since the
* full value is very long and does not cleanly fit on the screen.
*/
setLocalDeviceId(deviceIdTruncated) {
this.elements_.localDeviceId.textContent = deviceIdTruncated;
}
/**
* Update the enrollment state in the UI.
*/
updateEnrollmentState(state) {
this.elements_.lastEnrollment.textContent =
this.getLastSyncTimeString_(state, 'Never enrolled');
this.elements_.nextEnrollment.textContent =
this.getNextRefreshString_(state);
if (state['recoveringFromFailure']) {
this.elements_.enrollmentTitle.setAttribute('state', 'failure');
} else if (state['operationInProgress']) {
this.elements_.enrollmentTitle.setAttribute('state', 'in-progress');
} else {
this.elements_.enrollmentTitle.setAttribute('state', 'synced');
}
}
/**
* Updates the device sync state in the UI.
*/
updateDeviceSyncState(state) {
this.elements_.lastDeviceSync.textContent =
this.getLastSyncTimeString_(state, 'Never synced');
this.elements_.nextDeviceSync.textContent =
this.getNextRefreshString_(state);
if (state['recoveringFromFailure']) {
this.elements_.deviceSyncTitle.setAttribute('state', 'failure');
} else if (state['operationInProgress']) {
this.elements_.deviceSyncTitle.setAttribute('state', 'in-progress');
} else {
this.elements_.deviceSyncTitle.setAttribute('state', 'synced');
}
}
/**
* Returns the formatted string of the time of the last sync to be displayed.
*/
getLastSyncTimeString_(syncState, neverSyncedString) {
if (syncState.lastSuccessTime == 0)
return neverSyncedString;
var date = new Date(syncState.lastSuccessTime);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
}
/**
* Returns the formatted string of the next time to refresh to be displayed.
*/
getNextRefreshString_(syncState) {
var deltaMillis = syncState.nextRefreshTime;
if (deltaMillis == null)
return 'unknown';
if (deltaMillis == 0)
return 'sync in progress...';
var seconds = deltaMillis / 1000;
if (seconds < 60)
return Math.round(seconds) + ' seconds to refresh';
var minutes = seconds / 60;
if (minutes < 60)
return Math.round(minutes) + ' minutes to refresh';
var hours = minutes / 60;
if (hours < 24)
return Math.round(hours) + ' hours to refresh';
var days = hours / 24;
return Math.round(days) + ' days to refresh';
}
/**
* Forces a CryptAuth enrollment on button click.
*/
forceEnrollment_() {
WebUI.forceEnrollment();
}
/**
* Forces a device sync on button click.
*/
forceDeviceSync_() {
WebUI.forceDeviceSync();
}
/**
* Shows the "new user, potential host exists" notification.
*/
showNewUserNotification_() {
this.showMultiDeviceSetupPromoNotification_(
chromeos.multideviceSetup.mojom.EventTypeForDebugging.
kNewUserPotentialHostExists);
}
/**
* Shows the "existing user, new host" notification.
*/
showExistingUserNewHostNotification_() {
this.showMultiDeviceSetupPromoNotification_(
chromeos.multideviceSetup.mojom.EventTypeForDebugging.
kExistingUserConnectedHostSwitched);
}
/**
* Shows the "existing user, new Chromebook" notification.
*/
showExistingUserNewChromebookNotification_() {
this.showMultiDeviceSetupPromoNotification_(
chromeos.multideviceSetup.mojom.EventTypeForDebugging.
kExistingUserNewChromebookAdded);
}
/**
* Shows a "MultiDevice Setup" notification of the given type.
*/
showMultiDeviceSetupPromoNotification_(type) {
this.multiDeviceSetup.triggerEventForDebugging(type).then(
function(responseParams) {
if (responseParams.success) {
console.log('Successfully triggered notification for type ' +
type + '.');
} else {
console.error('Failed to trigger notification for type ' + type +
'; no NotificationPresenter has been registered.');
}
}).catch(function(error) {
console.error('Failed to trigger notification type. ' + error);
});
}
};
/**
* Controller for a list of remote devices. These lists are displayed in a
* number of locations on the debug page.
*/
class DeviceListController {
constructor(rootElement, showScanButton, showToggleUnlockKeyButton) {
this.rootElement_ = rootElement;
this.showScanButton_ = showScanButton;
this.showToggleUnlockKeyButton_ = showToggleUnlockKeyButton;
this.remoteDeviceTemplate_ =
document.getElementById('remote-device-template');
}
/**
* Updates the UI with the given remote devices.
*/
updateRemoteDevices(remoteDevices) {
var existingItems =
this.rootElement_.querySelectorAll('.remote-device');
for (var i = 0; i < existingItems.length; ++i) {
existingItems[i].remove();
}
for (var i = 0; i < remoteDevices.length; ++i) {
this.rootElement_.appendChild(
this.createRemoteDeviceItem_(remoteDevices[i]));
}
this.rootElement_.setAttribute('device-count', remoteDevices.length);
}
/**
* Creates a DOM element for a given remote device.
*/
createRemoteDeviceItem_(remoteDevice) {
var isUnlockKey = !!remoteDevice['unlockKey'];
var hasMobileHotspot = !!remoteDevice['hasMobileHotspot'];
var isArcPlusPlusEnrollment = !!remoteDevice['isArcPlusPlusEnrollment'];
var isPixelPhone = !!remoteDevice['isPixelPhone'];
var t = this.remoteDeviceTemplate_.content;
t.querySelector('.device-connection-status').setAttribute(
'state', remoteDevice['connectionStatus']);
t.querySelector('.device-name').textContent =
remoteDevice['friendlyDeviceName'];
t.querySelector('.device-id').textContent =
remoteDevice['publicKeyTruncated'];
t.querySelector('.software-features').textContent =
remoteDevice['featureStates'];
t.querySelector('.is-unlock-key').textContent = isUnlockKey;
t.querySelector('.supports-mobile-hotspot').textContent = hasMobileHotspot;
t.querySelector('.is-arc-plus-plus-enrollment').textContent =
isArcPlusPlusEnrollment;
t.querySelector('.is-pixel-phone').textContent = isPixelPhone;
if (!!remoteDevice['bluetoothAddress']) {
t.querySelector('.bluetooth-address-row').classList.remove('hidden');
t.querySelector('.bluetooth-address').textContent =
remoteDevice['bluetoothAddress'];
}
var scanButton = t.querySelector('.device-scan');
scanButton.classList.toggle(
'hidden', !this.showScanButton_ || !isUnlockKey);
scanButton.textContent =
remoteDevice['connectionStatus'] == 'disconnected'
? 'EasyUnlock Scan' : 'EasyUnlock Disconnect';
t.querySelector('.device-toggle-key').classList.toggle(
'hidden', !this.showToggleUnlockKeyButton_ || !isUnlockKey);
var element = document.importNode(this.remoteDeviceTemplate_.content, true);
// Initialize buttons on new element.
element.querySelector('.device-scan').onclick =
this.scanForDevice_.bind(this, remoteDevice['publicKey']);
element.querySelector('.device-toggle-key').onclick =
this.toggleUnlockKey_.bind(
this, remoteDevice['publicKey'], !remoteDevice['unlockKey']);
return element;
}
/**
* Button handler to start scanning and connecting to a device.
*/
scanForDevice_(publicKey) {
WebUI.toggleConnection(publicKey);
}
/**
* Button handler to toggle a device as an unlock key.
*/
toggleUnlockKey_(publicKey, makeUnlockKey) {
console.log(publicKey);
WebUI.toggleUnlockKey(publicKey, makeUnlockKey);
}
}
/**
* Controller for the 'Eligible Unlock Keys' controls.
*/
class EligibleDevicesController {
constructor() {
this.eligibleDeviceList_ = new DeviceListController(
document.getElementById('eligible-devices-list'), false, true);
this.ineligibleDeviceList_ = new DeviceListController(
document.getElementById('ineligible-devices-list'), false, false);
this.button_ = document.getElementById('find-eligible-devices');
this.button_.onclick = this.findEligibleUnlockDevices_.bind(this);
}
/**
* Updates the UI with the fetched eligible and ineligible devices.
*/
updateEligibleDevices(eligibleDevices, ineligibleDevices) {
this.eligibleDeviceList_.updateRemoteDevices(eligibleDevices);
this.ineligibleDeviceList_.updateRemoteDevices(ineligibleDevices);
}
/**
* Button handler to fetch eligible unlock devices.
*/
findEligibleUnlockDevices_() {
WebUI.findEligibleUnlockDevices();
}
}
/**
* Interface for the native WebUI to call into our JS.
*/
LocalStateInterface = {
onGotLocalState: function(
localDeviceId, enrollmentState, deviceSyncState, remoteDevices) {
LocalStateInterface.setLocalDeviceId(localDeviceId);
LocalStateInterface.onEnrollmentStateChanged(enrollmentState);
LocalStateInterface.onDeviceSyncStateChanged(deviceSyncState);
LocalStateInterface.onRemoteDevicesChanged(remoteDevices);
},
setLocalDeviceId: function(localDeviceId) {
ProximityAuth.cryptauthController_.setLocalDeviceId(localDeviceId);
},
onEnrollmentStateChanged: function(enrollmentState) {
ProximityAuth.cryptauthController_.updateEnrollmentState(enrollmentState);
},
onDeviceSyncStateChanged: function(deviceSyncState) {
ProximityAuth.cryptauthController_.updateDeviceSyncState(deviceSyncState);
},
onRemoteDevicesChanged: function(remoteDevices) {
ProximityAuth.remoteDevicesController_.updateRemoteDevices(remoteDevices);
}
};
/**
* Interface for the native WebUI to call into our JS.
*/
CryptAuthInterface = {
onGotEligibleDevices: function(eligibleDevices, ineligibleDevices) {
ProximityAuth.eligibleDevicesController_.updateEligibleDevices(
eligibleDevices, ineligibleDevices);
}
};
document.addEventListener('DOMContentLoaded', function() {
WebUI.onWebContentsInitialized();
Logs.init();
ProximityAuth.init();
});