diff --git a/lib/db/api.js b/lib/db/api.js index 6480a1d2..2750dd0a 100644 --- a/lib/db/api.js +++ b/lib/db/api.js @@ -117,6 +117,65 @@ dbapi.setDeviceAbsent = function(serial) { })) } +dbapi.setDeviceAirplaneMode = function(serial, enabled) { + return db.run(r.table('devices').get(serial).update({ + airplaneMode: enabled + })) +} + +dbapi.setDeviceBattery = function(serial, battery) { + return db.run(r.table('devices').get(serial).update({ + battery: { + status: battery.status + , health: battery.health + , source: battery.source + , level: battery.level + , scale: battery.scale + , temp: battery.temp + , voltage: battery.voltage + } + })) +} + +dbapi.setDeviceBrowser = function(serial, browser) { + return db.run(r.table('devices').get(serial).update({ + browser: { + selected: browser.selected + , apps: browser.apps + } + })) +} + +dbapi.setDeviceConnectivity = function(serial, connectivity) { + return db.run(r.table('devices').get(serial).update({ + network: { + connected: connectivity.connected + , type: connectivity.type + , subtype: connectivity.subtype + , failover: !!connectivity.failover + , roaming: !!connectivity.roaming + } + })) +} + +dbapi.setDevicePhoneState = function(serial, state) { + return db.run(r.table('devices').get(serial).update({ + network: { + state: state.state + , manual: state.manual + , operator: state.operator + } + })) +} + +dbapi.setDeviceRotation = function(serial, rotation) { + return db.run(r.table('devices').get(serial).update({ + display: { + orientation: rotation + } + })) +} + dbapi.setDeviceChannel = function(serial, channel) { return db.run(r.table('devices').get(serial).update({ channel: channel diff --git a/lib/roles/app.js b/lib/roles/app.js index 8579b61a..03675a6a 100644 --- a/lib/roles/app.js +++ b/lib/roles/app.js @@ -330,6 +330,52 @@ module.exports = function(options) { .on(wire.DeviceLogcatEntryMessage, function(channel, message) { socket.emit('logcat.entry', message) }) + .on(wire.AirplaneModeEvent, function(channel, message) { + socket.emit('device.change', { + serial: message.serial + , airplaneMode: message.enabled + }) + }) + .on(wire.BatteryEvent, function(channel, message) { + var serial = message.serial + delete message.serial + socket.emit('device.change', { + serial: serial + , battery: message + }) + }) + .on(wire.BrowserPackageEvent, function(channel, message) { + var serial = message.serial + delete message.serial + socket.emit('device.change', { + serial: serial + , browser: message + }) + }) + .on(wire.ConnectivityEvent, function(channel, message) { + var serial = message.serial + delete message.serial + socket.emit('device.change', { + serial: serial + , network: message + }) + }) + .on(wire.PhoneStateEvent, function(channel, message) { + var serial = message.serial + delete message.serial + socket.emit('device.change', { + serial: serial + , network: message + }) + }) + .on(wire.RotationEvent, function(channel, message) { + socket.emit('device.change', { + serial: message.serial + , display: { + orientation: message.rotation + } + }) + }) .handler() // Global messages diff --git a/lib/roles/device/plugins/input.js b/lib/roles/device/plugins/input.js index a8a6eb3e..1945015f 100644 --- a/lib/roles/device/plugins/input.js +++ b/lib/roles/device/plugins/input.js @@ -2,6 +2,7 @@ var util = require('util') var syrup = require('syrup') var Promise = require('bluebird') +var _ = require('lodash') var wire = require('../../../wire') var wireutil = require('../../../wire/util') @@ -12,6 +13,22 @@ var logger = require('../../../util/logger') var ms = require('../../../wire/messagestream') var lifecycle = require('../../../util/lifecycle') +function MessageResolver() { + this.resolvers = Object.create(null) + + this.await = function(id, resolver) { + this.resolvers[id] = resolver + return resolver.promise + } + + this.resolve = function(id, value) { + var resolver = this.resolvers[id] + delete this.resolvers[id] + resolver.resolve(value) + return resolver.promise + } +} + module.exports = syrup.serial() .dependency(require('../support/adb')) .dependency(require('../support/router')) @@ -19,7 +36,7 @@ module.exports = syrup.serial() .dependency(require('../resources/service')) .define(function(options, adb, router, push, apk) { var log = logger.createLogger('device:plugins:input') - var serviceQueue = [] + var messageResolver = new MessageResolver() var agent = { socket: null @@ -142,35 +159,117 @@ module.exports = syrup.serial() .then(function(conn) { service.socket = conn service.reader = conn.pipe(new ms.DelimitedStream()) - service.reader.on('data', function(data) { - if (serviceQueue.length) { - var resolver = serviceQueue.shift() - resolver.resolve(data) - } - else { - log.warn('Unexpected data from service', data) - } - }) + service.reader.on('data', handleEnvelope) service.writer = new ms.DelimitingStream() service.writer.pipe(conn) lifecycle.share('InputService connection', conn) }) } + function handleEnvelope(data) { + var envelope = apk.wire.Envelope.decode(data) + if (envelope.id !== null) { + messageResolver.resolve(envelope.id, envelope.message) + } + else { + switch (envelope.type) { + case apk.wire.MessageType.EVENT_AIRPLANE_MODE: + var message = apk.wire.AirplaneModeEvent.decode(envelope.message) + push.send([ + wireutil.global + , wireutil.envelope(new wire.AirplaneModeEvent( + options.serial + , message.enabled + )) + ]) + break + case apk.wire.MessageType.EVENT_BATTERY: + var message = apk.wire.BatteryEvent.decode(envelope.message) + push.send([ + wireutil.global + , wireutil.envelope(new wire.BatteryEvent( + options.serial + , message.status + , message.health + , message.source + , message.level + , message.scale + , message.temp + , message.voltage + )) + ]) + break + case apk.wire.MessageType.EVENT_BROWSER_PACKAGE: + var message = apk.wire.BrowserPackageEvent.decode(envelope.message) + push.send([ + wireutil.global + , wireutil.envelope(new wire.BrowserPackageEvent( + options.serial + , message.selected + , message.apps.map(function(app) { + return new wire.BrowserApp( + app.name + , app.component + , app.selected + ) + }) + )) + ]) + break + case apk.wire.MessageType.EVENT_CONNECTIVITY: + var message = apk.wire.ConnectivityEvent.decode(envelope.message) + push.send([ + wireutil.global + , wireutil.envelope(new wire.ConnectivityEvent( + options.serial + , message.connected + , message.type + , message.subtype + , message.failover + , message.roaming + )) + ]) + break + case apk.wire.MessageType.EVENT_PHONE_STATE: + var message = apk.wire.PhoneStateEvent.decode(envelope.message) + push.send([ + wireutil.global + , wireutil.envelope(new wire.PhoneStateEvent( + options.serial + , message.state + , message.manual + , message.operator + )) + ]) + break + case apk.wire.MessageType.EVENT_ROTATION: + var message = apk.wire.RotationEvent.decode(envelope.message) + push.send([ + wireutil.global + , wireutil.envelope(new wire.RotationEvent( + options.serial + , message.rotation + )) + ]) + break + } + } + } + function stopService() { return callService(util.format("-a '%s'", apk.stopAction)) } function keyEvent(data) { return runAgentCommand( - apk.wire.RequestType.KEYEVENT + apk.wire.MessageType.DO_KEYEVENT , new apk.wire.KeyEventRequest(data) ) } function type(text) { return runAgentCommand( - apk.wire.RequestType.TYPE + apk.wire.MessageType.DO_TYPE , new apk.wire.TypeRequest(text) ) } @@ -179,7 +278,7 @@ module.exports = syrup.serial() return setClipboard(text) .then(function() { keyEvent({ - event: apk.wire.KeyEvent.PRESS + event: apk.wire.MessageType.DO_PRESS , keyCode: adb.Keycode.KEYCODE_V , ctrlKey: true }) @@ -188,33 +287,33 @@ module.exports = syrup.serial() function wake() { return runAgentCommand( - apk.wire.RequestType.WAKE + apk.wire.MessageType.DO_WAKE , new apk.wire.WakeRequest() ) } function freezeRotation(rotation) { return runAgentCommand( - apk.wire.RequestType.SET_ROTATION + apk.wire.MessageType.SET_ROTATION , new apk.wire.SetRotationRequest(rotation, true) ) } function thawRotation() { return runAgentCommand( - apk.wire.RequestType.SET_ROTATION + apk.wire.MessageType.SET_ROTATION , new apk.wire.SetRotationRequest(0, false) ) } function version() { return runServiceCommand( - apk.wire.RequestType.VERSION - , new apk.wire.VersionRequest() + apk.wire.MessageType.GET_VERSION + , new apk.wire.GetVersionRequest() ) .timeout(10000) .then(function(data) { - var response = apk.wire.VersionResponse.decode(data) + var response = apk.wire.GetVersionResponse.decode(data) if (response.success) { return response.version } @@ -224,7 +323,7 @@ module.exports = syrup.serial() function unlock() { return runServiceCommand( - apk.wire.RequestType.SET_KEYGUARD_STATE + apk.wire.MessageType.SET_KEYGUARD_STATE , new apk.wire.SetKeyguardStateRequest(false) ) .timeout(10000) @@ -238,7 +337,7 @@ module.exports = syrup.serial() function lock() { return runServiceCommand( - apk.wire.RequestType.SET_KEYGUARD_STATE + apk.wire.MessageType.SET_KEYGUARD_STATE , new apk.wire.SetKeyguardStateRequest(true) ) .timeout(10000) @@ -252,7 +351,7 @@ module.exports = syrup.serial() function acquireWakeLock() { return runServiceCommand( - apk.wire.RequestType.SET_WAKE_LOCK + apk.wire.MessageType.SET_WAKE_LOCK , new apk.wire.SetWakeLockRequest(true) ) .timeout(10000) @@ -266,7 +365,7 @@ module.exports = syrup.serial() function releaseWakeLock() { return runServiceCommand( - apk.wire.RequestType.SET_WAKE_LOCK + apk.wire.MessageType.SET_WAKE_LOCK , new apk.wire.SetWakeLockRequest(false) ) .timeout(10000) @@ -280,12 +379,12 @@ module.exports = syrup.serial() function identity() { return runServiceCommand( - apk.wire.RequestType.IDENTIFY - , new apk.wire.IdentifyRequest(options.serial) + apk.wire.MessageType.DO_IDENTIFY + , new apk.wire.DoIdentifyRequest(options.serial) ) .timeout(10000) .then(function(data) { - var response = apk.wire.IdentifyResponse.decode(data) + var response = apk.wire.DoIdentifyResponse.decode(data) if (!response.success) { throw new Error('Unable to identify device') } @@ -294,7 +393,7 @@ module.exports = syrup.serial() function setClipboard(text) { return runServiceCommand( - apk.wire.RequestType.SET_CLIPBOARD + apk.wire.MessageType.SET_CLIPBOARD , new apk.wire.SetClipboardRequest( apk.wire.ClipboardType.TEXT , text @@ -311,7 +410,7 @@ module.exports = syrup.serial() function getClipboard() { return runServiceCommand( - apk.wire.RequestType.GET_CLIPBOARD + apk.wire.MessageType.GET_CLIPBOARD , new apk.wire.GetClipboardRequest( apk.wire.ClipboardType.TEXT ) @@ -331,7 +430,7 @@ module.exports = syrup.serial() function getBrowsers() { return runServiceCommand( - apk.wire.RequestType.GET_BROWSERS + apk.wire.MessageType.GET_BROWSERS , new apk.wire.GetBrowsersRequest() ) .timeout(15000) @@ -347,7 +446,7 @@ module.exports = syrup.serial() function getProperties(properties) { return runServiceCommand( - apk.wire.RequestType.GET_PROPERTIES + apk.wire.MessageType.GET_PROPERTIES , new apk.wire.GetPropertiesRequest(properties) ) .timeout(15000) @@ -366,16 +465,17 @@ module.exports = syrup.serial() function runServiceCommand(type, cmd) { var resolver = Promise.defer() - service.writer.write(new apk.wire.RequestEnvelope( - type + var id = Math.floor(Math.random() * 0xFFFFFF) + service.writer.write(new apk.wire.Envelope( + id + , type , cmd.encodeNB() ).encodeNB()) - serviceQueue.push(resolver) - return resolver.promise + return messageResolver.await(id, resolver) } function runAgentCommand(type, cmd) { - agent.writer.write(new apk.wire.RequestEnvelope( + agent.writer.write(new apk.wire.Envelope( type , cmd.encodeNB() ).encodeNB()) diff --git a/lib/roles/device/resources/service.js b/lib/roles/device/resources/service.js index 3ac7b51f..fabdf633 100644 --- a/lib/roles/device/resources/service.js +++ b/lib/roles/device/resources/service.js @@ -15,13 +15,13 @@ module.exports = syrup.serial() var log = logger.createLogger('device:resources:service') var resource = { - requiredVersion: '0.5.1' + requiredVersion: '0.6.2' , pkg: 'jp.co.cyberagent.stf' , main: 'jp.co.cyberagent.stf.Agent' , apk: pathutil.vendor('STFService/STFService.apk') , wire: ProtoBuf.loadProtoFile( pathutil.vendor('STFService/wire.proto') - ).build().jp.co.cyberagent.stf + ).build().jp.co.cyberagent.stf.proto , startAction: 'jp.co.cyberagent.stf.ACTION_START' , stopAction: 'jp.co.cyberagent.stf.ACTION_STOP' } diff --git a/lib/roles/processor.js b/lib/roles/processor.js index 7db5b47e..724643c2 100644 --- a/lib/roles/processor.js +++ b/lib/roles/processor.js @@ -94,6 +94,30 @@ module.exports = function(options) { .on(wire.DeviceLogcatEntryMessage, function(channel, message, data) { appDealer.send([channel, data]) }) + .on(wire.AirplaneModeEvent, function(channel, message, data) { + dbapi.setDeviceAirplaneMode(message.serial, message.enabled) + appDealer.send([channel, data]) + }) + .on(wire.BatteryEvent, function(channel, message, data) { + dbapi.setDeviceBattery(message.serial, message) + appDealer.send([channel, data]) + }) + .on(wire.BrowserPackageEvent, function(channel, message, data) { + dbapi.setDeviceBrowser(message.serial, message) + appDealer.send([channel, data]) + }) + .on(wire.ConnectivityEvent, function(channel, message, data) { + dbapi.setDeviceConnectivity(message.serial, message) + appDealer.send([channel, data]) + }) + .on(wire.PhoneStateEvent, function(channel, message, data) { + dbapi.setDevicePhoneState(message.serial, message) + appDealer.send([channel, data]) + }) + .on(wire.RotationEvent, function(channel, message, data) { + dbapi.setDeviceRotation(message.serial, message.rotation) + appDealer.send([channel, data]) + }) .handler()) lifecycle.observe(function() { diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index 99c29568..cfdf6016 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -43,6 +43,12 @@ enum MessageType { LogcatStopMessage = 40; BrowserOpenMessage = 41; BrowserClearMessage = 42; + AirplaneModeEvent = 43; + BatteryEvent = 44; + BrowserPackageEvent = 45; + ConnectivityEvent = 46; + PhoneStateEvent = 47; + RotationEvent = 48; } message Envelope { @@ -387,3 +393,54 @@ message BrowserOpenMessage { message BrowserClearMessage { optional string browser = 1; } + +// Events, these must be kept in sync with STFService/wire.proto + +message AirplaneModeEvent { + required string serial = 1; + required bool enabled = 2; +} + +message BatteryEvent { + required string serial = 1; + required string status = 2; + required string health = 3; + required string source = 4; + required uint32 level = 5; + required uint32 scale = 6; + required double temp = 7; + required double voltage = 8; +} + +message BrowserApp { + required string name = 1; + required string component = 2; + required bool selected = 3; +} + +message BrowserPackageEvent { + required string serial = 1; + required bool selected = 2; + repeated BrowserApp apps = 3; +} + +message ConnectivityEvent { + required string serial = 1; + required bool connected = 2; + optional string type = 3; + optional string subtype = 4; + optional bool failover = 5; + optional bool roaming = 6; +} + +message PhoneStateEvent { + required string serial = 1; + required string state = 2; + required bool manual = 3; + optional string operator = 4; +} + +message RotationEvent { + required string serial = 1; + required int32 rotation = 2; +} diff --git a/res/app/components/stf/device/device-service.js b/res/app/components/stf/device/device-service.js index f7fd0511..9ea674cc 100644 --- a/res/app/components/stf/device/device-service.js +++ b/res/app/components/stf/device/device-service.js @@ -74,7 +74,7 @@ module.exports = function DeviceServiceFactory($http, socket) { } function modify(data, newData) { - _.assign(data, newData) + _.merge(data, newData) sync(data) notify() } diff --git a/vendor/STFService/STFService.apk b/vendor/STFService/STFService.apk index 98b75843..695bedfd 100644 Binary files a/vendor/STFService/STFService.apk and b/vendor/STFService/STFService.apk differ diff --git a/vendor/STFService/wire.proto b/vendor/STFService/wire.proto index e0abfd57..66bb4c28 100644 --- a/vendor/STFService/wire.proto +++ b/vendor/STFService/wire.proto @@ -1,144 +1,189 @@ -package jp.co.cyberagent.stf; +package jp.co.cyberagent.stf.proto; option java_outer_classname = "Wire"; -enum RequestType { - VERSION = 0; - SET_KEYGUARD_STATE = 1; - SET_WAKE_LOCK = 2; - SET_CLIPBOARD = 3; - GET_CLIPBOARD = 4; - GET_BROWSERS = 5; - GET_PROPERTIES = 6; - IDENTIFY = 7; - KEYEVENT = 8; - TYPE = 9; - WAKE = 10; - SET_ROTATION = 11; +enum MessageType { + DO_IDENTIFY = 1; + DO_KEYEVENT = 2; + DO_TYPE = 3; + DO_WAKE = 4; + GET_BROWSERS = 5; + GET_CLIPBOARD = 6; + GET_PROPERTIES = 7; + GET_VERSION = 8; + SET_CLIPBOARD = 9; + SET_KEYGUARD_STATE = 10; + SET_WAKE_LOCK = 11; + SET_ROTATION = 12; + EVENT_AIRPLANE_MODE = 13; + EVENT_BATTERY = 14; + EVENT_CONNECTIVITY = 15; + EVENT_PHONE_STATE = 16; + EVENT_ROTATION = 17; + EVENT_BROWSER_PACKAGE = 18; } -message RequestEnvelope { - required RequestType type = 1; - required bytes request = 2; +message Envelope { + optional uint32 id = 1; + required MessageType type = 2; + required bytes message = 3; +} + +// Events + +message AirplaneModeEvent { + required bool enabled = 1; +} + +message BatteryEvent { + required string status = 1; + required string health = 2; + required string source = 3; + required uint32 level = 4; + required uint32 scale = 5; + required double temp = 6; + required double voltage = 7; +} + +message BrowserApp { + required string name = 1; + required string component = 2; + required bool selected = 3; +} + +message BrowserPackageEvent { + required bool selected = 1; + repeated BrowserApp apps = 2; +} + +message ConnectivityEvent { + required bool connected = 1; + optional string type = 2; + optional string subtype = 3; + optional bool failover = 4; + optional bool roaming = 5; +} + +message PhoneStateEvent { + required string state = 1; + required bool manual = 2; + optional string operator = 3; +} + +message RotationEvent { + required int32 rotation = 1; } // Service -message VersionRequest { +message GetVersionRequest { } -message VersionResponse { - required bool success = 1; - optional string version = 2; +message GetVersionResponse { + required bool success = 1; + optional string version = 2; } message SetKeyguardStateRequest { - required bool enabled = 1; + required bool enabled = 1; } message SetKeyguardStateResponse { - required bool success = 1; + required bool success = 1; } message SetWakeLockRequest { - required bool enabled = 1; + required bool enabled = 1; } message SetWakeLockResponse { - required bool success = 1; + required bool success = 1; } enum ClipboardType { - TEXT = 1; + TEXT = 1; } message SetClipboardRequest { - required ClipboardType type = 1; - optional string text = 2; + required ClipboardType type = 1; + optional string text = 2; } message SetClipboardResponse { - required bool success = 1; + required bool success = 1; } message GetClipboardRequest { - required ClipboardType type = 1; + required ClipboardType type = 1; } message GetClipboardResponse { - required bool success = 1; - optional ClipboardType type = 2; - optional string text = 3; -} - -message BrowserApp { - required string name = 1; - required string component = 2; - required bool selected = 3; - required bytes icon = 4; + required bool success = 1; + optional ClipboardType type = 2; + optional string text = 3; } message GetBrowsersRequest { } message GetBrowsersResponse { - required bool success = 1; - required bool selected = 2; - repeated BrowserApp apps = 3; + required bool success = 1; + required bool selected = 2; + repeated BrowserApp apps = 3; } message Property { - required string name = 1; - required string value = 2; + required string name = 1; + required string value = 2; } message GetPropertiesRequest { - repeated string properties = 1; + repeated string properties = 1; } message GetPropertiesResponse { - required bool success = 1; - repeated Property properties = 2; + required bool success = 1; + repeated Property properties = 2; } -message IdentifyRequest { - required string serial = 1; +message DoIdentifyRequest { + required string serial = 1; } -message IdentifyResponse { - required bool success = 1; +message DoIdentifyResponse { + required bool success = 1; } // Agent enum KeyEvent { - DOWN = 0; - UP = 1; - PRESS = 2; + DOWN = 0; + UP = 1; + PRESS = 2; } message KeyEventRequest { - required KeyEvent event = 1; - required int32 keyCode = 2; - optional bool shiftKey = 3; - optional bool ctrlKey = 4; - optional bool altKey = 5; - optional bool metaKey = 6; - optional bool symKey = 7; - optional bool functionKey = 8; - optional bool capsLockKey = 9; - optional bool scrollLockKey = 10; - optional bool numLockKey = 11; + required KeyEvent event = 1; + required int32 keyCode = 2; + optional bool shiftKey = 3; + optional bool ctrlKey = 4; + optional bool altKey = 5; + optional bool metaKey = 6; + optional bool symKey = 7; + optional bool functionKey = 8; + optional bool capsLockKey = 9; + optional bool scrollLockKey = 10; + optional bool numLockKey = 11; } -message TypeRequest { - required string text = 1; +message DoTypeRequest { + required string text = 1; } message SetRotationRequest { - required int32 rotation = 1; - required bool lock = 2; + required int32 rotation = 1; + required bool lock = 2; } -message WakeRequest { +message DoWakeRequest { }