android-emulator-webrtc 1.0.18 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -204
- package/dist/components/emulator/emulator.d.ts +64 -0
- package/dist/components/emulator/emulator.js +161 -173
- package/dist/components/emulator/net/emulator_status.d.ts +45 -0
- package/dist/components/emulator/net/emulator_status.js +35 -42
- package/dist/components/emulator/net/logcat.js +37 -54
- package/dist/components/emulator/net/logger.d.ts +7 -0
- package/dist/components/emulator/net/logger.js +20 -0
- package/dist/components/emulator/net/ws_jsep_protocol_driver.d.ts +213 -0
- package/dist/components/emulator/net/ws_jsep_protocol_driver.js +640 -0
- package/dist/components/emulator/views/event_handler.d.ts +37 -0
- package/dist/components/emulator/views/event_handler.js +274 -260
- package/dist/components/emulator/views/webrtc_view.d.ts +27 -0
- package/dist/components/emulator/views/webrtc_view.js +42 -20
- package/dist/index.d.ts +8 -0
- package/dist/index.js +0 -7
- package/dist/proto/emulator_controller_pb.d.ts +1 -0
- package/dist/proto/emulator_controller_pb.js +146 -287
- package/dist/src/components/emulator/emulator.d.ts +64 -0
- package/dist/src/components/emulator/net/emulator_status.d.ts +45 -0
- package/dist/src/components/emulator/net/logger.d.ts +7 -0
- package/dist/src/components/emulator/net/ws_jsep_protocol_driver.d.ts +213 -0
- package/dist/src/components/emulator/views/event_handler.d.ts +37 -0
- package/dist/src/components/emulator/views/webrtc_view.d.ts +27 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/proto/emulator_controller_pb.d.ts +1 -0
- package/package.json +17 -17
- package/proto/emulator_controller.proto +746 -181
- package/.vscode/launch.json +0 -21
- package/.vscode/settings.json +0 -3
- package/CONTRIBUTING.md +0 -28
- package/Makefile +0 -123
- package/android-emulator-webrtc.code-workspace +0 -7
- package/api_descriptor.pb +0 -0
- package/babel.config.js +0 -16
- package/cloudbuild.yaml +0 -41
- package/cloudbuilders/.gcloudignore +0 -1
- package/cloudbuilders/Dockerfile +0 -20
- package/cloudbuilders/README.md +0 -29
- package/cloudbuilders/cloudbuild.yaml +0 -19
- package/dist/components/emulator/net/jsep_protocol_driver.js +0 -365
- package/dist/components/emulator/views/simple_png_view.js +0 -110
- package/dist/proto/emulator_controller_grpc_web_pb.js +0 -1601
- package/dist/proto/emulator_web_client.js +0 -176
- package/dist/proto/rtc_service_grpc_web_pb.js +0 -231
- package/dist/proto/rtc_service_pb.js +0 -338
- package/emulator/index.js +0 -1
- package/eslint_prefix.py +0 -37
- package/gen_md_doc.js +0 -40
- package/npmrc.enc +0 -0
- package/proto/rtc_service.proto +0 -117
- package/protoc-plugin/Makefile +0 -31
- package/protoc-plugin/grpc_generator.cc +0 -1755
- package/src/components/emulator/emulator.js +0 -217
- package/src/components/emulator/net/emulator_status.js +0 -94
- package/src/components/emulator/net/jsep_protocol_driver.js +0 -364
- package/src/components/emulator/net/logcat.js +0 -155
- package/src/components/emulator/views/event_handler.js +0 -268
- package/src/components/emulator/views/simple_png_view.js +0 -105
- package/src/components/emulator/views/webrtc_view.js +0 -142
- package/src/index.js +0 -20
- package/src/proto/emulator_web_client.js +0 -140
- package/test/cloudbuild.yaml +0 -41
- package/test/emulator.test.js +0 -125
- package/test/event_handler.test.js +0 -91
- package/test/fake_events.js +0 -50
- package/test/jsep_protocol_driver.test.js +0 -228
- package/test/simple_png_view.test.js +0 -145
- package/test/touch_event_handler.test.js +0 -118
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2019 The Android Open Source Project
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License")
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import PropTypes from "prop-types";
|
|
17
|
-
import React, { Component } from "react";
|
|
18
|
-
import EmulatorPngView from "./views/simple_png_view.js";
|
|
19
|
-
import EmulatorWebrtcView from "./views/webrtc_view.js";
|
|
20
|
-
import withMouseKeyHandler from "./views/event_handler";
|
|
21
|
-
import JsepProtocol from "./net/jsep_protocol_driver.js";
|
|
22
|
-
import * as Proto from "../../proto/emulator_controller_pb";
|
|
23
|
-
import {
|
|
24
|
-
RtcService,
|
|
25
|
-
EmulatorControllerService,
|
|
26
|
-
} from "../../proto/emulator_web_client";
|
|
27
|
-
|
|
28
|
-
const PngView = withMouseKeyHandler(EmulatorPngView);
|
|
29
|
-
const RtcView = withMouseKeyHandler(EmulatorWebrtcView);
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* A React component that displays a remote android emulator.
|
|
33
|
-
*
|
|
34
|
-
* The emulator will mount a png or webrtc view component to display the current state
|
|
35
|
-
* of the emulator. It will translate mouse events on this component and send them
|
|
36
|
-
* to the actual emulator.
|
|
37
|
-
*
|
|
38
|
-
* #### Authentication Service
|
|
39
|
-
*
|
|
40
|
-
* The authentication service should implement the following methods:
|
|
41
|
-
*
|
|
42
|
-
* - `authHeader()` which must return a set of headers that should be send along with a request.
|
|
43
|
-
* - `unauthorized()` a function that gets called when a 401 was received.
|
|
44
|
-
*
|
|
45
|
-
* #### Type of view
|
|
46
|
-
*
|
|
47
|
-
* You usually want this to be webrtc as this will make use of the efficient
|
|
48
|
-
* webrtc implementation. The png view will request screenshots, which are
|
|
49
|
-
* very slow, and require the envoy proxy. You should not use this for remote emulators.
|
|
50
|
-
*
|
|
51
|
-
* Note that chrome will not autoplay the video if it is not muted and no interaction
|
|
52
|
-
* with the page has taken place. See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes.
|
|
53
|
-
*
|
|
54
|
-
* #### Pressing hardware buttons
|
|
55
|
-
*
|
|
56
|
-
* This component has a method `sendKey` that sends a key to the emulator.
|
|
57
|
-
* You can use this to send physical button events to the emulator for example:
|
|
58
|
-
*
|
|
59
|
-
* "AudioVolumeDown" - Decreases the audio volume.
|
|
60
|
-
* "AudioVolumeUp" - Increases the audio volume.
|
|
61
|
-
* "Power" - The Power button or key, turn off the device.
|
|
62
|
-
* "AppSwitch" - Should bring up the application switcher dialog.
|
|
63
|
-
* "GoHome" - Go to the home screen.
|
|
64
|
-
* "GoBack" - Open the previous screen you were looking at.
|
|
65
|
-
*
|
|
66
|
-
*/
|
|
67
|
-
class Emulator extends Component {
|
|
68
|
-
static propTypes = {
|
|
69
|
-
/** gRPC Endpoint where we can reach the emulator. */
|
|
70
|
-
uri: PropTypes.string.isRequired,
|
|
71
|
-
/** The authentication service to use, or null for no authentication. */
|
|
72
|
-
auth: PropTypes.object,
|
|
73
|
-
/** True if the audio should be disabled. This is only relevant when using the webrtc engine. */
|
|
74
|
-
muted: PropTypes.bool,
|
|
75
|
-
/** Volume between [0, 1] when audio is enabled. 0 is muted, 1.0 is 100% */
|
|
76
|
-
volume: PropTypes.number,
|
|
77
|
-
/** Called upon state change, one of ["connecting", "connected", "disconnected"] */
|
|
78
|
-
onStateChange: PropTypes.func,
|
|
79
|
-
/** Called when the audio becomes (un)available. True if audio is available, false otherwise. */
|
|
80
|
-
onAudioStateChange: PropTypes.func,
|
|
81
|
-
/** The width of the component */
|
|
82
|
-
width: PropTypes.number,
|
|
83
|
-
/** The height of the component */
|
|
84
|
-
height: PropTypes.number,
|
|
85
|
-
/** The underlying view used to display the emulator, one of ["webrtc", "png"] */
|
|
86
|
-
view: PropTypes.oneOf(["webrtc", "png"]).isRequired,
|
|
87
|
-
/** A [GeolocationCoordinates](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates) like object indicating where the device is. */
|
|
88
|
-
gps: PropTypes.object,
|
|
89
|
-
/** True if polling should be used, only set this to true if you are using the go webgrpc proxy. */
|
|
90
|
-
poll: PropTypes.bool,
|
|
91
|
-
/** Callback that will be invoked in case of gRPC errors. */
|
|
92
|
-
onError: PropTypes.func,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
static defaultProps = {
|
|
96
|
-
view: "webrtc",
|
|
97
|
-
auth: null,
|
|
98
|
-
poll: false,
|
|
99
|
-
muted: true,
|
|
100
|
-
volume: 1.0,
|
|
101
|
-
onError: (e) => {
|
|
102
|
-
console.error(e);
|
|
103
|
-
},
|
|
104
|
-
onAudioStateChange: (s) => {
|
|
105
|
-
console.debug("emulator audio: " + s);
|
|
106
|
-
},
|
|
107
|
-
onStateChange: (s) => {
|
|
108
|
-
console.debug("emulator state: " + s);
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
components = {
|
|
113
|
-
webrtc: RtcView,
|
|
114
|
-
png: PngView,
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
state = {
|
|
118
|
-
audio: false,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
constructor(props) {
|
|
122
|
-
super(props);
|
|
123
|
-
const { uri, auth, poll, onError } = props;
|
|
124
|
-
this.emulator = new EmulatorControllerService(uri, auth, onError);
|
|
125
|
-
this.rtc = new RtcService(uri, auth, onError);
|
|
126
|
-
this.jsep = new JsepProtocol(this.emulator, this.rtc, poll);
|
|
127
|
-
this.view = React.createRef();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
static getDerivedStateFromProps(nextProps, prevState) {
|
|
131
|
-
if (nextProps.view === "png")
|
|
132
|
-
return {
|
|
133
|
-
audio: false,
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
return prevState;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
componentDidMount = () => {
|
|
140
|
-
this.updateLocation();
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
componentDidUpdate = (prevProps) => {
|
|
144
|
-
if (prevProps.gps !== this.props.gps) {
|
|
145
|
-
this.updateLocation();
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
updateLocation = () => {
|
|
150
|
-
const { gps } = this.props;
|
|
151
|
-
if (typeof gps === "undefined") {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const state = new Proto.GpsState();
|
|
156
|
-
state.setLatitude(gps.latitude);
|
|
157
|
-
state.setLongitude(gps.longitude);
|
|
158
|
-
state.setAltitude(gps.altitude);
|
|
159
|
-
state.setBearing(gps.heading);
|
|
160
|
-
state.setSpeed(gps.speed);
|
|
161
|
-
this.emulator.setGps(state);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Sends the given key to the emulator.
|
|
166
|
-
*
|
|
167
|
-
* You can use this to send physical hardware events to the emulator for example:
|
|
168
|
-
*
|
|
169
|
-
* "AudioVolumeDown" - Decreases the audio volume.
|
|
170
|
-
* "AudioVolumeUp" - Increases the audio volume.
|
|
171
|
-
* "Power" - The Power button or key, turn off the device.
|
|
172
|
-
* "AppSwitch" - Should bring up the application switcher dialog.
|
|
173
|
-
* "GoHome" - Go to the home screen.
|
|
174
|
-
* "GoBack" - Open the previous screen you were looking at.
|
|
175
|
-
*
|
|
176
|
-
* See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values for
|
|
177
|
-
* a list of valid values you can send as well.
|
|
178
|
-
*/
|
|
179
|
-
sendKey = (key) => {
|
|
180
|
-
var request = new Proto.KeyboardEvent();
|
|
181
|
-
request.setEventtype(Proto.KeyboardEvent.KeyEventType.KEYPRESS);
|
|
182
|
-
request.setKey(key);
|
|
183
|
-
this.jsep.send("keyboard", request);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
_onAudioStateChange = (s) => {
|
|
187
|
-
const { onAudioStateChange } = this.props;
|
|
188
|
-
this.setState({ audio: s }, () => {
|
|
189
|
-
onAudioStateChange(s);
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
render() {
|
|
194
|
-
const { width, height, view, poll, muted, onStateChange, onError, volume } =
|
|
195
|
-
this.props;
|
|
196
|
-
const SpecificView = this.components[view] || RtcView;
|
|
197
|
-
|
|
198
|
-
console.log(`render ${width}x${height}`);
|
|
199
|
-
return (
|
|
200
|
-
<SpecificView
|
|
201
|
-
ref={this.view}
|
|
202
|
-
width={width}
|
|
203
|
-
height={height}
|
|
204
|
-
emulator={this.emulator}
|
|
205
|
-
jsep={this.jsep}
|
|
206
|
-
onStateChange={onStateChange}
|
|
207
|
-
poll={poll}
|
|
208
|
-
muted={muted}
|
|
209
|
-
volume={volume}
|
|
210
|
-
onError={onError}
|
|
211
|
-
onAudioStateChange={this._onAudioStateChange}
|
|
212
|
-
/>
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export default Emulator;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2020 The Android Open Source Project
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License")
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
|
|
17
|
-
import { EmulatorControllerService } from "../../../proto/emulator_web_client";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Gets the status of the emulator, parsing the hardware config into something
|
|
21
|
-
* easy to digest.
|
|
22
|
-
*
|
|
23
|
-
* @export
|
|
24
|
-
* @class EmulatorStatus
|
|
25
|
-
*/
|
|
26
|
-
class EmulatorStatus {
|
|
27
|
-
/**
|
|
28
|
-
* Creates an EmulatorStatus object that can retrieve the status of the running emulator.
|
|
29
|
-
*
|
|
30
|
-
* @param {object} uriOrEmulator An emulator controller service, or a URI to a gRPC endpoint.
|
|
31
|
-
* @param {object} auth The authentication service to use, or null for no authentication.
|
|
32
|
-
*
|
|
33
|
-
* The authentication service should implement the following methods:
|
|
34
|
-
* - `authHeader()` which must return a set of headers that should be send along with a request.
|
|
35
|
-
* - `unauthorized()` a function that gets called when a 401 was received.
|
|
36
|
-
*/
|
|
37
|
-
constructor(uriOrEmulator, auth) {
|
|
38
|
-
if (uriOrEmulator instanceof EmulatorControllerService) {
|
|
39
|
-
this.emulator = uriOrEmulator;
|
|
40
|
-
} else {
|
|
41
|
-
this.emulator = new EmulatorControllerService(uriOrEmulator, auth);
|
|
42
|
-
}
|
|
43
|
-
this.status = null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Gets the cached status.
|
|
48
|
-
*
|
|
49
|
-
* @memberof EmulatorStatus
|
|
50
|
-
*/
|
|
51
|
-
getStatus = () => {
|
|
52
|
-
return this.status;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Retrieves the current status from the emulator.
|
|
57
|
-
*
|
|
58
|
-
* @param {Callback} fnNotify when the status is available, returns the retrieved status.
|
|
59
|
-
* @param {boolean} cache True if the cache can be used.
|
|
60
|
-
* @memberof EmulatorStatus
|
|
61
|
-
*/
|
|
62
|
-
updateStatus = (fnNotify, cache) => {
|
|
63
|
-
const request = new Empty();
|
|
64
|
-
if (cache && this.status) {
|
|
65
|
-
fnNotify(this.status);
|
|
66
|
-
return this.status;
|
|
67
|
-
}
|
|
68
|
-
this.emulator.getStatus(request, {}, (err, response) => {
|
|
69
|
-
var hwConfig = {};
|
|
70
|
-
const entryList = response.getHardwareconfig().getEntryList();
|
|
71
|
-
for (var i = 0; i < entryList.length; i++) {
|
|
72
|
-
const key = entryList[i].getKey();
|
|
73
|
-
const val = entryList[i].getValue();
|
|
74
|
-
hwConfig[key] = val;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const vmConfig = response.getVmconfig();
|
|
78
|
-
this.status = {
|
|
79
|
-
version: response.getVersion(),
|
|
80
|
-
uptime: response.getUptime(),
|
|
81
|
-
booted: response.getBooted(),
|
|
82
|
-
hardwareConfig: hwConfig,
|
|
83
|
-
vmConfig: {
|
|
84
|
-
hypervisorType: vmConfig.getHypervisortype(),
|
|
85
|
-
numberOfCpuCores: vmConfig.getNumberofcpucores(),
|
|
86
|
-
ramSizeBytes: vmConfig.getRamsizebytes()
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
fnNotify(this.status);
|
|
90
|
-
});
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export default EmulatorStatus;
|
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2019 The Android Open Source Project
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License")
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import { EventEmitter } from "events";
|
|
17
|
-
import { Empty } from "google-protobuf/google/protobuf/empty_pb";
|
|
18
|
-
/**
|
|
19
|
-
* This drives the jsep protocol with the emulator, and can be used to
|
|
20
|
-
* send key/mouse/touch events to the emulator. Events will be send
|
|
21
|
-
* over the data channel if open, otherwise they will be send via the
|
|
22
|
-
* grpc endpoint.
|
|
23
|
-
*
|
|
24
|
-
* The jsep protocol is described here:
|
|
25
|
-
* https://rtcweb-wg.github.io/jsep/.
|
|
26
|
-
*
|
|
27
|
-
* This class can fire two events:
|
|
28
|
-
*
|
|
29
|
-
* - `connected` when the stream has become available.
|
|
30
|
-
* - `disconnected` when the stream broke down, or when we failed to establish a connection
|
|
31
|
-
*
|
|
32
|
-
* You usually want to start the stream after instantiating this object. Do not forget to
|
|
33
|
-
* disconnect once you are finished to terminate the message pump.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* jsep = new JsepProtocolDriver(emulator, s => { video.srcObject = s; video.play() });
|
|
38
|
-
* jsep.startStream();
|
|
39
|
-
*
|
|
40
|
-
* @export
|
|
41
|
-
* @class JsepProtocol
|
|
42
|
-
*/
|
|
43
|
-
export default class JsepProtocol {
|
|
44
|
-
/**
|
|
45
|
-
* Creates an instance of JsepProtocol.
|
|
46
|
-
* @param {EmulatorService} emulator Service used to make the gRPC calls
|
|
47
|
-
* @param {RtcService} rtc Service used to open up the rtc calls.
|
|
48
|
-
* @param {boolean} poll True if we should use polling
|
|
49
|
-
* @param {callback} onConnect optional callback that is invoked when a stream is available
|
|
50
|
-
* @param {callback} onDisconnect optional callback that is invoked when the stream is closed.
|
|
51
|
-
* @memberof JsepProtocol
|
|
52
|
-
*/
|
|
53
|
-
constructor(emulator, rtc, poll, onConnect, onDisconnect) {
|
|
54
|
-
this.emulator = emulator;
|
|
55
|
-
this.rtc = rtc;
|
|
56
|
-
this.events = new EventEmitter();
|
|
57
|
-
|
|
58
|
-
// Workaround for older emulators that send messages out of order
|
|
59
|
-
// and do not handle all answers properly.
|
|
60
|
-
this.old_emu_patch = {
|
|
61
|
-
candidates: [],
|
|
62
|
-
sdp: null,
|
|
63
|
-
haveOffer: false,
|
|
64
|
-
answer: false,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
this.poll = poll;
|
|
68
|
-
this.guid = null;
|
|
69
|
-
this.stream = null;
|
|
70
|
-
this.event_forwarders = {};
|
|
71
|
-
if (onConnect) this.events.on("connected", onConnect);
|
|
72
|
-
if (onDisconnect) this.events.on("disconnected", onDisconnect);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
on = (name, fn) => {
|
|
76
|
-
this.events.on(name, fn);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Disconnects the stream. This will stop the message pump as well.
|
|
81
|
-
*
|
|
82
|
-
* @memberof JsepProtocol
|
|
83
|
-
*/
|
|
84
|
-
disconnect = () => {
|
|
85
|
-
this.connected = false;
|
|
86
|
-
if (this.peerConnection) this.peerConnection.close();
|
|
87
|
-
if (this.stream) {
|
|
88
|
-
this.stream.cancel();
|
|
89
|
-
this.stream = null;
|
|
90
|
-
}
|
|
91
|
-
this.active = false;
|
|
92
|
-
this.old_emu_patch = {
|
|
93
|
-
candidates: [],
|
|
94
|
-
sdp: null,
|
|
95
|
-
haveOffer: false,
|
|
96
|
-
answer: false,
|
|
97
|
-
};
|
|
98
|
-
this.events.emit("disconnected", this);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Initiates the JSEP protocol.
|
|
103
|
-
*
|
|
104
|
-
* @memberof JsepProtocol
|
|
105
|
-
*/
|
|
106
|
-
startStream = () => {
|
|
107
|
-
const self = this;
|
|
108
|
-
this.connected = false;
|
|
109
|
-
this.peerConnection = null;
|
|
110
|
-
this.active = true;
|
|
111
|
-
this.old_emu_patch = {
|
|
112
|
-
candidates: [],
|
|
113
|
-
sdp: null,
|
|
114
|
-
haveOffer: false,
|
|
115
|
-
answer: false,
|
|
116
|
-
};
|
|
117
|
-
var request = new Empty();
|
|
118
|
-
this.rtc.requestRtcStream(request, {}, (err, response) => {
|
|
119
|
-
if (err) {
|
|
120
|
-
console.error("Failed to configure rtc stream: " + JSON.stringify(err));
|
|
121
|
-
this.disconnect();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Configure
|
|
126
|
-
self.guid = response;
|
|
127
|
-
self.connected = true;
|
|
128
|
-
|
|
129
|
-
if (!this.poll) {
|
|
130
|
-
// Streaming envoy based.
|
|
131
|
-
self._streamJsepMessage();
|
|
132
|
-
} else {
|
|
133
|
-
// Poll pump messages, go/envoy based proxy.
|
|
134
|
-
console.debug("Polling jsep messages.");
|
|
135
|
-
self._receiveJsepMessage();
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
cleanup = () => {
|
|
141
|
-
this.disconnect();
|
|
142
|
-
if (this.peerConnection) {
|
|
143
|
-
this.peerConnection.removeEventListener(
|
|
144
|
-
"track",
|
|
145
|
-
this._handlePeerConnectionTrack
|
|
146
|
-
);
|
|
147
|
-
this.peerConnection.removeEventListener(
|
|
148
|
-
"icecandidate",
|
|
149
|
-
this._handlePeerIceCandidate
|
|
150
|
-
);
|
|
151
|
-
this.peerConnection = null;
|
|
152
|
-
}
|
|
153
|
-
this.event_forwarders = {};
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
_handlePeerConnectionTrack = (e) => {
|
|
157
|
-
this.events.emit("connected", e.track);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
_handlePeerConnectionStateChange = (e) => {
|
|
161
|
-
switch (this.peerConnection.connectionState) {
|
|
162
|
-
case "disconnected":
|
|
163
|
-
// At least one of the ICE transports for the connection is in the "disconnected" state
|
|
164
|
-
// and none of the other transports are in the state "failed", "connecting",
|
|
165
|
-
// or "checking".
|
|
166
|
-
case "failed":
|
|
167
|
-
// One or more of the ICE transports on the connection is in the "failed" state.
|
|
168
|
-
case "closed":
|
|
169
|
-
//The RTCPeerConnection is closed.
|
|
170
|
-
this.disconnect();
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
send(label, msg) {
|
|
175
|
-
let bytes = msg.serializeBinary();
|
|
176
|
-
let forwarder = this.event_forwarders[label];
|
|
177
|
-
console.log("Send " + label + " " + JSON.stringify(msg.toObject()));
|
|
178
|
-
// Send via data channel/gRPC bridge.
|
|
179
|
-
if (this.connected && forwarder && forwarder.readyState == "open") {
|
|
180
|
-
this.event_forwarders[label].send(bytes);
|
|
181
|
-
} else {
|
|
182
|
-
// Fallback to using the gRPC protocol
|
|
183
|
-
switch (label) {
|
|
184
|
-
case "mouse":
|
|
185
|
-
this.emulator.sendMouse(msg);
|
|
186
|
-
break;
|
|
187
|
-
case "keyboard":
|
|
188
|
-
this.emulator.sendKey(msg);
|
|
189
|
-
break;
|
|
190
|
-
case "touch":
|
|
191
|
-
this.emulator.sendTouch(msg);
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
_handlePeerIceCandidate = (e) => {
|
|
198
|
-
if (e.candidate === null) return;
|
|
199
|
-
this._sendJsep({ candidate: e.candidate });
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
_handleDataChannel = (e) => {
|
|
203
|
-
let channel = e.channel;
|
|
204
|
-
this.event_forwarders[channel.label] = channel;
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
_handleStart = (signal) => {
|
|
208
|
-
this.peerConnection = new RTCPeerConnection(signal.start);
|
|
209
|
-
this.peerConnection.ontrack = this._handlePeerConnectionTrack;
|
|
210
|
-
this.peerConnection.onicecandidate = this._handlePeerIceCandidate;
|
|
211
|
-
this.peerConnection.onconnectionstatechange = this._handlePeerConnectionStateChange;
|
|
212
|
-
this.peerConnection.ondatachannel = this._handleDataChannel;
|
|
213
|
-
this.peerConnection.onsignalingstatechange = this._handlePeerState;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
_handlePeerState = (event) => {
|
|
217
|
-
if (!this.peerConnection) {
|
|
218
|
-
console.log("Peerconnection no longer available, ignoring signal state.");
|
|
219
|
-
}
|
|
220
|
-
switch (this.peerConnection.signalingState) {
|
|
221
|
-
case "have-remote-offer":
|
|
222
|
-
this.old_emu_patch.haveOffer = true;
|
|
223
|
-
while (this.old_emu_patch.candidates.length > 0) {
|
|
224
|
-
this._handleCandidate(this.old_emu_patch.candidates.shift());
|
|
225
|
-
}
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
_handleSDP = async (signal) => {
|
|
231
|
-
// We should not call this more than once..
|
|
232
|
-
this.old_emu_patch.sdp = null;
|
|
233
|
-
this.peerConnection.setRemoteDescription(new RTCSessionDescription(signal));
|
|
234
|
-
const answer = await this.peerConnection.createAnswer();
|
|
235
|
-
if (answer) {
|
|
236
|
-
// Older emulators cannot handle multiple answers, so make sure we do not send one
|
|
237
|
-
// again.
|
|
238
|
-
if (!this.old_emu_patch.answer) {
|
|
239
|
-
this.old_emu_patch.answer = true;
|
|
240
|
-
this.peerConnection.setLocalDescription(answer);
|
|
241
|
-
this._sendJsep({ sdp: answer });
|
|
242
|
-
}
|
|
243
|
-
} else {
|
|
244
|
-
this.disconnect();
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
_handleCandidate = (signal) => {
|
|
249
|
-
this.peerConnection.addIceCandidate(new RTCIceCandidate(signal));
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
_handleSignal = (signal) => {
|
|
253
|
-
if (signal.start) {
|
|
254
|
-
this._handleStart(signal);
|
|
255
|
-
}
|
|
256
|
-
if (signal.bye) {
|
|
257
|
-
this._handleBye();
|
|
258
|
-
}
|
|
259
|
-
if (signal.sdp && !this.old_emu_patch.sdp) {
|
|
260
|
-
this.old_emu_patch.sdp = signal;
|
|
261
|
-
}
|
|
262
|
-
if (signal.candidate) {
|
|
263
|
-
this.old_emu_patch.candidates.push(signal);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (!!this.peerConnection) {
|
|
267
|
-
// We have created a peer connection..
|
|
268
|
-
if (this.old_emu_patch.sdp) {
|
|
269
|
-
this._handleSDP(this.old_emu_patch.sdp);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (this.old_emu_patch.haveOffer) {
|
|
273
|
-
// We have a remote peer, add the candidates in.
|
|
274
|
-
while (this.old_emu_patch.candidates.length > 0) {
|
|
275
|
-
this._handleCandidate(this.old_emu_patch.candidates.shift());
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
_handleJsepMessage = (message) => {
|
|
282
|
-
try {
|
|
283
|
-
const signal = JSON.parse(message);
|
|
284
|
-
this._handleSignal(signal);
|
|
285
|
-
} catch (e) {
|
|
286
|
-
console.error(
|
|
287
|
-
"Failed to handle message: [" +
|
|
288
|
-
message +
|
|
289
|
-
"], due to: " +
|
|
290
|
-
JSON.stringify(e)
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
_handleBye = () => {
|
|
296
|
-
if (this.connected) {
|
|
297
|
-
this.disconnect();
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
_sendJsep = (jsonObject) => {
|
|
302
|
-
/* eslint-disable */
|
|
303
|
-
var request = new proto.android.emulation.control.JsepMsg();
|
|
304
|
-
request.setId(this.guid);
|
|
305
|
-
request.setMessage(JSON.stringify(jsonObject));
|
|
306
|
-
this.rtc.sendJsepMessage(request);
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
_streamJsepMessage = () => {
|
|
310
|
-
if (!this.connected) return;
|
|
311
|
-
var self = this;
|
|
312
|
-
|
|
313
|
-
this.stream = this.rtc.receiveJsepMessages(this.guid, {});
|
|
314
|
-
this.stream.on("data", (response) => {
|
|
315
|
-
const msg = response.getMessage();
|
|
316
|
-
self._handleJsepMessage(msg);
|
|
317
|
-
});
|
|
318
|
-
this.stream.on("error", (e) => {
|
|
319
|
-
console.log("Jsep message stream error:", e);
|
|
320
|
-
if (self.connected) {
|
|
321
|
-
console.log("Attempting to reconnect to jsep message stream.");
|
|
322
|
-
self._streamJsepMessage();
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
this.stream.on("end", (e) => {
|
|
326
|
-
if (self.connected) {
|
|
327
|
-
console.log("Stream end while still connected.. Reconnecting");
|
|
328
|
-
self._streamJsepMessage();
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// This function is a fallback for v1 (go based proxy), that does not support streaming.
|
|
334
|
-
_receiveJsepMessage = () => {
|
|
335
|
-
if (!this.connected) return;
|
|
336
|
-
|
|
337
|
-
var self = this;
|
|
338
|
-
|
|
339
|
-
// This is a blocking call, that will return as soon as a series
|
|
340
|
-
// of messages have been made available, or if we reach a timeout
|
|
341
|
-
this.rtc.receiveJsepMessage(this.guid, {}, (err, response) => {
|
|
342
|
-
if (err) {
|
|
343
|
-
console.error(
|
|
344
|
-
"Failed to receive jsep message, disconnecting: " +
|
|
345
|
-
JSON.stringify(err)
|
|
346
|
-
);
|
|
347
|
-
this.disconnect();
|
|
348
|
-
}
|
|
349
|
-
const msg = response.getMessage();
|
|
350
|
-
// Handle only if we received a useful message.
|
|
351
|
-
// it is possible to get nothing if the server decides
|
|
352
|
-
// to kick us out.
|
|
353
|
-
if (msg) {
|
|
354
|
-
self._handleJsepMessage(response.getMessage());
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// And pump messages. Note we must continue the message pump as we
|
|
358
|
-
// can receive new ICE candidates at any point in time.
|
|
359
|
-
if (self.active) {
|
|
360
|
-
self._receiveJsepMessage();
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
};
|
|
364
|
-
}
|